# YouTube Transcript API 🚨 Real-Time, Under 1 Second (`nokia2k/youtube-transcript-instant-api`) Actor

Real-time YouTube transcript API: send one YouTube link to an always-on web address and get the transcript and captions back in 0.2–0.7 seconds once warm, with likes, chapters and comments on request. For chatbots, voice assistants and AI agents; unreachable videos are free.

- **URL**: https://apify.com/nokia2k/youtube-transcript-instant-api.md
- **Developed by:** [nokia2k](https://apify.com/nokia2k) (community)
- **Security:** Limited permissions
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-usage

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

**A real-time YouTube transcript API: send one video link to an always-on web address and get the transcript back in 0.2–0.7 seconds once it is awake, with likes, chapters and top comments on request, for chatbots, voice assistants, AI agents and live apps.**

**$99.00 per 1,000 answers** at the time of writing, about ten cents a video.

**Videos that do not exist, are private or could not be reached are never charged; on the always-on address a video without captions is free unless you ask for its details.**

🇪🇸 [Guía completa en español](#guía-completa-en-español)

### What does the real-time YouTube Transcript API do?

This Actor (Apify's word for a ready-made tool that runs on Apify's servers) is for when a person or an AI agent is waiting for the answer right now. It stays switched on at a fixed address (Apify calls this mode **Standby**), so there is no start-up wait between your question and the answer. Every request is raced over several lines at once, and the first answer wins. Nothing to set up on YouTube's side: no API key, no login, no proxy.

#### Why people pick it

- **No start-up wait.** The address is already running: 0.2–0.7 seconds per video for the transcript, measured from inside Apify's network on 7–8 October 2026.
- **No spinner because of one slow line.** Each request goes out over several lines at the same moment; the fastest one answers.
- **One address to paste.** A plain `GET ?url=` request works in a browser, in n8n, in Make or in a few lines of code. `format=text` returns just the words.
- **Details in the same answer.** Add `details=true` for likes, exact date, chapters, most replayed moments and channel facts, and `comments=20` for the top comments.
- **An answer for every request.** Every request gets a `status` and a plain `message`. A video without captions gets a normal answer with its reason, not a failed request.
- **Batches too.** The **Start** button runs lists of videos, whole channels, playlists and searches, with **Only new videos since the last run** for schedules.

#### Is it the right Actor for you?

This is the premium product of a family of six, priced for urgency: about ten cents a video. If nobody is waiting, you are paying for speed you do not need.

| Your situation | Use | Price per 1,000, at the time of writing |
| --- | --- | --- |
| A person or an AI agent is waiting and every second counts | **This Actor** | $99.00 |
| You can wait about 3 seconds for a video | [YouTube Transcript Scraper](https://apify.com/nokia2k/youtube-transcript-scraper) | $4.99 |
| You have a long list of video links and want plain text | [YouTube Video to Text](https://apify.com/nokia2k/youtube-video-to-text) | $1.99 |
| You want every video of a channel or a playlist | [YouTube Channel Transcripts](https://apify.com/nokia2k/youtube-channel-transcripts) | $9.99 |

Buy this one when the wait itself costs you something: a customer in a chat, a caller on a voice line, an agent in the middle of a conversation.

#### Measured speed and reliability

**Speed of the always-on address**, measured on 7–8 October 2026 from inside Apify's network (your own connection adds a little):

- First request after a pause: **4–5 s** (the container wakes up).
- After that: **0.2–0.7 s** per video for the transcript.
- With details and comments: **0.6–1.4 s** per video.

**Reliability**, measured on the Apify platform on 8 October 2026: 3,629 videos from 20 channels in two runs gave 3,447 transcripts, 171 videos without captions, 10 age-restricted, 1 video that failed on a timeout (0.03 %) and 0 blocked by YouTube. 25 difficult videos (no captions, live, age-restricted, private, deleted, paid, blocked in the US, a 5-hour video, Japanese, Korean, Spanish, Shorts) all got the right answer. Before a video is reported as `blocked` or `error`, two separate app identities and three kinds of connection are tried: home addresses, datacenter addresses and the server's own address. A normal run always ends as **Succeeded**, with one row per entry that says what happened.

### Quick start: your first real-time YouTube transcript in 5 steps

You need no code and no card.

1. **Create a free account.** Open `https://console.apify.com/sign-up`. Type an email and a password and click **Sign up**, then click the link in the email Apify sends you. Or click **Continue with Google**, which needs no email check.
   - *You see:* Apify Console, the web panel of your account. The free plan gives you $5 of credit each month.

2. **Copy your token.** In Console open **Settings**, then **API & Integrations**.
   - *You see:* your API token, a long line of letters and numbers. Copy it.

3. **Copy this address** into a plain text editor and replace `<YOUR_APIFY_TOKEN>`, including the two angle brackets, with your token:

   ```text
   https://nokia2k--youtube-transcript-instant-api.apify.actor/?url=arj7oStGLkU&format=text&token=<YOUR_APIFY_TOKEN>
   ```

4. **Paste the address into your browser's address bar** and press Enter.
   - *You see:* the words of a TED talk, beginning "So in college, I was a government major…". This first request can take about 4 to 5 seconds, because the Actor was asleep and had to wake up. Load the page again: now the answer is there almost at once.

5. **Try your own video.** Replace `arj7oStGLkU` with the 11 characters that follow `v=` in any YouTube link, or with the whole link. To keep the text, select it, copy it and paste it into a document.

Two honest notes. Each time you load the page you send a new request, and each answer with a transcript costs $0.099, plus what Apify bills for the container while it is awake (see [the cost section](#how-much-does-a-real-time-youtube-transcript-api-cost)). And a token inside an address is fine for a private test like this one, but not for real use: see [Keep the token secret](#what-a-token-and-a-header-are-and-how-to-keep-the-token-secret).

#### Your first file with the Start button

1. Open `https://apify.com/nokia2k/youtube-transcript-instant-api` and click **Try for free**. Sign in if you are asked.
2. Open the **Input** tab.
   - *You see:* a form. The first field, **📺 Videos, channels or playlists**, already holds one example video.
3. Click **Start**.
   - *You see:* the **run** (one execution of the Actor) working, and after a moment it is marked as succeeded.
4. Open the **Output** tab, click **Export**, choose a format (JSON, CSV, XML, Excel, HTML table, RSS or JSONL) and click **Download**.
   - *You see:* first a table with one row per video: title, status, language, transcript. Then the file lands in your browser's downloads folder.

This run delivers one row, so it costs $0.099, plus Apify's start event of $0.00005.

The smallest input for a normal run, in JSON (the **Input** tab has a switch between the form and JSON):

```json
{
    "urls": ["https://www.youtube.com/watch?v=arj7oStGLkU"]
}
```

### What data do you get from each video?

One row per video, whether it comes from the address or from a run. On the address, details are **off** unless you ask for them with `details=true`; in the form they are on by default.

#### Every output field

| Field | Meaning | Example |
| --- | --- | --- |
| **The answer itself** | | |
| `status` | What happened. See [Errors and troubleshooting](#errors-and-troubleshooting). | `success` |
| `message` | The reason in plain words when the status is not `success`, or a note. | `""` |
| `charged` | `true` when this row was billed. | `true` |
| `seconds` | Always-on address only: time the Actor spent on the request, rounded to two decimals. | `0.84` |
| `input` | The value you sent, to match answers to your list. | `arj7oStGLkU` |
| `scrapedAt` | When the row was made (UTC). | `2026-10-08T09:00:00+00:00` |
| `detailsMissing` | Parts of the details YouTube did not answer this time. Empty when all arrived. | `[]` |
| **Transcript** | | |
| `text` | The whole transcript as one paragraph. | `So in college, I was a government major…` |
| `timestampedText` | One line per caption with its time. | `[00:01] All right…` |
| `segments` | Lines with `start`, `duration`, `end` (seconds) and `text`. | `[{"start": 1.2, …}]` |
| `srt`, `vtt` | The content of an SRT or WebVTT subtitle file. | `WEBVTT…` |
| `markdown` | The transcript as a document with title, channel, date and link. | `# Video title…` |
| `files` | Download links of saved files (normal runs with `saveFiles`). | `{"srt": "https://…"}` |
| `data` | Caption lines as `start`, `dur`, `text` strings, when the request used the single `videoUrl` field. | `[{"start": "0.280", …}]` |
| `segmentCount`, `wordCount`, `charCount` | Lines, words and characters of the transcript. | `315`, `2277`, `12671` |
| **Language** | | |
| `language`, `languageName` | Language of the returned transcript. | `en`, `English` |
| `isAutoGenerated` | `true` for YouTube's speech recognition, `false` when a person wrote the captions. | `false` |
| `isTranslated`, `translatedFrom` | Whether it is a machine translation, and from which language. | `false`, `null` |
| `availableLanguages` | Every caption track the video has: `code`, `name`, `isAutoGenerated`, `isTranslatable`. | `[{"code": "en", …}]` |
| **Video** | | |
| `videoId`, `url` | The 11-character ID and the watch link. | `arj7oStGLkU` |
| `title`, `description` | Title and description. | `Inside the Mind of a Master Procrastinator…` |
| `keywords`, `hashtags` | The uploader's tags; the hashtags above the title. | `["#TED"]` |
| `thumbnailUrl` | Link to the largest thumbnail. | `https://i.ytimg.com/…` |
| `durationSeconds`, `viewCount` | Length in seconds; views at that moment. | `844`, `62013820` |
| `category` | YouTube's category. | `People & Blogs` |
| `descriptionLinks` | Every link in the description, as `text` and `url`. | `[{"text": "ted.com", …}]` |
| `isShort`, `isUnlisted`, `isFamilySafe`, `isMembersOnly`, `isPaid` | Yes/no flags as YouTube sets them. | `false` |
| `availableCountries`, `blockedCountries` | Country codes where the video can or cannot be watched. | `[]` |
| **Dates** | | |
| `publishedAt`, `uploadedAt` | Exact moment of publication and of upload. | `2016-04-06T09:59:35-07:00` |
| `publishedDateText` | The date as YouTube prints it under the video. | `Apr 6, 2016` |
| `publishedText` | The age as YouTube's lists show it. | `2 hours ago` |
| **Live streams** | | |
| `isLive`, `isLiveNow`, `liveViewers` | Live content; on air now; people watching now. | `false` |
| `liveStartedAt`, `liveEndedAt` | When the stream started and ended. | `null` |
| **Reactions** | | |
| `likeCount` | Exact number of likes. | `2054977` |
| `commentCount`, `commentCountText` | Number of comments, and as YouTube prints it. | `79650` |
| `commentsDisabled` | `true` when comments are switched off. | `false` |
| `comments` | Top comments: `id`, `text`, `author`, `likes`, `likesText`, `replyCount`, `publishedText`, `isPinned`, `isHearted`, `authorIsChannelOwner`. | see the sample |
| **Inside the video** | | |
| `chapters`, `chaptersAreAuto` | Chapters with `title`, `start`, `startText`; whether YouTube made them itself. | see the sample |
| `mostReplayed` | The moments viewers replay most: `start`, `end`, `intensity` (a relative score from 0 to 1). | see the sample |
| `music`, `musicTracks` | Song, artist and album when YouTube credits them. | `null` |
| `aiSummary` | YouTube's AI summary, when the video has one. | `null` |
| `relatedVideos` | Up to 10 videos YouTube suggests next. | `[{"videoId": "…", …}]` |
| **Channel** | | |
| `channelName`, `channelId` | Name and ID of the channel. | `TED`, `UCAuUUnT6oDeKwE6v1NGQxug` |
| `channelHandle`, `channelUrl` | The @handle and the link. | `@TED` |
| `channelSubscribers`, `channelSubscribersText` | Subscribers, rounded by YouTube, and as printed. | `27900000`, `27.9M subscribers` |
| `channelVerified`, `channelAvatar` | Verified badge; link to the profile picture. | `true` |
| **Where it was found** (normal runs) | | |
| `source`, `sourceType`, `sourceTitle` | The channel, playlist or search the video came from. | `https://www.youtube.com/@TED`, `channel`, `TED` |
| `positionInSource` | Its place in that list, starting at 1. | `1` |

#### Sample output: one success answer and one no_captions answer

An answer to `?url=arj7oStGLkU&details=true&comments=1`, shortened. Real values of 8 October 2026; `scrapedAt` and `seconds` are illustrations.

```json
{
    "videoId": "arj7oStGLkU",
    "url": "https://www.youtube.com/watch?v=arj7oStGLkU",
    "status": "success",
    "message": "",
    "title": "Inside the Mind of a Master Procrastinator | Tim Urban | TED",
    "channelName": "TED",
    "channelId": "UCAuUUnT6oDeKwE6v1NGQxug",
    "channelHandle": "@TED",
    "channelSubscribers": 27900000,
    "channelSubscribersText": "27.9M subscribers",
    "channelVerified": true,
    "durationSeconds": 844,
    "viewCount": 62013820,
    "publishedAt": "2016-04-06T09:59:35-07:00",
    "category": "People & Blogs",
    "likeCount": 2054977,
    "commentCount": 79650,
    "language": "en",
    "languageName": "English",
    "isAutoGenerated": false,
    "segmentCount": 315,
    "wordCount": 2277,
    "charCount": 12671,
    "text": "So in college, I was a government major, which means I had to write a lot of papers. …",
    "chapters": [
        { "title": "My procrastination journey", "start": 0, "startText": "0:00" },
        { "title": "The procrastinator's brain", "start": 176, "startText": "2:56" },
        { "title": "The monkey and the playground", "start": 249, "startText": "4:09" },
        { "title": "The role of the panic monster", "start": 436, "startText": "7:16" },
        { "title": "Two types of procrastination", "start": 599, "startText": "9:59" },
        { "title": "The life calendar", "start": 756, "startText": "12:36" }
    ],
    "chaptersAreAuto": true,
    "mostReplayed": [
        { "start": 481.08, "end": 489.52, "intensity": 1 },
        { "start": 16.88, "end": 25.32, "intensity": 0.951 }
    ],
    "comments": [
        {
            "text": "He procrastinated in creating a Ted talk about procrastination. Ted definitely picked the right man for the job.",
            "author": "@SWIFTzTrigger",
            "likes": 3200,
            "likesText": "3.2K",
            "replyCount": 2,
            "publishedText": "7 years ago"
        }
    ],
    "detailsMissing": [],
    "input": "arj7oStGLkU",
    "scrapedAt": "2026-10-08T09:00:00+00:00",
    "charged": true,
    "seconds": 0.84
}
```

An answer to `?url=<video>` for a video without captions, without `details=true`. The ID and the title are placeholders here; the other values are what such an answer carries. It is free:

```json
{
    "videoId": "<11-character ID>",
    "url": "https://www.youtube.com/watch?v=<11-character ID>",
    "status": "no_captions",
    "message": "The video has no captions, neither uploaded nor auto-generated.",
    "title": "<title of the video>",
    "language": null,
    "availableLanguages": [],
    "wordCount": 0,
    "charged": false
}
```

With `details=true` the same answer would carry the video's details and be charged.

### Use cases

- **Let a chatbot answer questions about any YouTube video** a customer pastes, without a pause in the conversation.
- **Give a voice assistant the words of a video** fast enough to read a summary back while the caller is still on the line.
- **Add a "summarize this video" button** to a browser extension or an app, with your own server holding the token.
- **Equip your AI agent with a sub-second transcript tool** for research, support or sales conversations.
- **Show a video's chapters, likes and top comments next to its transcript** in a live dashboard.
- **Run the same Actor in batches** for channels, playlists and searches, with only new videos on a schedule.

### Input examples

#### Two doors into the same Actor

1. **The always-on address** (Standby). You send it a **request**, one question sent over the internet, and the answer goes straight back to whoever asked. One video per request.
2. **The Start button.** This starts a **run** that works through a batch (videos, whole channels, playlists, searches) and puts the results in a table you can download. It costs the same per result. It has to start up first, so it is for work where nobody is waiting.

Everything this Actor can do is available through the **Start** button. The address does the urgent part: one video, now.

#### Parameters of the always-on address

**Base address:** `https://nokia2k--youtube-transcript-instant-api.apify.actor`. **Sign in:** header `Authorization: Bearer <YOUR_APIFY_TOKEN>`, or `?token=<YOUR_APIFY_TOKEN>`. **Requests:** `GET /?url=<video link or 11-character ID>`, or `POST /` with a JSON body that uses the same fields as the input form, for one video.

| Parameter | What it does | Example |
| --- | --- | --- |
| `url` (also `video`, `v`, `id`) | The video: a link or the 11-character ID. Required. | `url=arj7oStGLkU` |
| `language` (also `lang`) | Language code or name. Several, separated by commas, are tried in order. Without it you get the original language of the video. | `language=es,en` |
| `translate` | YouTube's machine translation into this language when the video has no captions in it. | `translate=es` |
| `format` | `text`, `timestampedText`, `srt`, `vtt` or `markdown`: the answer is the bare text in that format. Without it the answer is the JSON row. | `format=srt` |
| `details=true` | Adds everything YouTube shows about the video and its channel. | `details=true` |
| `comments=<number>` | Adds the top comments, up to 100. Needs `details=true`. | `comments=20` |

Other fields of the input form can be sent under their own names, for example `captionType=manual` or `mergeSegmentsSeconds=60`. The two file options, `saveFiles` and `combinedFile`, belong to normal runs.

#### One video on the address, plain text

```bash
curl -s "https://nokia2k--youtube-transcript-instant-api.apify.actor/?url=arj7oStGLkU&format=text" \
  -H "Authorization: Bearer <YOUR_APIFY_TOKEN>"
```

#### One video with details and five comments, as JSON

```bash
curl -s "https://nokia2k--youtube-transcript-instant-api.apify.actor/?url=arj7oStGLkU&details=true&comments=5" \
  -H "Authorization: Bearer <YOUR_APIFY_TOKEN>"
```

#### Spanish first, or translated into Spanish, as SRT

```bash
curl -s "https://nokia2k--youtube-transcript-instant-api.apify.actor/?url=arj7oStGLkU&language=es,en&translate=es&format=srt" \
  -H "Authorization: Bearer <YOUR_APIFY_TOKEN>"
```

#### POST with a JSON body, one video

```bash
curl -s -X POST "https://nokia2k--youtube-transcript-instant-api.apify.actor/" \
  -H "Authorization: Bearer <YOUR_APIFY_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"urls": ["https://www.youtube.com/watch?v=arj7oStGLkU"], "languages": ["es", "en"], "includeDetails": true, "maxComments": 5}'
```

#### A batch through the Start button: videos with details

```json
{
    "urls": [
        "https://www.youtube.com/watch?v=arj7oStGLkU",
        "https://youtu.be/jNQXAC9IVRw"
    ],
    "outputFormats": ["text"],
    "includeDetails": true,
    "maxComments": 5
}
```

#### The 20 newest videos of a channel, Spanish first, as Markdown

```json
{
    "urls": ["https://www.youtube.com/@TED"],
    "maxVideosPerSource": 20,
    "languages": ["es", "en"],
    "outputFormats": ["markdown"],
    "contentTypes": ["videos"]
}
```

#### A topic instead of links, this month's most viewed first

```json
{
    "searchQueries": ["how to learn spanish"],
    "maxVideosPerSearch": 10,
    "searchUploadDate": "month",
    "searchSortBy": "views"
}
```

#### Only new videos of a channel, for a schedule

```json
{
    "urls": ["https://www.youtube.com/@TED"],
    "maxVideosPerSource": 50,
    "onlyNewVideos": true,
    "stateKey": "ted-daily",
    "outputFormats": ["text"]
}
```

#### Every field of the form (normal runs)

Only the first field or the search field is needed. The rest has sensible defaults.

| Field in the form | Name | What it is and when to touch it | Example |
| --- | --- | --- | --- |
| 📺 Videos, channels or playlists | `urls` | YouTube links, one per line. A video link gives that video; a channel link or `@name` gives the channel's videos, newest first; a playlist link gives its videos. | `https://www.youtube.com/@TED` |
| 🔢 Videos per channel or playlist | `maxVideosPerSource` | How many videos to take from each channel or playlist. Default 50, up to 5,000. Start small: every row costs. | `5` |
| 🌐 Transcript language | `languages` | Leave on "Original language" to get each video in the language it is spoken in. Or pick languages in order of preference: 70 in the list, and any other code or name can be typed. | `["es", "en"]` |
| 📄 Output formats | `outputFormats` | `segments`, `text`, `timestampedText`, `srt`, `vtt`, `markdown`. Several cost the same as one. | `["text", "markdown"]` |
| 📚 Also give me everything as one Markdown file | `combinedFile` | Saves all transcripts of the run in one file, `ALL_TRANSCRIPTS.md`, for ChatGPT, Claude, NotebookLM or a knowledge base. Off by default. | `true` |
| 💾 Also save subtitle files I can download | `saveFiles` | Saves a `.srt`, `.vtt` or `.txt` file per video and adds download links in `files`. | `true` |
| 📊 Video and channel details | `includeDetails` | Adds exact date, likes, comment count, chapters, hashtags, channel subscribers and more. On by default. | `true` |
| 💬 Top comments per video | `maxComments` | How many top comments to add to each row. Default 0, up to 100. | `20` |
| 🔎 What to search for | `searchQueries` | What you would type into YouTube's search box, one search per line. | `how to learn spanish` |
| 🔢 Videos per search | `maxVideosPerSearch` | How many of the top results to take per search. Default 10, up to 500. | `5` |
| 📅 Uploaded | `searchUploadDate` | YouTube's own filter: `any`, `hour`, `today`, `week`, `month`, `year`. | `month` |
| ⏱️ Length | `searchDuration` | YouTube's own filter: `any`, `short` (under 4 minutes), `medium` (4 to 20), `long` (over 20). | `medium` |
| ↕️ Order | `searchSortBy` | `relevance` is YouTube's order; `date` and `views` put the found videos in that order. | `views` |
| Only videos that have captions (recommended) | `onlyWithCaptions` | Asks YouTube for videos with captions only. On by default. | `true` |
| 📅 Only videos newer than | `newerThan` | A date, or an age such as `30 days`. For channels and playlists. The cut is approximate. | `6 months` |
| Only new videos since the last run | `onlyNewVideos` | For scheduled runs: only videos not delivered before for the same channels, playlists and searches. Off by default. | `true` |
| Memory name | `stateKey` | Optional, with the field above: the name of the list of delivered videos. | `ted-daily` |
| 🎞️ Kinds of videos to take from a channel | `contentTypes` | Regular videos, Shorts, recordings of live streams. Untick what you do not need. | `["videos"]` |
| 🔁 Translate to | `translateTo` | YouTube's machine translation, used when the video has no captions in that language. | `es` |
| If my language is missing, give me the original language | `fallbackToAnyLanguage` | On: you still get a transcript, in the original language. Off: such videos are skipped. | `false` |
| Caption type | `captionType` | `any` (human-made first), `manual` (human-made only), `auto` (auto-generated only). | `manual` |
| Merge caption lines into blocks | `mergeSegmentsSeconds` | Joins the short caption lines into blocks of about this many seconds. 0 keeps the original lines. | `60` |
| Include video details | `includeMetadata` | Adds channel, duration, views, description, keywords and thumbnail. The title is always there. | `true` |

### Run it on a schedule and get only new videos

The always-on address answers one video per request. Following channels, playlists or searches is a job for normal runs on a schedule.

#### How "Only new videos since the last run" works

- Turn on **Only new videos since the last run** (`onlyNewVideos`). It is off by default and made for scheduled runs (Apify **Schedules**).
- Each run then delivers only the videos it has not delivered before for the same channels, playlists and searches. Videos already delivered are left out **before anything is fetched**, so they are neither fetched nor charged.
- The first run delivers everything it finds and remembers it.
- Video links you paste directly are always delivered, never left out. They are remembered, so a channel or search that contains them later does not deliver them again.

#### Set it up step by step

1. **Fill the form:** your channels, playlists or searches and your formats. Tick **Only new videos since the last run**. Optional: type a **Memory name** such as `ted-daily`.
2. **Click Start once by hand.** The first run delivers what it finds and remembers it. It also makes the Actor schedulable: an Actor must have run once before it can be scheduled.
3. **Click Save as a new task** at the top right of the Actor page. A **task** is a saved form; it appears under **Saved tasks**.
4. **Open Schedules** in the left menu and click **Create new**.
5. **In the Schedule setup card** choose how often (a cron expression such as `@daily`, or the visual builder) and your time zone. **Next runs** shows the coming dates.
6. **In the Add dropdown** pick your task.
7. **Click Enable.** New schedules start disabled.

#### What you see in later runs

- The log and the run's final status message say how many videos were left out, for example: "Skipped 37 videos already delivered in earlier runs (memory 'x')."
- A run with nothing new ends normally, with no rows: "No new videos since the last run: … Nothing was charged."

#### Memory name: one list or several

- **Left empty:** the memory is named `auto-…` after your list of channels, playlists and searches. The order and the letter case of searches do not matter; any other change to the list starts a new memory.
- **With a name:** you pick the record. The same name keeps one list even when the links change; a different name keeps a separate list.
- Letters, digits and `-_.!'()` are kept; other characters become `-`.

#### Where the memory lives and how to start afresh

The memory is kept in your own Apify account: **Storage** > **Key-value stores**, in the store named `youtube-transcripts-memory`. Each memory is one record that holds the IDs of the most recent 20,000 delivered videos and the time it was last updated. Delete the record, or the whole store, and the next run starts afresh and delivers everything again.

#### Good to know

- **Remembered:** videos delivered as `success` or `no_captions`, and every row that was charged. Free rows (`blocked`, `error`, `age_restricted` without details) and a live stream that is still running or has not started are tried again next run.
- **Never charged twice:** a row can be charged for its details even when its status is not `success` or `no_captions`, for example an `age_restricted` video whose details arrived. Every charged video is remembered, so a later run with **Only new videos since the last run** never charges it again.
- **Keep the numbers high enough.** **🔢 Videos per channel or playlist** and **🔢 Videos per search** count the videos looked at in each run, before the known ones are left out.
- **The memory is saved at the end of the run,** also when the run stops before its time limit or at the spending cap (then only the videos that were processed). After a server move the run continues and saves at its end; nothing is counted or charged twice.
- **If the memory cannot be read,** the run logs a warning and delivers all videos like a normal run. If it cannot be saved, the results are still delivered, and the next run may repeat some videos.
- **Two runs with the same memory at the same time** can both deliver the same new video. The memory itself keeps the videos of both.

#### Send new videos to Google Sheets or Slack

- **n8n:** add the **Apify Trigger** node (**Resource to Watch**: **Task**, your task; **Event Type**: **Succeeded**). Then an **Apify** node with Resource **Dataset**, **Get Items**, and the `defaultDatasetId` of the run as **Dataset ID** (raise **Limit**: it starts at 50). Then a Google Sheets node that appends one row per item (`title`, `url`, `publishedAt`, `text`), or a Slack node that posts `title` and `url`. A self-hosted n8n needs a public `WEBHOOK_URL` so that Apify can reach it.
- **Make:** start with **Watch Actor Runs** (it fires when a run finishes), then **Get Dataset Items**, then a Google Sheets module (Apify's guide uses "Bulk Add Rows") or a Slack module.
- **Any other tool:** on the task's **Integrations** tab add a webhook for the event `ACTOR.RUN.SUCCEEDED`. Apify sends a POST to your address; the dataset ID is in `resource.defaultDatasetId`. The receiver must answer within 2 minutes, and a webhook can arrive twice.

A run with no new videos still finishes as Succeeded, with an empty dataset: your sheet simply gets no new rows that time.

### Use the real-time YouTube Transcript API from AI agents (MCP)

MCP is the standard that lets an AI assistant use outside tools. Apify's MCP address with this Actor as a tool is:

```text
https://mcp.apify.com?tools=nokia2k/youtube-transcript-instant-api
```

Be aware: through MCP the assistant starts a **normal run** and waits for it (up to 45 seconds), so this path has the start-up wait. If your own agent must answer within a second, give its code a tool that calls the always-on address directly (see [Integrations](#integrations-n8n-make-zapier-python-javascript-and-langchain)).

Sign-in is OAuth: a browser window opens on the first connection and no token is stored in the configuration. A header `Authorization: Bearer <YOUR_APIFY_TOKEN>` works too.

#### Claude.ai and Claude Desktop

1. Open **Connectors** in Claude (**Settings** > **Connectors**, or **Customize** > **Connectors** depending on your version).
2. Choose **Add custom connector**.
3. Fill in **Name** and **Remote MCP server URL** with the address above, click **Continue**, then **Add**.
4. Sign in to Apify in the browser window that opens.
5. In a chat, click **+**, then **Connectors**, and switch it on.

On the free Claude plan you can add one custom connector. On Team and Enterprise plans an Owner adds it for the organization.

#### Claude Desktop with a local server

`claude_desktop_config.json`, needs Node.js:

```json
{
  "mcpServers": {
    "youtube-instant": {
      "command": "npx",
      "args": ["-y", "@apify/actors-mcp-server", "--tools", "nokia2k/youtube-transcript-instant-api"],
      "env": {
        "APIFY_TOKEN": "<YOUR_APIFY_TOKEN>"
      }
    }
  }
}
```

#### Cursor

`.cursor/mcp.json` in your project, or `~/.cursor/mcp.json`:

```json
{
  "mcpServers": {
    "youtube-instant": {
      "url": "https://mcp.apify.com?tools=nokia2k/youtube-transcript-instant-api"
    }
  }
}
```

With a token instead of OAuth, add `"headers": { "Authorization": "Bearer <YOUR_APIFY_TOKEN>" }` next to `"url"`.

#### VS Code

Needs GitHub Copilot. Open the Command Palette, run **MCP: Open User Configuration** and add:

```json
{
  "servers": {
    "youtube-instant": {
      "type": "http",
      "url": "https://mcp.apify.com?tools=nokia2k/youtube-transcript-instant-api"
    }
  }
}
```

#### Claude Code and ChatGPT

```bash
claude mcp add --transport http youtube-instant \
  "https://mcp.apify.com?tools=nokia2k/youtube-transcript-instant-api" \
  --header "Authorization: Bearer $APIFY_TOKEN"
```

**ChatGPT:** Developer mode is required. Go to **Settings > Apps & Connectors > Create**, fill in **Name**, **Description** and **MCP Server URL**, leave **Authentication** on OAuth, click **Create**. In a chat, click **+**, then **More**, and pick the connector.

#### Example prompts

- "Get the transcript of https://www.youtube.com/watch?v=arj7oStGLkU and answer: what is the 'panic monster'?"
- "Fetch this video with its chapters and tell me which chapter covers the life calendar and at what time: https://youtu.be/arj7oStGLkU"
- "Get the transcript and the top 5 comments of this video and draft a short reply to the most liked comment: \[link]"

### Integrations: n8n, Make, Zapier, Python, JavaScript and LangChain

#### What a token and a header are, and how to keep the token secret

**A token** is a long secret password that tells Apify which account is asking and which account pays. It is in Console under **Settings**, then **API & Integrations**; here it is written `<YOUR_APIFY_TOKEN>`. **A header** is a labelled line that travels with a request but outside the address, like the sender's name on an envelope instead of inside the letter.

You can hand over the token in two ways:

- **In a header** (the better way): the header is named `Authorization` and its value is the word `Bearer`, one space, then your token.
- **In the address**: add `token=<YOUR_APIFY_TOKEN>` as a parameter. This is the only way that works when you type an address into a browser.

How to keep it secret:

- Prefer the header. An address is saved in browser history and gets copied into chats, screenshots and logs. A header is not.
- In n8n, Make or Zapier, store the token in the tool's own box for credentials or connections, not in a text field of a workflow you may share or export.
- Never put the token in something that runs on your visitor's device: a web page, a browser extension, a mobile app. Anyone can read it there. Let your own server hold the token and call the address.
- Do not paste it into forums, screenshots or the **Issues** tab.
- If it leaks, go back to **Settings**, **API & Integrations**. A token can be rotated (replaced by a new one), given an expiry date, and limited with the toggle "Limit token permissions".

#### n8n (HTTP Request node, the always-on address)

1. In your workflow add the **HTTP Request** node.
   - *You see:* its settings panel.
2. Set **Method** to GET.
3. In **URL** paste `https://nokia2k--youtube-transcript-instant-api.apify.actor/`
4. Under **Authentication** choose the generic credential type, then **Header auth**. Create a credential with Name `Authorization` and Value `Bearer <YOUR_APIFY_TOKEN>`. n8n now keeps your token out of the workflow itself.
5. Turn on **Send Query Parameters**. Add a parameter named `url` and give it the YouTube link from your earlier node. Add a second one named `format` with the value `text` if you want only the words.
6. Click **Add Option**, choose **Timeout** and type `60000`. That is 60 seconds in milliseconds, so the wake-up after a pause never cuts the request short.
7. Run the node.
   - *You see:* with `format`, the transcript as text. Without it, one item with fields such as `status`, `title`, `text` and `seconds`.
8. Connect the next node. When you read the fields, check that `status` is `success` before you use `text`.

To send more options, change **Method** to POST, turn on **Send Body**, set **Body Content Type** to **JSON** and **Specify Body** to **Using JSON**, and paste the POST example from [Input examples](#input-examples).

For batches, n8n also has an **Apify** node, which you install from the nodes panel by searching **Apify**: Resource **Actor**, Operation **Run an Actor and Get Dataset**. Pick this Actor and paste the input into **Input JSON**. That starts a normal run.

#### Make (HTTP module)

Make's own screens are not quoted here, so look for these things by what they do.

1. Add Make's HTTP app to your scenario and pick the module that sends a request.
2. In the field for the address, paste `https://nokia2k--youtube-transcript-instant-api.apify.actor/`
3. Choose GET as the method.
4. Add one header. Name: `Authorization`. Value: `Bearer <YOUR_APIFY_TOKEN>`.
5. Add a query parameter named `url` and map the YouTube link from your earlier module into it. Add `format` with the value `text` if you want only the words.
6. Run the scenario once.
   - *You see:* the status code 200 and the transcript in the module's output.
7. If you left `format` out, switch on the module's option that reads the answer as data. Then `title`, `text` and the other fields can be mapped in the next module.

For batches, Make's Apify app has **Run an Actor** (set **Run synchronously** to **Yes**) followed by **Get Dataset Items**. For runs longer than about 2 minutes, use **Watch Actor Runs** and then **Get Dataset Items**.

#### Zapier

1. For the always-on address: use a Zapier step that can send a web request and fill in the same things as in Make: address, method GET, the `Authorization` header and the `url` parameter.
2. For a batch: add the Apify app and choose the action **Run Actor**. Click **Allow Access** to connect your account, pick this Actor and give it the input.
3. A synchronous **Run Actor** has a hard limit of 30 seconds. For anything that may take longer, use two Zaps: Zap A ends with **Run Actor** (asynchronous); Zap B starts with **Finished Actor Run**, then **Fetch Dataset Items**.

#### Python (requests, the always-on address)

```python
import requests

ADDRESS = "https://nokia2k--youtube-transcript-instant-api.apify.actor/"
HEADERS = {"Authorization": "Bearer <YOUR_APIFY_TOKEN>"}

response = requests.get(
    ADDRESS,
    params={
        "url": "https://www.youtube.com/watch?v=arj7oStGLkU",
        "details": "true",
        "comments": 5,
    },
    headers=HEADERS,
    timeout=60,
)
row = response.json()  # 200, 400 and 402 all answer with JSON here

if response.status_code == 200 and row["status"] == "success":
    print(row["title"], "-", row["wordCount"], "words in", row["seconds"], "s")
    print(row["text"][:200])
else:
    print(response.status_code, row["status"], row["message"])
```

#### JavaScript (fetch, the always-on address)

Run this on your server, not in a web page, so the token stays private.

```javascript
const address = new URL('https://nokia2k--youtube-transcript-instant-api.apify.actor/');
address.searchParams.set('url', 'https://www.youtube.com/watch?v=arj7oStGLkU');
address.searchParams.set('format', 'srt');

const response = await fetch(address, {
    headers: { Authorization: 'Bearer <YOUR_APIFY_TOKEN>' },
});

const type = response.headers.get('content-type') || '';
if (type.includes('json')) {
    const row = await response.json(); // no transcript, or a problem
    console.log(response.status, row.status, row.message);
} else {
    console.log(await response.text()); // the subtitle file
}
```

#### Normal runs through the Apify API: curl and Python

A batch goes through Apify's own API. This call starts a run, waits and returns the rows. It has a limit of 300 seconds; a longer run answers with HTTP 408, so start big jobs with `POST /v2/acts/nokia2k~youtube-transcript-instant-api/runs` and read the dataset when the run has finished.

```bash
curl -X POST "https://api.apify.com/v2/acts/nokia2k~youtube-transcript-instant-api/run-sync-get-dataset-items?maxTotalChargeUsd=5" \
  -H "Authorization: Bearer <YOUR_APIFY_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"urls": ["https://www.youtube.com/@TED"], "maxVideosPerSource": 5, "outputFormats": ["text"]}'
```

With `pip install apify-client` (version 3, Python 3.11 or newer):

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("nokia2k/youtube-transcript-instant-api").call(
    run_input={
        "urls": ["https://www.youtube.com/@TED"],
        "maxVideosPerSource": 5,
        "outputFormats": ["text"],
    },
)
for row in client.dataset(run.default_dataset_id).iterate_items():
    print(row["status"], row["charged"], row.get("title"))
```

#### LangChain

For an agent that must answer within a second, wrap the always-on address in your own tool (the Python example above is the whole call). For batches, `langchain-apify` runs the Actor as a normal run: `pip install langchain-apify langchain-core`, set `APIFY_API_TOKEN`, then:

```python
from langchain_apify import ApifyWrapper
from langchain_core.documents import Document

loader = ApifyWrapper().call_actor(
    actor_id="nokia2k/youtube-transcript-instant-api",
    run_input={
        "urls": ["https://www.youtube.com/watch?v=arj7oStGLkU"],
        "outputFormats": ["text"],
    },
    dataset_mapping_function=lambda row: Document(
        page_content=row.get("text") or "",
        metadata={
            "source": row.get("url"),
            "title": row.get("title"),
            "status": row.get("status"),
        },
    ),
)
docs = [d for d in loader.load() if d.metadata["status"] == "success"]
```

#### Google Sheets and Excel

- **By hand:** after a normal run click **Export**, choose Excel or CSV, click **Download**. Open the file in Excel or import it into Google Sheets.
- **Automatically:** in n8n, Make or Zapier, put a spreadsheet step after the HTTP step and write `title`, `url` and `text` into a new row.
- **Afterwards:** every answer from the always-on address is also saved in the dataset of the standby run. A **dataset** is the table where Apify keeps results. In Console open **Storage**, then the **Datasets** tab, click the **Dataset ID** and use **Export**.

### How much does a real-time YouTube transcript API cost?

**$99.00 per 1,000 answers** at the time of writing. "Per 1,000" is only a way of printing the price: each answer is charged on its own, $99.00 ÷ 1,000 = **$0.099 per video**. There is no subscription. The price that counts is the one on the Actor's **Pricing** tab.

**What is charged:** an answer or row that carries a transcript, or the video's details, or both. The `charged` field of every row says which it was.

**What else you pay:**

- **Normal runs:** Apify's start event, $0.00005 per run for each GB of memory (this Actor uses 1 GB by default). Nothing else: no platform usage, no proxy fees.
- **The always-on address:** Apify also bills you the platform usage of the standby container while it is up. This is Apify's rule for every Standby Actor. The container goes to sleep after 5 minutes without requests. That usage depends on how long the container stays up, so it is not in the table below.

| One normal run with | Rows charged | Videos | Run start (1 GB) | Total |
| --- | --- | --- | --- | --- |
| 10 videos | 10 | $0.99 | $0.00005 | **$0.99005** |
| 1,000 videos | 1,000 | $99.00 | $0.00005 | **$99.00005** |
| 10,000 videos | 10,000 | $990.00 | $0.00005 | **$990.00005** |

On the always-on address the same counts cost $0.99, $99.00 and $990.00 in answers, plus the platform usage of the container. 100 requests of which 12 are for private or deleted videos cost 88 × $0.099 = $8.71 in answers.

**What is never charged:** videos that do not exist, are private or could not be reached; values that are not a YouTube video; the 400 and 402 answers; extra formats, translation, comments. On the always-on address, a video without captions is free unless you asked for `details=true` and the details were delivered.

**The free plan:** $5 of credit each month, no card needed. At this price that is about 50 answers a month: enough to test, not to run a product.

**How to cap spending:** before a normal run, open **Run options** and fill in **Maximum cost per run**. The Actor stops at the cap and says so. Through the API the same cap is the parameter `maxTotalChargeUsd`. On the always-on address, a request over the cap gets HTTP 402. What you have spent is under **Billing** in Console.

### Errors and troubleshooting

#### What each status means

Read `status`, then `message`. The last column is the rule of thumb; the `charged` field of each row is the final word.

| `status` | In plain words | What to do | Charged |
| --- | --- | --- | --- |
| `success` | The transcript was delivered. | Use it. | Yes |
| `no_captions` | The video has no captions, or it is a live stream that is still running. | Nothing can be read. For a stream, ask again after it ends. | Only if the row carries the details |
| `language_unavailable` | No captions in the languages you chose, and the fallback is off. | Look at `availableLanguages`, or turn the fallback on. | Only if the row carries the details |
| `video_unavailable` | Deleted, or never existed. | Check the link. | No |
| `video_unplayable` | Private, paid, blocked in the region or removed by YouTube. `message` has YouTube's reason. | Nothing to do: it is not public. | No for private videos; otherwise only if the details arrived |
| `age_restricted` | YouTube shows it only to signed-in adults. | Cannot be read. | Only if the details arrived |
| `blocked` | YouTube refused every connection that was tried. | Ask again later. | No, unless the details arrived |
| `error` | Something unexpected. `message` says what. | Ask again. | No, unless the details arrived |
| `invalid_input` | The value is not a YouTube link or ID. On the address this is HTTP 400. | Correct the value. One video per request. | No |
| `source_problem` | A channel, playlist or search could not be listed (normal runs). `message` says why. | Check the link or the search words. | No |

#### Status codes of the always-on address

| Code | When | Body |
| --- | --- | --- |
| 200 | The request was answered | The JSON row: the same fields as a dataset row, plus `seconds`. Or the bare text, when `format` is given and there is a transcript. |
| 400 | No video in the request, or more than one | JSON with `status: "invalid_input"` and a `message` |
| 402 | The spending limit of the run is reached | JSON with `status: "error"` and a `message`. Nothing is charged. |

What to handle in your code:

- **200 does not mean "transcript".** Read `status`. A video without captions is a 200 with `status: "no_captions"`.
- **With `format`, check the content type.** A transcript arrives as `text/plain` (`text`, `timestampedText`), `application/x-subrip` (`srt`), `text/vtt` or `text/markdown`. If there is no transcript, you get the JSON row as `application/json` instead.
- **`seconds`** is the time the Actor spent on your request. The trip between you and Apify is not in it.
- **Cold start:** after 5 minutes without requests the container goes to sleep, and the next request wakes it, which takes about 4 to 5 seconds. Give your client a generous timeout, for example 60 seconds.
- **One video per request.** Lists, channels and searches go through a normal run.

#### Problems and solutions

- **"My first request took 4 to 5 seconds."** The container was asleep. Requests after it are fast again.
- **"I get HTTP 400."** The request has no video, or more than one. Send exactly one `url`.
- **"I get HTTP 402."** The spending limit of the run is reached. Nothing was charged.
- **"The answer is JSON although I asked for format=text."** There is no transcript for that video; read `status` and `message`.
- **"Detail fields are empty."** On the address, details come only with `details=true`. Some things do not exist for every video (chapters, music, AI summary); `detailsMissing` lists what YouTube did not answer this time.
- **"My MCP assistant is slow."** MCP starts a normal run with a start-up wait. Call the address from your agent's code for sub-second answers.
- **"n8n times out on the first call."** Set the HTTP Request **Timeout** option to `60000`.

#### Limits to know

- It does not listen to the audio. A video with no captions at all gives `no_captions`.
- It cannot read age-restricted, private or members-only videos.
- It has no dislikes: YouTube does not show them.
- Subscriber counts are rounded by YouTube ("27.9M"). Comment likes above 999 are rounded too ("1.2K"); you get both `likes` and `likesText`.
- The address takes one video per request.
- After 5 quiet minutes, the next request waits about 4 to 5 seconds.

### FAQ

#### Why did a video come back without a transcript?

Because YouTube has no captions for it, or it is not public. The `status` field names the case and `message` explains it.

#### Am I charged when there is no result?

Not when the video does not exist, is private or could not be reached. An answer is charged when it carries a transcript or the video's details. Look at `charged` on any row.

#### What does "$99.00 per 1,000" mean, and what will I pay?

It is the price of 1,000 answers. You pay per answer: $0.099 each at the time of writing. Ten videos cost $0.99, plus the platform usage of the container on the always-on address.

#### Why does it cost so much more than the other Actors of the family?

You pay for an Actor that is already awake and for several lines raced on every request. If you can wait about 3 seconds, [YouTube Transcript Scraper](https://apify.com/nokia2k/youtube-transcript-scraper) costs $4.99 per 1,000.

#### Why did my first request take about 4 to 5 seconds?

The container sleeps after 5 minutes without requests. The next request wakes it. The ones after that took 0.2–0.7 s for the transcript in the measurement above.

#### Can I keep it awake?

A request within every 5 minutes keeps it up. Remember that Apify bills the platform usage of the container for as long as it is up.

#### Does it work with YouTube Shorts and live streams?

Shorts: yes, when they have captions. A live stream that is still running gives `no_captions`; ask again after it ends.

#### Can I give it a channel or a playlist?

Through the **Start** button, yes: paste the link into **📺 Videos, channels or playlists**. The address takes one video per request and answers 400 to anything else.

#### Which language do I get, and can it translate?

By default the language the video is spoken in. Choose others with `language`, or with **🌐 Transcript language** in the form. `translate` asks for YouTube's own machine translation, which YouTube does not offer for every video.

#### Does it transcribe the audio when there are no captions?

No. It reads captions that already exist on YouTube.

#### Will YouTube block it? Do I need a proxy or a YouTube API key?

No and no. There is nothing to set up. In the measurement above, 0 of 3,629 videos were blocked.

#### Where are my results?

On the address, the answer is the result: it comes back to whoever asked. A copy is in the dataset of the standby run. After a normal run, open the **Output** tab.

#### A field is empty or missing. Why?

Detail fields come only with `details=true` (address) or **📊 Video and channel details** (form). Transcript formats come only when chosen. Some things do not exist for every video: chapters, music, AI summary. `detailsMissing` lists what YouTube did not answer this time.

#### How exact is the date filter?

**📅 Only videos newer than** is approximate, because YouTube's lists show ages roughly ("3 weeks ago"). Nothing newer is left out; a few videos just past the limit can slip in. The exact date is in `publishedAt`.

#### Why did a run return fewer rows than I asked for?

The channel or search had fewer videos, a filter removed some, a link was pasted twice (each video is fetched once), the memory left out videos delivered earlier, or the spending cap or the run's timeout was reached. The run's message says how many videos were not processed.

#### How do I get the answer into n8n, Make, Zapier or Google Sheets?

Follow [Integrations](#integrations-n8n-make-zapier-python-javascript-and-langchain). It is one HTTP step with the address, one header and one parameter.

#### Can ChatGPT or Claude use it?

Yes, through Apify's MCP server. That path starts a normal run. For an agent that must answer within a second, call the address from the agent's own code.

#### What do the timestamps mean? SRT or VTT?

Each line has a `start` and an `end` in seconds from the beginning of the video. SRT and VTT are two subtitle file types with the same lines: SRT is the common one for video editors, VTT the one web players use.

#### Can I get comments, likes and the publish date?

Yes: `details=true` adds likes, the exact date and the rest; `comments=20` adds the top 20 comments, up to 100.

#### How many videos per request, and per run?

One per request. Requests can be sent side by side. A run takes up to 5,000 videos per channel or playlist and up to 500 per search. How many runs you may have at once depends on your Apify plan.

#### Is it safe to call the address from my web page or extension?

Not directly: the token would be visible to every visitor. Let your own server hold the token and make the call.

#### Is it legal? What about private videos?

The Actor reads only what YouTube shows publicly to any visitor. It does not read private videos. What you do with the texts is your responsibility: respect copyright and YouTube's terms.

#### What do Standby, request, header and status code mean?

- **Actor:** a ready-made tool that runs on Apify's servers.
- **Run:** one execution of an Actor, from **Start** to finished.
- **Standby:** Apify's name for an Actor that stays switched on and answers requests at a fixed address.
- **Request:** one question sent over the internet to an address.
- **Parameter:** a setting written into an address after the `?`, as `name=value`, joined with `&`.
- **Token (API token):** the secret password of your Apify account.
- **Header:** a labelled line sent with a request, outside the address.
- **Status code:** the number every web answer begins with. 200 means answered, 400 means the request was wrong, 402 means the spending limit was reached.
- **JSON:** a text format for data that programs read easily, made of names and values.
- **Dataset:** the table in which Apify stores results.
- **Key-value store:** the folder of a run where files such as `ALL_TRANSCRIPTS.md` are kept.
- **Captions and transcript:** captions are the text lines YouTube shows over a video; the transcript is all of those lines together.
- **Auto-generated captions:** captions made by YouTube's speech recognition, not by a person. Many have no punctuation.
- **SRT and VTT:** two common subtitle file types.
- **Cold start:** the wake-up of a sleeping container, about 4 to 5 seconds here.
- **MCP:** the standard through which AI assistants use outside tools.

#### Something went wrong. Where do I get help?

Open the **Issues** tab of this Actor. See [Support and updates](#support-and-updates).

### Related YouTube tools

Six Actors by the same developer, each made for a different starting point: pick the one that matches what you have. Prices per 1,000 results at the time of writing.

| Actor | Use it when you have | You get | Price |
| --- | --- | --- | --- |
| [YouTube Video to Text 📝](https://apify.com/nokia2k/youtube-video-to-text) | A big list of video links | Plain text only, the lowest price | $1.99 |
| [YouTube Transcript Scraper ⚡](https://apify.com/nokia2k/youtube-transcript-scraper) | Video links, and about 3 seconds is fast enough | Text, timestamps, SRT, VTT, translation | $4.99 |
| [YouTube Channel Transcripts 📚](https://apify.com/nokia2k/youtube-channel-transcripts) | A channel, `@handle` or playlist | Every video, publish date, likes, one Markdown file, only new videos | $9.99 |
| [YouTube Search Transcripts Scraper 🔎](https://apify.com/nokia2k/youtube-search-transcripts) | A topic, no links | Transcripts of the videos YouTube ranks highest | $14.99 |
| [YouTube Scraper 🧰](https://apify.com/nokia2k/youtube-all-in-one-scraper) | Any of the above, you need numbers, and nobody is waiting | Transcript plus likes, comments, chapters, channel facts | $19.99 |
| **YouTube Transcript API 🚨 (you are here)** | An app that needs one video right now | An always-on web address, no start-up wait | $99.00 |

### Support and updates

Open an issue on the **Issues** tab of this Actor. Include the video link, the address you called **without your token**, the `status` and `message` you received and, for a normal run, the link to the run.

Last updated: October 2026.

**Get your first transcript: paste one link and click Start, or open the address above in your browser.**

### 🇪🇸 Guía completa en español

**Una API de transcripciones de YouTube en tiempo real: envía un enlace de video a una dirección web siempre encendida y recibe la transcripción en 0.2–0.7 segundos cuando está despierta, con «me gusta», capítulos y comentarios principales si los pides, para chatbots, asistentes de voz, agentes de IA y aplicaciones en vivo.**

**$99.00 por cada 1000 respuestas** en el momento de escribir esto, unos diez centavos por video.

**Los videos que no existen, son privados o no se pudieron alcanzar nunca se cobran; en la dirección siempre encendida, un video sin subtítulos es gratis salvo que pidas sus datos.**

Este Actor (así llama Apify a una herramienta lista para usar que funciona en sus servidores) es para cuando una persona o un agente de IA está esperando la respuesta ahora mismo. Se queda encendido en una dirección fija (Apify llama a este modo **Standby**, «en espera»), así que no hay espera de arranque entre tu pregunta y la respuesta. Cada petición compite por varias líneas a la vez, y gana la primera respuesta. No hay nada que configurar del lado de YouTube: ni clave de API, ni inicio de sesión, ni proxy.

Las pantallas de Apify están en inglés. Por eso los botones y pestañas aparecen aquí en inglés y en negrita, con su significado entre comillas angulares la primera vez. Los nombres de los campos, los parámetros, los valores de `status` y el JSON también se quedan en inglés.

#### Por qué lo eligen

- **Sin espera de arranque.** La dirección ya está en marcha: de 0.2 a 0.7 segundos por video para la transcripción, medido desde dentro de la red de Apify el 7 y 8 de octubre de 2026.
- **Sin ruedita girando por una línea lenta.** Cada petición sale por varias líneas en el mismo momento; responde la más rápida.
- **Una dirección que pegar.** Una petición sencilla `GET ?url=` funciona en un navegador, en n8n, en Make o en unas pocas líneas de código. `format=text` devuelve solo las palabras.
- **Los datos en la misma respuesta.** Añade `details=true` para «me gusta», fecha exacta, capítulos, momentos más repetidos y datos del canal, y `comments=20` para los comentarios principales.
- **Una respuesta para cada petición.** Cada petición recibe un `status` y un `message` en lenguaje claro. Un video sin subtítulos recibe una respuesta normal con su motivo, no una petición fallida.
- **También por lotes.** El botón **Start** («Iniciar») procesa listas de videos, canales enteros, listas de reproducción y búsquedas, con **Only new videos since the last run** («Solo videos nuevos desde la última ejecución») para programaciones.

#### ¿Es el Actor que necesitas?

Es el producto premium de una familia de seis, con un precio pensado para la urgencia: unos diez centavos por video. Si nadie está esperando, pagas una velocidad que no necesitas.

| Tu situación | Usa | Precio por cada 1000, en el momento de escribir esto |
| --- | --- | --- |
| Una persona o un agente de IA está esperando y cada segundo cuenta | **Este Actor** | $99.00 |
| Puedes esperar unos 3 segundos por video | [YouTube Transcript Scraper](https://apify.com/nokia2k/youtube-transcript-scraper) | $4.99 |
| Tienes una lista larga de enlaces de videos y quieres texto plano | [YouTube Video to Text](https://apify.com/nokia2k/youtube-video-to-text) | $1.99 |
| Quieres todos los videos de un canal o de una lista de reproducción | [YouTube Channel Transcripts](https://apify.com/nokia2k/youtube-channel-transcripts) | $9.99 |

Elige este cuando la propia espera te cuesta algo: un cliente en un chat, alguien al teléfono con un asistente de voz, un agente en mitad de una conversación.

#### Velocidad y fiabilidad medidas

**Velocidad de la dirección siempre encendida**, medida el 7 y 8 de octubre de 2026 desde dentro de la red de Apify (tu propia conexión añade un poco):

- Primera petición tras una pausa: **de 4 a 5 s** (el contenedor se despierta).
- Después: **de 0.2 a 0.7 s** por video para la transcripción.
- Con datos y comentarios: **de 0.6 a 1.4 s** por video.

**Fiabilidad**, medida en la plataforma de Apify el 8 de octubre de 2026: 3629 videos de 20 canales en dos ejecuciones dieron 3447 transcripciones, 171 videos sin subtítulos, 10 con restricción de edad, 1 video fallido por tiempo agotado (0.03 %) y 0 bloqueados por YouTube. 25 videos difíciles (sin subtítulos, en directo, con restricción de edad, privados, borrados, de pago, bloqueados en Estados Unidos, un video de 5 horas, en japonés, coreano y español, Shorts) recibieron todos la respuesta correcta. Antes de dar un video por `blocked` o `error`, se prueban dos identidades de aplicación distintas y tres tipos de conexión: direcciones domésticas, direcciones de centros de datos y la dirección del propio servidor. Una ejecución normal siempre termina como **Succeeded** («completada»), con una fila por entrada que dice qué pasó.

### Inicio rápido: tu primera transcripción de YouTube en tiempo real en 5 pasos

No necesitas código ni tarjeta.

1. **Crea una cuenta gratuita.** Abre `https://console.apify.com/sign-up`. Escribe un correo y una contraseña y haz clic en **Sign up** («Registrarse»); después haz clic en el enlace del correo que te envía Apify. O haz clic en **Continue with Google** («Continuar con Google»), que no necesita comprobar el correo.
   - *Verás:* Apify Console («Consola de Apify»), el panel web de tu cuenta. El plan gratuito te da $5 de crédito cada mes.

2. **Copia tu token.** En la consola abre **Settings** («Configuración») y luego **API & Integrations** («API e integraciones»).
   - *Verás:* tu token de API, una línea larga de letras y números. Cópialo.

3. **Copia esta dirección** en un editor de texto sencillo y sustituye `<YOUR_APIFY_TOKEN>`, incluidos los dos signos angulares, por tu token:

   ```text
   https://nokia2k--youtube-transcript-instant-api.apify.actor/?url=arj7oStGLkU&format=text&token=<YOUR_APIFY_TOKEN>
   ```

4. **Pega la dirección en la barra de direcciones del navegador** y pulsa Intro.
   - *Verás:* las palabras de una charla TED, que empiezan por "So in college, I was a government major…". Esta primera petición puede tardar unos 4 o 5 segundos, porque el Actor estaba dormido y tuvo que despertarse. Vuelve a cargar la página: ahora la respuesta llega casi al instante.

5. **Prueba con tu propio video.** Sustituye `arj7oStGLkU` por los 11 caracteres que siguen a `v=` en cualquier enlace de YouTube, o por el enlace entero. Para guardar el texto, selecciónalo, cópialo y pégalo en un documento.

Dos avisos honestos. Cada vez que cargas la página envías una petición nueva, y cada respuesta con transcripción cuesta $0.099, más lo que Apify factura por el contenedor mientras está despierto (mira «¿Cuánto cuesta una API de transcripciones de YouTube en tiempo real?»). Y un token dentro de una dirección vale para una prueba privada como esta, pero no para el uso real: mira «Qué son un token y una cabecera, y cómo guardar el token en secreto».

#### Tu primer archivo con el botón Start

1. Abre `https://apify.com/nokia2k/youtube-transcript-instant-api` y haz clic en **Try for free** («Probar gratis»). Inicia sesión si te lo pide.
2. Abre la pestaña **Input** («Entrada»).
   - *Verás:* un formulario. El primer campo, **📺 Videos, channels or playlists** («Videos, canales o listas»), ya trae un video de ejemplo.
3. Haz clic en **Start**.
   - *Verás:* la **ejecución** (cada vez que el Actor trabaja) en marcha, y al poco aparece como completada.
4. Abre la pestaña **Output** («Salida»), haz clic en **Export** («Exportar»), elige un formato (JSON, CSV, XML, Excel, HTML table, RSS o JSONL) y haz clic en **Download** («Descargar»).
   - *Verás:* primero una tabla con una fila por video: título, estado, idioma, transcripción. Después el archivo llega a la carpeta de descargas de tu navegador.

Esta ejecución entrega una fila, así que cuesta $0.099, más el evento de inicio de Apify de $0.00005.

La entrada más pequeña para una ejecución normal, en JSON (la pestaña **Input** tiene un selector entre el formulario y JSON):

```json
{
    "urls": ["https://www.youtube.com/watch?v=arj7oStGLkU"]
}
```

### Qué datos recibes de cada video

Una fila por video, venga de la dirección o de una ejecución. En la dirección, los datos están **desactivados** salvo que los pidas con `details=true`; en el formulario están activados por defecto.

#### Todos los campos de salida

| Campo | Qué significa | Ejemplo |
| --- | --- | --- |
| **La respuesta** | | |
| `status` | Qué pasó. Mira «Errores y solución de problemas». | `success` |
| `message` | El motivo en palabras sencillas cuando el estado no es `success`, o una nota. | `""` |
| `charged` | `true` cuando esta fila se facturó. | `true` |
| `seconds` | Solo en la dirección siempre encendida: el tiempo que el Actor dedicó a la petición, redondeado a dos decimales. | `0.84` |
| `input` | El valor que enviaste, para emparejar respuestas con tu lista. | `arj7oStGLkU` |
| `scrapedAt` | Cuándo se creó la fila (UTC). | `2026-10-08T09:00:00+00:00` |
| `detailsMissing` | Partes de los datos que YouTube no respondió esta vez. Vacío cuando llegó todo. | `[]` |
| **Transcripción** | | |
| `text` | Toda la transcripción como un párrafo. | `So in college, I was a government major…` |
| `timestampedText` | Una línea por subtítulo con su tiempo. | `[00:01] All right…` |
| `segments` | Líneas con `start`, `duration`, `end` (segundos) y `text`. | `[{"start": 1.2, …}]` |
| `srt`, `vtt` | El contenido de un archivo de subtítulos SRT o WebVTT. | `WEBVTT…` |
| `markdown` | La transcripción como documento con título, canal, fecha y enlace. | `# Video title…` |
| `files` | Enlaces de descarga de los archivos guardados (ejecuciones normales con `saveFiles`). | `{"srt": "https://…"}` |
| `data` | Líneas de subtítulos como cadenas `start`, `dur`, `text`, cuando la petición usó el campo único `videoUrl`. | `[{"start": "0.280", …}]` |
| `segmentCount`, `wordCount`, `charCount` | Líneas, palabras y caracteres de la transcripción. | `315`, `2277`, `12671` |
| **Idioma** | | |
| `language`, `languageName` | Idioma de la transcripción devuelta. | `en`, `English` |
| `isAutoGenerated` | `true` para el reconocimiento de voz de YouTube, `false` cuando los subtítulos los escribió una persona. | `false` |
| `isTranslated`, `translatedFrom` | Si es una traducción automática, y desde qué idioma. | `false`, `null` |
| `availableLanguages` | Todas las pistas de subtítulos del video: `code`, `name`, `isAutoGenerated`, `isTranslatable`. | `[{"code": "en", …}]` |
| **Video** | | |
| `videoId`, `url` | El identificador de 11 caracteres y el enlace de reproducción. | `arj7oStGLkU` |
| `title`, `description` | Título y descripción. | `Inside the Mind of a Master Procrastinator…` |
| `keywords`, `hashtags` | Las etiquetas de quien lo subió; los hashtags sobre el título. | `["#TED"]` |
| `thumbnailUrl` | Enlace a la miniatura más grande. | `https://i.ytimg.com/…` |
| `durationSeconds`, `viewCount` | Duración en segundos; visualizaciones en ese momento. | `844`, `62013820` |
| `category` | Categoría de YouTube. | `People & Blogs` |
| `descriptionLinks` | Todos los enlaces de la descripción, como `text` y `url`. | `[{"text": "ted.com", …}]` |
| `isShort`, `isUnlisted`, `isFamilySafe`, `isMembersOnly`, `isPaid` | Marcas sí/no tal como las pone YouTube. | `false` |
| `availableCountries`, `blockedCountries` | Códigos de los países donde el video se puede o no se puede ver. | `[]` |
| **Fechas** | | |
| `publishedAt`, `uploadedAt` | Momento exacto de publicación y de subida. | `2016-04-06T09:59:35-07:00` |
| `publishedDateText` | La fecha tal como YouTube la imprime bajo el video. | `Apr 6, 2016` |
| `publishedText` | La antigüedad tal como la muestran las listas de YouTube. | `2 hours ago` |
| **Directos** | | |
| `isLive`, `isLiveNow`, `liveViewers` | Contenido en directo; en emisión ahora; personas viéndolo ahora. | `false` |
| `liveStartedAt`, `liveEndedAt` | Cuándo empezó y terminó la emisión. | `null` |
| **Reacciones** | | |
| `likeCount` | Número exacto de «me gusta». | `2054977` |
| `commentCount`, `commentCountText` | Número de comentarios, y tal como lo imprime YouTube. | `79650` |
| `commentsDisabled` | `true` cuando los comentarios están desactivados. | `false` |
| `comments` | Comentarios principales: `id`, `text`, `author`, `likes`, `likesText`, `replyCount`, `publishedText`, `isPinned`, `isHearted`, `authorIsChannelOwner`. | mira el ejemplo |
| **Dentro del video** | | |
| `chapters`, `chaptersAreAuto` | Capítulos con `title`, `start`, `startText`; si los hizo YouTube. | mira el ejemplo |
| `mostReplayed` | Los momentos que más vuelven a ver los espectadores: `start`, `end`, `intensity` (una puntuación relativa de 0 a 1). | mira el ejemplo |
| `music`, `musicTracks` | Canción, artista y álbum cuando YouTube los acredita. | `null` |
| `aiSummary` | El resumen de IA de YouTube, cuando el video lo tiene. | `null` |
| `relatedVideos` | Hasta 10 videos que YouTube sugiere a continuación. | `[{"videoId": "…", …}]` |
| **Canal** | | |
| `channelName`, `channelId` | Nombre e identificador del canal. | `TED`, `UCAuUUnT6oDeKwE6v1NGQxug` |
| `channelHandle`, `channelUrl` | El @handle y el enlace. | `@TED` |
| `channelSubscribers`, `channelSubscribersText` | Suscriptores, redondeados por YouTube, y tal como se imprimen. | `27900000`, `27.9M subscribers` |
| `channelVerified`, `channelAvatar` | Insignia de verificación; enlace a la foto de perfil. | `true` |
| **Dónde se encontró** (ejecuciones normales) | | |
| `source`, `sourceType`, `sourceTitle` | El canal, la lista o la búsqueda de donde vino el video. | `https://www.youtube.com/@TED`, `channel`, `TED` |
| `positionInSource` | Su puesto en esa lista, empezando en 1. | `1` |

#### Ejemplo de salida: una respuesta correcta y una respuesta no_captions

Una respuesta a `?url=arj7oStGLkU&details=true&comments=1`, acortada. Valores reales del 8 de octubre de 2026; `scrapedAt` y `seconds` son ilustrativos.

```json
{
    "videoId": "arj7oStGLkU",
    "url": "https://www.youtube.com/watch?v=arj7oStGLkU",
    "status": "success",
    "message": "",
    "title": "Inside the Mind of a Master Procrastinator | Tim Urban | TED",
    "channelName": "TED",
    "channelId": "UCAuUUnT6oDeKwE6v1NGQxug",
    "channelHandle": "@TED",
    "channelSubscribers": 27900000,
    "channelSubscribersText": "27.9M subscribers",
    "channelVerified": true,
    "durationSeconds": 844,
    "viewCount": 62013820,
    "publishedAt": "2016-04-06T09:59:35-07:00",
    "category": "People & Blogs",
    "likeCount": 2054977,
    "commentCount": 79650,
    "language": "en",
    "languageName": "English",
    "isAutoGenerated": false,
    "segmentCount": 315,
    "wordCount": 2277,
    "charCount": 12671,
    "text": "So in college, I was a government major, which means I had to write a lot of papers. …",
    "chapters": [
        { "title": "My procrastination journey", "start": 0, "startText": "0:00" },
        { "title": "The procrastinator's brain", "start": 176, "startText": "2:56" },
        { "title": "The monkey and the playground", "start": 249, "startText": "4:09" },
        { "title": "The role of the panic monster", "start": 436, "startText": "7:16" },
        { "title": "Two types of procrastination", "start": 599, "startText": "9:59" },
        { "title": "The life calendar", "start": 756, "startText": "12:36" }
    ],
    "chaptersAreAuto": true,
    "mostReplayed": [
        { "start": 481.08, "end": 489.52, "intensity": 1 },
        { "start": 16.88, "end": 25.32, "intensity": 0.951 }
    ],
    "comments": [
        {
            "text": "He procrastinated in creating a Ted talk about procrastination. Ted definitely picked the right man for the job.",
            "author": "@SWIFTzTrigger",
            "likes": 3200,
            "likesText": "3.2K",
            "replyCount": 2,
            "publishedText": "7 years ago"
        }
    ],
    "detailsMissing": [],
    "input": "arj7oStGLkU",
    "scrapedAt": "2026-10-08T09:00:00+00:00",
    "charged": true,
    "seconds": 0.84
}
```

Una respuesta a `?url=<video>` para un video sin subtítulos, sin `details=true`. El identificador y el título son marcadores; los demás valores son los que lleva una respuesta así. Es gratis:

```json
{
    "videoId": "<11-character ID>",
    "url": "https://www.youtube.com/watch?v=<11-character ID>",
    "status": "no_captions",
    "message": "The video has no captions, neither uploaded nor auto-generated.",
    "title": "<title of the video>",
    "language": null,
    "availableLanguages": [],
    "wordCount": 0,
    "charged": false
}
```

Con `details=true`, la misma respuesta traería los datos del video y se cobraría.

### Casos de uso

- **Haz que un chatbot responda preguntas sobre cualquier video de YouTube** que pegue un cliente, sin pausas en la conversación.
- **Dale a un asistente de voz las palabras de un video** lo bastante rápido para leer un resumen mientras la persona sigue al teléfono.
- **Añade un botón «resumir este video»** a una extensión del navegador o a una aplicación, con tu propio servidor guardando el token.
- **Equipa a tu agente de IA con una herramienta de transcripción de menos de un segundo** para investigación, soporte o ventas.
- **Muestra los capítulos, los «me gusta» y los comentarios principales de un video junto a su transcripción** en un panel en vivo.
- **Usa el mismo Actor por lotes** para canales, listas y búsquedas, con solo videos nuevos de forma programada.

### Ejemplos de entrada

#### Dos puertas al mismo Actor

1. **La dirección siempre encendida** (Standby). Le envías una **petición**, una pregunta enviada por internet, y la respuesta vuelve directamente a quien preguntó. Un video por petición.
2. **El botón Start.** Inicia una **ejecución** que procesa un lote (videos, canales enteros, listas, búsquedas) y deja los resultados en una tabla que puedes descargar. Cuesta lo mismo por resultado. Primero tiene que arrancar, así que es para trabajos en los que nadie espera.

Todo lo que puede hacer este Actor está disponible con el botón **Start**. La dirección hace la parte urgente: un video, ahora.

#### Parámetros de la dirección siempre encendida

**Dirección base:** `https://nokia2k--youtube-transcript-instant-api.apify.actor`. **Inicio de sesión:** cabecera `Authorization: Bearer <YOUR_APIFY_TOKEN>`, o `?token=<YOUR_APIFY_TOKEN>`. **Peticiones:** `GET /?url=<enlace del video o identificador de 11 caracteres>`, o `POST /` con un cuerpo JSON que usa los mismos campos que el formulario de entrada, para un video.

| Parámetro | Qué hace | Ejemplo |
| --- | --- | --- |
| `url` (también `video`, `v`, `id`) | El video: un enlace o el identificador de 11 caracteres. Obligatorio. | `url=arj7oStGLkU` |
| `language` (también `lang`) | Código o nombre de idioma. Varios, separados por comas, se prueban en orden. Sin él recibes el idioma original del video. | `language=es,en` |
| `translate` | La traducción automática de YouTube a este idioma cuando el video no tiene subtítulos en él. | `translate=es` |
| `format` | `text`, `timestampedText`, `srt`, `vtt` o `markdown`: la respuesta es el texto a secas en ese formato. Sin él, la respuesta es la fila JSON. | `format=srt` |
| `details=true` | Añade todo lo que YouTube muestra del video y de su canal. | `details=true` |
| `comments=<número>` | Añade los comentarios principales, hasta 100. Necesita `details=true`. | `comments=20` |

Otros campos del formulario se pueden enviar con su propio nombre, por ejemplo `captionType=manual` o `mergeSegmentsSeconds=60`. Las dos opciones de archivos, `saveFiles` y `combinedFile`, son de las ejecuciones normales.

#### Un video en la dirección, en texto plano

```bash
curl -s "https://nokia2k--youtube-transcript-instant-api.apify.actor/?url=arj7oStGLkU&format=text" \
  -H "Authorization: Bearer <YOUR_APIFY_TOKEN>"
```

#### Un video con datos y cinco comentarios, en JSON

```bash
curl -s "https://nokia2k--youtube-transcript-instant-api.apify.actor/?url=arj7oStGLkU&details=true&comments=5" \
  -H "Authorization: Bearer <YOUR_APIFY_TOKEN>"
```

#### Primero español, o traducido al español, en SRT

```bash
curl -s "https://nokia2k--youtube-transcript-instant-api.apify.actor/?url=arj7oStGLkU&language=es,en&translate=es&format=srt" \
  -H "Authorization: Bearer <YOUR_APIFY_TOKEN>"
```

#### POST con un cuerpo JSON, un video

```bash
curl -s -X POST "https://nokia2k--youtube-transcript-instant-api.apify.actor/" \
  -H "Authorization: Bearer <YOUR_APIFY_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"urls": ["https://www.youtube.com/watch?v=arj7oStGLkU"], "languages": ["es", "en"], "includeDetails": true, "maxComments": 5}'
```

#### Un lote con el botón Start: videos con datos

```json
{
    "urls": [
        "https://www.youtube.com/watch?v=arj7oStGLkU",
        "https://youtu.be/jNQXAC9IVRw"
    ],
    "outputFormats": ["text"],
    "includeDetails": true,
    "maxComments": 5
}
```

#### Los 20 videos más nuevos de un canal, primero en español, en Markdown

```json
{
    "urls": ["https://www.youtube.com/@TED"],
    "maxVideosPerSource": 20,
    "languages": ["es", "en"],
    "outputFormats": ["markdown"],
    "contentTypes": ["videos"]
}
```

#### Un tema en lugar de enlaces, primero los más vistos de este mes

```json
{
    "searchQueries": ["how to learn spanish"],
    "maxVideosPerSearch": 10,
    "searchUploadDate": "month",
    "searchSortBy": "views"
}
```

#### Solo los videos nuevos de un canal, para una programación

```json
{
    "urls": ["https://www.youtube.com/@TED"],
    "maxVideosPerSource": 50,
    "onlyNewVideos": true,
    "stateKey": "ted-daily",
    "outputFormats": ["text"]
}
```

#### Todos los campos del formulario (ejecuciones normales)

Solo hace falta el primer campo o el de búsqueda. El resto tiene valores por defecto razonables.

| Campo del formulario | Nombre | Qué es y cuándo tocarlo | Ejemplo |
| --- | --- | --- | --- |
| 📺 Videos, channels or playlists | `urls` | Enlaces de YouTube, uno por línea. Un enlace de video da ese video; un enlace de canal o `@nombre` da los videos del canal, primero los más nuevos; un enlace de lista da sus videos. | `https://www.youtube.com/@TED` |
| 🔢 Videos per channel or playlist | `maxVideosPerSource` | «Videos por canal o lista»: cuántos tomar de cada uno. Por defecto 50, hasta 5000. Empieza con poco: cada fila cuesta. | `5` |
| 🌐 Transcript language | `languages` | «Idioma de la transcripción». Déjalo en "Original language" para recibir cada video en el idioma en que se habla. O elige idiomas por orden de preferencia: 70 en la lista, y se puede escribir cualquier otro código o nombre. | `["es", "en"]` |
| 📄 Output formats | `outputFormats` | «Formatos de salida»: `segments`, `text`, `timestampedText`, `srt`, `vtt`, `markdown`. Varios cuestan lo mismo que uno. | `["text", "markdown"]` |
| 📚 Also give me everything as one Markdown file | `combinedFile` | Guarda todas las transcripciones de la ejecución en un archivo, `ALL_TRANSCRIPTS.md`, para ChatGPT, Claude, NotebookLM o una base de conocimiento. Desactivado por defecto. | `true` |
| 💾 Also save subtitle files I can download | `saveFiles` | Guarda un archivo `.srt`, `.vtt` o `.txt` por video y añade enlaces de descarga en `files`. | `true` |
| 📊 Video and channel details | `includeDetails` | «Datos del video y del canal»: fecha exacta, «me gusta», número de comentarios, capítulos, hashtags, suscriptores del canal y más. Activado por defecto. | `true` |
| 💬 Top comments per video | `maxComments` | «Comentarios principales por video»: cuántos añadir a cada fila. Por defecto 0, hasta 100. | `20` |
| 🔎 What to search for | `searchQueries` | «Qué buscar»: lo que escribirías en el buscador de YouTube, una búsqueda por línea. | `how to learn spanish` |
| 🔢 Videos per search | `maxVideosPerSearch` | Cuántos de los primeros resultados tomar por búsqueda. Por defecto 10, hasta 500. | `5` |
| 📅 Uploaded | `searchUploadDate` | Filtro propio de YouTube: `any`, `hour`, `today`, `week`, `month`, `year`. | `month` |
| ⏱️ Length | `searchDuration` | Filtro propio de YouTube: `any`, `short` (menos de 4 minutos), `medium` (de 4 a 20), `long` (más de 20). | `medium` |
| ↕️ Order | `searchSortBy` | `relevance` es el orden de YouTube; `date` y `views` ordenan así los videos encontrados. | `views` |
| Only videos that have captions (recommended) | `onlyWithCaptions` | Pide a YouTube solo videos con subtítulos. Activado por defecto. | `true` |
| 📅 Only videos newer than | `newerThan` | Una fecha, o una antigüedad como `30 days`. Para canales y listas. El corte es aproximado. | `6 months` |
| Only new videos since the last run | `onlyNewVideos` | Para ejecuciones programadas: solo los videos no entregados antes para los mismos canales, listas y búsquedas. Desactivado por defecto. | `true` |
| Memory name | `stateKey` | «Nombre de la memoria». Opcional, con el campo anterior: el nombre de la lista de videos entregados. | `ted-daily` |
| 🎞️ Kinds of videos to take from a channel | `contentTypes` | Videos normales, Shorts, grabaciones de directos. Desmarca lo que no necesites. | `["videos"]` |
| 🔁 Translate to | `translateTo` | La traducción automática de YouTube, que se usa cuando el video no tiene subtítulos en ese idioma. | `es` |
| If my language is missing, give me the original language | `fallbackToAnyLanguage` | Activado: recibes igualmente una transcripción, en el idioma original. Desactivado: esos videos se omiten. | `false` |
| Caption type | `captionType` | `any` (primero los de personas), `manual` (solo de personas), `auto` (solo automáticos). | `manual` |
| Merge caption lines into blocks | `mergeSegmentsSeconds` | Junta las líneas cortas en bloques de unos tantos segundos. 0 conserva las líneas originales. | `60` |
| Include video details | `includeMetadata` | Añade canal, duración, visualizaciones, descripción, etiquetas y miniatura. El título va siempre. | `true` |

### Ejecútalo de forma programada y recibe solo los videos nuevos

La dirección siempre encendida responde un video por petición. Seguir canales, listas o búsquedas es trabajo de las ejecuciones normales programadas.

#### Cómo funciona «Only new videos since the last run»

- Activa **Only new videos since the last run** (`onlyNewVideos`). Está desactivado por defecto y está pensado para ejecuciones programadas (las **Schedules** de Apify, «Programaciones»).
- Cada ejecución entrega entonces solo los videos que no había entregado antes para los mismos canales, listas y búsquedas. Los videos ya entregados se dejan fuera **antes de pedir nada**, así que ni se piden ni se cobran.
- La primera ejecución entrega todo lo que encuentra y lo recuerda.
- Los enlaces de videos que pegas directamente se entregan siempre, nunca se dejan fuera. Se recuerdan, así que un canal o una búsqueda que los contenga más adelante no los vuelve a entregar.

#### Configúralo paso a paso

1. **Rellena el formulario:** tus canales, listas o búsquedas y tus formatos. Marca **Only new videos since the last run**. Opcional: escribe un **Memory name** como `ted-daily`.
2. **Haz clic en Start una vez a mano.** La primera ejecución entrega lo que encuentra y lo recuerda. Además deja el Actor listo para programarse: un Actor tiene que haberse ejecutado una vez antes de poder programarlo.
3. **Haz clic en Save as a new task** («Guardar como tarea nueva»), arriba a la derecha en la página del Actor. Una **tarea** es un formulario guardado; aparece en **Saved tasks** («Tareas guardadas»).
4. **Abre Schedules** en el menú de la izquierda y haz clic en **Create new** («Crear nueva»).
5. **En la tarjeta Schedule setup** («Configuración de la programación») elige cada cuánto (una expresión cron como `@daily`, o el constructor visual) y tu zona horaria. **Next runs** («Próximas ejecuciones») muestra las fechas siguientes.
6. **En el desplegable Add** («Añadir») elige tu tarea.
7. **Haz clic en Enable** («Activar»). Las programaciones nuevas empiezan desactivadas.

#### Qué verás en las ejecuciones siguientes

- El registro y el mensaje final de la ejecución dicen cuántos videos se dejaron fuera, por ejemplo: "Skipped 37 videos already delivered in earlier runs (memory 'x')."
- Una ejecución sin nada nuevo termina con normalidad, sin filas: "No new videos since the last run: … Nothing was charged."

#### Nombre de la memoria: una lista o varias

- **Vacío:** la memoria se llama `auto-…` según tu lista de canales, listas y búsquedas. El orden y las mayúsculas de las búsquedas no importan; cualquier otro cambio en la lista empieza una memoria nueva.
- **Con un nombre:** eliges tú el registro. El mismo nombre mantiene una sola lista aunque cambien los enlaces; un nombre distinto mantiene una lista aparte.
- Se conservan las letras, las cifras y `-_.!'()`; los demás caracteres se convierten en `-`.

#### Dónde se guarda la memoria y cómo empezar de cero

La memoria se guarda en tu propia cuenta de Apify: **Storage** («Almacenamiento») > **Key-value stores** («almacenes de archivos»), en el almacén llamado `youtube-transcripts-memory`. Cada memoria es un registro que guarda los identificadores de los 20 000 videos entregados más recientes y la hora de su última actualización. Borra el registro, o el almacén entero, y la siguiente ejecución empieza de cero y vuelve a entregarlo todo.

#### Conviene saber

- **Se recuerdan:** los videos entregados como `success` o `no_captions`, y toda fila que se cobró. Las filas gratuitas (`blocked`, `error`, `age_restricted` sin datos) y un directo que sigue en marcha o no ha empezado se vuelven a intentar en la siguiente ejecución.
- **Nunca se cobra dos veces:** una fila puede cobrarse por sus datos aunque su estado no sea `success` ni `no_captions`, por ejemplo un video `age_restricted` cuyos datos llegaron. Todo video cobrado queda recordado, así que una ejecución posterior con **Only new videos since the last run** no vuelve a cobrarlo.
- **Mantén las cantidades lo bastante altas.** **🔢 Videos per channel or playlist** y **🔢 Videos per search** cuentan los videos que se miran en cada ejecución, antes de dejar fuera los conocidos.
- **La memoria se guarda al final de la ejecución,** también cuando la ejecución se detiene antes de su tiempo máximo o en el tope de gasto (entonces, solo con los videos que se procesaron). Tras un traslado de servidor, la ejecución continúa y guarda al final; nada se cuenta ni se cobra dos veces.
- **Si la memoria no se puede leer,** la ejecución anota un aviso y entrega todos los videos como una ejecución normal. Si no se puede guardar, los resultados se entregan igualmente, y la siguiente ejecución puede repetir algunos videos.
- **Dos ejecuciones con la misma memoria al mismo tiempo** pueden entregar las dos el mismo video nuevo. La memoria guarda los videos de ambas.

#### Envía los videos nuevos a Google Sheets o Slack

- **n8n:** añade el nodo **Apify Trigger** (**Resource to Watch**: **Task**, tu tarea; **Event Type**: **Succeeded**). Después un nodo **Apify** con Resource **Dataset**, **Get Items** y el `defaultDatasetId` de la ejecución como **Dataset ID** (sube **Limit**: empieza en 50). Después un nodo de Google Sheets que añada una fila por elemento (`title`, `url`, `publishedAt`, `text`), o un nodo de Slack que publique `title` y `url`. Un n8n en tu propio servidor necesita una `WEBHOOK_URL` pública para que Apify pueda llegar a él.
- **Make:** empieza con **Watch Actor Runs** («Vigilar ejecuciones del Actor»; se dispara cuando termina una ejecución), después **Get Dataset Items** y después un módulo de Google Sheets (la guía de Apify usa "Bulk Add Rows") o de Slack.
- **Cualquier otra herramienta:** en la pestaña **Integrations** («Integraciones») de la tarea añade un webhook («aviso automático a otra aplicación») para el evento `ACTOR.RUN.SUCCEEDED`. Apify envía un POST a tu dirección; el identificador del dataset está en `resource.defaultDatasetId`. El receptor debe responder en menos de 2 minutos, y un webhook puede llegar dos veces.

Una ejecución sin videos nuevos termina igualmente como Succeeded, con un dataset vacío: esa vez tu hoja simplemente no recibe filas nuevas.

### Usa la API de transcripciones de YouTube en tiempo real desde agentes de IA (MCP)

MCP es el estándar que permite a un asistente de IA usar herramientas externas. La dirección MCP de Apify con este Actor como herramienta es:

```text
https://mcp.apify.com?tools=nokia2k/youtube-transcript-instant-api
```

Ten en cuenta: mediante MCP el asistente inicia una **ejecución normal** y la espera (hasta 45 segundos), así que este camino tiene la espera de arranque. Si tu propio agente debe responder en menos de un segundo, dale en su código una herramienta que llame directamente a la dirección siempre encendida (mira «Integraciones»).

El inicio de sesión es OAuth: en la primera conexión se abre una ventana del navegador y no se guarda ningún token en la configuración. También sirve una cabecera `Authorization: Bearer <YOUR_APIFY_TOKEN>`.

#### Claude.ai y Claude Desktop

1. Abre **Connectors** («Conectores») en Claude (**Settings** > **Connectors**, o **Customize** > **Connectors** según tu versión).
2. Elige **Add custom connector** («Añadir conector personalizado»).
3. Rellena **Name** («Nombre») y **Remote MCP server URL** («URL del servidor MCP remoto») con la dirección de arriba, haz clic en **Continue** («Continuar») y luego en **Add** («Añadir»).
4. Inicia sesión en Apify en la ventana del navegador que se abre.
5. En un chat, haz clic en **+**, luego en **Connectors**, y actívalo.

En el plan gratuito de Claude puedes añadir un conector personalizado. En los planes Team y Enterprise lo añade un propietario («Owner») para toda la organización.

#### Claude Desktop con un servidor local

`claude_desktop_config.json`, necesita Node.js:

```json
{
  "mcpServers": {
    "youtube-instant": {
      "command": "npx",
      "args": ["-y", "@apify/actors-mcp-server", "--tools", "nokia2k/youtube-transcript-instant-api"],
      "env": {
        "APIFY_TOKEN": "<YOUR_APIFY_TOKEN>"
      }
    }
  }
}
```

#### Cursor

`.cursor/mcp.json` en tu proyecto, o `~/.cursor/mcp.json`:

```json
{
  "mcpServers": {
    "youtube-instant": {
      "url": "https://mcp.apify.com?tools=nokia2k/youtube-transcript-instant-api"
    }
  }
}
```

Con un token en lugar de OAuth, añade `"headers": { "Authorization": "Bearer <YOUR_APIFY_TOKEN>" }` junto a `"url"`.

#### VS Code

Necesita GitHub Copilot. Abre la paleta de comandos, ejecuta **MCP: Open User Configuration** («abrir la configuración de usuario de MCP») y añade:

```json
{
  "servers": {
    "youtube-instant": {
      "type": "http",
      "url": "https://mcp.apify.com?tools=nokia2k/youtube-transcript-instant-api"
    }
  }
}
```

#### Claude Code y ChatGPT

```bash
claude mcp add --transport http youtube-instant \
  "https://mcp.apify.com?tools=nokia2k/youtube-transcript-instant-api" \
  --header "Authorization: Bearer $APIFY_TOKEN"
```

**ChatGPT:** necesita el modo Developer («modo de desarrollador»). Ve a **Settings > Apps & Connectors > Create** («Crear»), rellena **Name**, **Description** («Descripción») y **MCP Server URL**, deja **Authentication** («Autenticación») en OAuth y haz clic en **Create**. En un chat, haz clic en **+**, luego en **More** («Más»), y elige el conector.

#### Ejemplos de peticiones

- «Consigue la transcripción de https://www.youtube.com/watch?v=arj7oStGLkU y responde: ¿qué es el "panic monster"?»
- «Trae este video con sus capítulos y dime qué capítulo trata del calendario de la vida y en qué minuto: https://youtu.be/arj7oStGLkU»
- «Consigue la transcripción y los 5 comentarios principales de este video y redacta una respuesta breve al comentario con más "me gusta": \[enlace]»

### Integraciones: n8n, Make, Zapier, Python, JavaScript y LangChain

#### Qué son un token y una cabecera, y cómo guardar el token en secreto

**Un token** es una contraseña secreta larga que le dice a Apify qué cuenta pregunta y qué cuenta paga. Está en la consola, en **Settings** y luego **API & Integrations**; aquí se escribe `<YOUR_APIFY_TOKEN>`. **Una cabecera** es una línea con etiqueta que viaja con una petición pero fuera de la dirección, como el remitente escrito en el sobre en lugar de dentro de la carta.

Puedes entregar el token de dos formas:

- **En una cabecera** (la mejor forma): la cabecera se llama `Authorization` y su valor es la palabra `Bearer`, un espacio y tu token.
- **En la dirección**: añade `token=<YOUR_APIFY_TOKEN>` como parámetro. Es la única forma que funciona cuando escribes una dirección en un navegador.

Cómo guardarlo en secreto:

- Prefiere la cabecera. Una dirección se guarda en el historial del navegador y acaba copiada en chats, capturas de pantalla y registros. Una cabecera no.
- En n8n, Make o Zapier, guarda el token en el apartado propio de la herramienta para credenciales o conexiones, no en un campo de texto de un flujo que puedas compartir o exportar.
- No pongas nunca el token en algo que se ejecuta en el dispositivo de tu visitante: una página web, una extensión del navegador, una aplicación móvil. Cualquiera puede leerlo ahí. Deja que tu propio servidor guarde el token y llame a la dirección.
- No lo pegues en foros, capturas de pantalla ni en la pestaña **Issues** («Incidencias»).
- Si se filtra, vuelve a **Settings**, **API & Integrations**. Un token se puede rotar (sustituir por uno nuevo), se le puede poner fecha de caducidad y se puede limitar con el interruptor "Limit token permissions" («Limitar los permisos del token»).

#### n8n (nodo HTTP Request, la dirección siempre encendida)

En n8n una automatización es un «flujo de trabajo» y cada paso, un «nodo».

1. En tu flujo añade el nodo **HTTP Request**.
   - *Verás:* su panel de ajustes.
2. Pon **Method** («Método») en GET.
3. En **URL** pega `https://nokia2k--youtube-transcript-instant-api.apify.actor/`
4. En **Authentication** («Autenticación») elige el tipo de credencial genérico y luego **Header auth**. Crea una credencial con Name `Authorization` y Value `Bearer <YOUR_APIFY_TOKEN>`. Así n8n mantiene tu token fuera del propio flujo.
5. Activa **Send Query Parameters** («Enviar parámetros de consulta»). Añade un parámetro llamado `url` y dale el enlace de YouTube de tu nodo anterior. Añade otro llamado `format` con el valor `text` si solo quieres las palabras.
6. Haz clic en **Add Option** («Añadir opción»), elige **Timeout** («Tiempo máximo») y escribe `60000`. Son 60 segundos en milisegundos, para que el despertar tras una pausa nunca corte la petición.
7. Ejecuta el nodo.
   - *Verás:* con `format`, la transcripción como texto. Sin él, un elemento con campos como `status`, `title`, `text` y `seconds`.
8. Conecta el nodo siguiente. Cuando leas los campos, comprueba que `status` sea `success` antes de usar `text`.

Para enviar más opciones, cambia **Method** a POST, activa **Send Body** («Enviar cuerpo»), pon **Body Content Type** en **JSON** y **Specify Body** en **Using JSON**, y pega el ejemplo POST de «Ejemplos de entrada».

Para lotes, n8n tiene también un nodo **Apify**, que instalas desde el panel de nodos buscando **Apify**: Resource («Recurso») **Actor**, Operation («Operación») **Run an Actor and Get Dataset** («Ejecutar un Actor y obtener los resultados»). Elige este Actor y pega la entrada en **Input JSON**. Eso inicia una ejecución normal.

#### Make (módulo HTTP)

En Make una automatización es un «escenario» y cada paso, un «módulo». Aquí no se citan las pantallas propias de Make, así que busca estas cosas por lo que hacen.

1. Añade la aplicación HTTP de Make a tu escenario y elige el módulo que envía una petición.
2. En el campo de la dirección, pega `https://nokia2k--youtube-transcript-instant-api.apify.actor/`
3. Elige GET como método.
4. Añade una cabecera. Nombre: `Authorization`. Valor: `Bearer <YOUR_APIFY_TOKEN>`.
5. Añade un parámetro de consulta llamado `url` y asígnale el enlace de YouTube de tu módulo anterior. Añade `format` con el valor `text` si solo quieres las palabras.
6. Ejecuta el escenario una vez.
   - *Verás:* el código de estado 200 y la transcripción en la salida del módulo.
7. Si no pusiste `format`, activa la opción del módulo que lee la respuesta como datos. Así podrás asignar `title`, `text` y los demás campos en el módulo siguiente.

Para lotes, la aplicación de Apify en Make tiene **Run an Actor** («Ejecutar un Actor»; pon **Run synchronously**, «Ejecutar de forma síncrona», en **Yes**) seguido de **Get Dataset Items** («Obtener los resultados»). Para ejecuciones de más de unos 2 minutos, usa **Watch Actor Runs** («Vigilar ejecuciones del Actor») y después **Get Dataset Items**.

#### Zapier

1. Para la dirección siempre encendida: usa un paso de Zapier que pueda enviar una petición web y rellena lo mismo que en Make: la dirección, el método GET, la cabecera `Authorization` y el parámetro `url`.
2. Para un lote: añade la aplicación de Apify y elige la acción **Run Actor**. Haz clic en **Allow Access** («Permitir acceso») para conectar tu cuenta, elige este Actor y dale la entrada.
3. Un **Run Actor** síncrono tiene un límite estricto de 30 segundos. Para lo que pueda tardar más, usa dos Zaps: el Zap A termina con **Run Actor** (asíncrono); el Zap B empieza con **Finished Actor Run** («Ejecución del Actor terminada») y sigue con **Fetch Dataset Items** («Traer los resultados»).

#### Python (requests, la dirección siempre encendida)

```python
import requests

ADDRESS = "https://nokia2k--youtube-transcript-instant-api.apify.actor/"
HEADERS = {"Authorization": "Bearer <YOUR_APIFY_TOKEN>"}

response = requests.get(
    ADDRESS,
    params={
        "url": "https://www.youtube.com/watch?v=arj7oStGLkU",
        "details": "true",
        "comments": 5,
    },
    headers=HEADERS,
    timeout=60,
)
row = response.json()  # 200, 400 and 402 all answer with JSON here

if response.status_code == 200 and row["status"] == "success":
    print(row["title"], "-", row["wordCount"], "words in", row["seconds"], "s")
    print(row["text"][:200])
else:
    print(response.status_code, row["status"], row["message"])
```

#### JavaScript (fetch, la dirección siempre encendida)

Ejecútalo en tu servidor, no en una página web, para que el token siga siendo privado.

```javascript
const address = new URL('https://nokia2k--youtube-transcript-instant-api.apify.actor/');
address.searchParams.set('url', 'https://www.youtube.com/watch?v=arj7oStGLkU');
address.searchParams.set('format', 'srt');

const response = await fetch(address, {
    headers: { Authorization: 'Bearer <YOUR_APIFY_TOKEN>' },
});

const type = response.headers.get('content-type') || '';
if (type.includes('json')) {
    const row = await response.json(); // no transcript, or a problem
    console.log(response.status, row.status, row.message);
} else {
    console.log(await response.text()); // the subtitle file
}
```

#### Ejecuciones normales con la API de Apify: curl y Python

Un lote pasa por la API propia de Apify. Esta llamada inicia una ejecución, espera y devuelve las filas. Tiene un límite de 300 segundos; una ejecución más larga responde HTTP 408, así que inicia los trabajos grandes con `POST /v2/acts/nokia2k~youtube-transcript-instant-api/runs` y lee el dataset cuando la ejecución haya terminado.

```bash
curl -X POST "https://api.apify.com/v2/acts/nokia2k~youtube-transcript-instant-api/run-sync-get-dataset-items?maxTotalChargeUsd=5" \
  -H "Authorization: Bearer <YOUR_APIFY_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"urls": ["https://www.youtube.com/@TED"], "maxVideosPerSource": 5, "outputFormats": ["text"]}'
```

Con `pip install apify-client` (versión 3, Python 3.11 o posterior):

````python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("nokia2k/youtube-transcript-instant-api").call(
    run_input={
        "urls": ["https://www.youtube.com/@TED"],
        "maxVideosPerSource": 5,
        "outputFormats": ["text"],
    },
)
for row in client.dataset(run.default_dataset_id).iterate_items():
    print(row["status"], row



# Actor input Schema

## `urls` (type: `array`):

Paste YouTube links, one per line. A <b>channel</b> link (<code>https://www.youtube.com/@name</code>) gives you the videos of the channel, newest first. A <b>playlist</b> link gives you the videos of the playlist. A <b>video</b> link gives you that one video. You can mix them freely, and a plain <code>@name</code> works too.
## `maxVideosPerSource` (type: `integer`):

How many videos to take from each channel or playlist. Start with a small number to see what you get, then raise it. You only pay for transcripts that are delivered.
## `languages` (type: `array`):

Leave it on <b>Original language</b> to get each video in the language it is spoken in: you do not need to know the language in advance. Or pick one or more languages in order of preference, and the first one the video has is used. You can also type any language code (<code>pt-BR</code>, <code>es-419</code>) or a language name (<code>Spanish</code>, <code>español</code>, <code>German</code>). Captions written by people are preferred over auto-generated ones. Every row tells you which language was returned and which other languages the video has.
## `outputFormats` (type: `array`):

Choose how each transcript is returned. You can pick several: the price per transcript stays the same.
## `combinedFile` (type: `boolean`):

Saves all transcripts of the run into one file, <code>ALL_TRANSCRIPTS.md</code>, with a heading, the channel, the date and the link for every video. Upload it to ChatGPT, Claude, NotebookLM or your knowledge base in one go. You find it in the run's <b>Storage &gt; Key-value store</b> tab. Large runs are split into files of about 8 MB. The price stays the same.
## `saveFiles` (type: `boolean`):

Saves a ready-to-use file per video for each text format you selected above: <code>.srt</code>, <code>.vtt</code> and <code>.txt</code>. Each row gets a <code>files</code> field with direct download links, and all files are listed in the run's <b>Storage &gt; Key-value store</b> tab. The price per transcript stays the same.
## `includeDetails` (type: `boolean`):

Adds everything YouTube shows about the video: exact publish date, likes, number of comments, category, hashtags, links in the description, chapters, the most replayed moments, music credits, countries where it is blocked, and the channel's name, handle, subscribers and verified badge.
## `maxComments` (type: `integer`):

How many of the top comments to add to each row, with author, likes and reply count. 0 for none.
## `searchQueries` (type: `array`):

Type what you would type into YouTube's search box, one search per line. For each search you get the transcripts of the videos YouTube ranks highest. No links needed.
## `maxVideosPerSearch` (type: `integer`):

How many of the top results to take for each search. You only pay for transcripts that are delivered.
## `searchUploadDate` (type: `string`):

Keep only videos uploaded recently. This is YouTube's own filter.
## `searchDuration` (type: `string`):

Keep only short, medium or long videos. This is YouTube's own filter.
## `searchSortBy` (type: `string`):

Relevance is what YouTube shows by default. The other two put the found videos in order of date or views.
## `onlyWithCaptions` (type: `boolean`):

Asks YouTube for videos with captions only, so nearly every result comes back with a transcript.
## `newerThan` (type: `string`):

Leave empty for no limit. Pick a date, or an age such as <code>30 days</code> or <code>6 months</code>. YouTube's lists show the age of a video only roughly ("3 weeks ago"), so the cut is approximate: nothing newer is ever left out, and a few videos just past the limit can slip in. Every row carries the exact date in <code>publishedAt</code>.
## `onlyNewVideos` (type: `boolean`):

Turn this on when you run the Actor on a <b>schedule</b> (for example every day). Each run then gives you only the videos it has not delivered before for the same channels, playlists and searches, so you never get, or pay for, the same video twice. The first run delivers everything it finds and remembers it. The list of delivered videos is kept in your own Apify account, in the key-value store named <code>youtube-transcripts-memory</code> (<b>Storage &gt; Key-value stores</b>). Videos that failed are tried again next time. Video links you paste directly are always delivered.
## `stateKey` (type: `string`):

Optional. Used only with <b>Only new videos since the last run</b>. Leave it empty and the Actor makes a name from your channels, playlists and searches, so every combination has its own list. Type a name (for example <code>my-daily-news</code>) to choose it yourself: the same name keeps adding to the same list even if you change the links, and a different name starts a separate list. Letters, digits and <code>-_.!'()</code>; other characters become <code>-</code>.
## `contentTypes` (type: `array`):

A channel can hold regular videos, Shorts and recordings of live streams. Untick what you do not need.
## `translateTo` (type: `string`):

Pick a language to get YouTube's own machine translation when the video has no captions in that language. If the video already has captions in it, those are returned instead, because they are better than a machine translation. YouTube does not offer translation for every video: when it does not, you get the original language and a note in <code>message</code>.
## `fallbackToAnyLanguage` (type: `boolean`):

When on, a video without captions in the languages you picked still returns its transcript in its original language, and the <code>language</code> and <code>message</code> fields tell you which one. Turn it off to skip those videos instead. Skipped videos are not charged.
## `captionType` (type: `string`):

Choose which captions are accepted. Auto-generated captions are produced by YouTube's speech recognition and have no punctuation in many languages.
## `mergeSegmentsSeconds` (type: `integer`):

YouTube captions come in lines of a few words. Set a number of seconds, for example 30, to join them into longer blocks. This is useful for chapters, search indexes and RAG chunks. Use 0 to keep the original lines.
## `includeMetadata` (type: `boolean`):

Adds channel, duration, view count, description, keywords and thumbnail to every row. The video title is always included.

## Actor input object example

```json
{
  "urls": [
    "https://www.youtube.com/@TED",
    "https://www.youtube.com/watch?v=arj7oStGLkU"
  ],
  "maxVideosPerSource": 5,
  "languages": [
    "es",
    "en"
  ],
  "outputFormats": [
    "text",
    "markdown"
  ],
  "combinedFile": false,
  "saveFiles": false,
  "includeDetails": true,
  "maxComments": 0,
  "searchQueries": [
    "how to learn spanish",
    "iphone 17 review"
  ],
  "maxVideosPerSearch": 5,
  "searchUploadDate": "any",
  "searchDuration": "any",
  "searchSortBy": "relevance",
  "onlyWithCaptions": true,
  "newerThan": "30 days",
  "onlyNewVideos": false,
  "stateKey": "my-daily-news",
  "contentTypes": [
    "videos"
  ],
  "translateTo": "es",
  "fallbackToAnyLanguage": true,
  "captionType": "any",
  "mergeSegmentsSeconds": 0,
  "includeMetadata": true
}
````

# Actor output Schema

## `transcripts` (type: `string`):

All results of the run, one item per video. The 'charged' field of each item says whether it was charged.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "urls": [
        "https://www.youtube.com/watch?v=arj7oStGLkU"
    ],
    "maxVideosPerSource": 5,
    "maxVideosPerSearch": 5
};

// Run the Actor and wait for it to finish
const run = await client.actor("nokia2k/youtube-transcript-instant-api").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "urls": ["https://www.youtube.com/watch?v=arj7oStGLkU"],
    "maxVideosPerSource": 5,
    "maxVideosPerSearch": 5,
}

# Run the Actor and wait for it to finish
run = client.actor("nokia2k/youtube-transcript-instant-api").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "urls": [
    "https://www.youtube.com/watch?v=arj7oStGLkU"
  ],
  "maxVideosPerSource": 5,
  "maxVideosPerSearch": 5
}' |
apify call nokia2k/youtube-transcript-instant-api --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,nokia2k/youtube-transcript-instant-api"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/2wfiHoHQJobrvD9el/builds/nvBfW1vnWyfjNk407/openapi.json
