# YouTube Transcript Scraper: Channels, SRT & VTT (`zaiq/youtube-transcript-scraper`) Actor

Get YouTube transcripts, subtitles and captions as text, timed segments, SRT or VTT, with video details, from videos, Shorts, live replays, playlists and whole channels. Manual and auto-generated captions in any language, optional auto-translation. Pay only for transcripts delivered.

- **URL**: https://apify.com/zaiq/youtube-transcript-scraper.md
- **Developed by:** [Zaiq](https://apify.com/zaiq) (community)
- **Categories:** Videos, AI, Social media
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $8.00 / 1,000 transcripts

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## 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

## YouTube Transcript Scraper: Channels, SRT & VTT

Get the transcript, subtitles and captions of any public YouTube video as plain text, timed segments, SRT or WebVTT,
together with the video's details: title, channel, duration, publish date, views. Paste video links, Shorts, live
replays, whole playlists or channels. Manual and auto-generated captions in any language, with optional
auto-translation. You pay only for transcripts delivered.

### What it does

For each video it returns one row with:

- **The transcript** as continuous text (optionally with a timestamp on every line), as timed segments
  (start and duration in seconds), and as SRT or WebVTT subtitle files if you ask for them.
- **Which captions it is**: language, manual (uploaded by the channel) or auto-generated, and whether it was
  translated.
- **Every caption language the video has**, manual and auto-generated, so you can see what else is available.
- **Video details**: title, channel name, ID and link, duration, publish and upload dates, views, likes, category,
  description, tags, thumbnail, and whether it is a Short, a live stream or a live replay.
- **A clear status** when there is no transcript: no captions, private, removed, age-restricted, live now, and so
  on. Those rows cost nothing.

Channels and playlists are listed newest first, up to the number of videos you choose per channel or playlist.

### Who uses it

- **AI and RAG pipelines**: feed lectures, talks, podcasts and tutorials to a language model, a search index or a
  vector database.
- **Content repurposing**: turn videos into articles, newsletters, show notes, quotes and social posts.
- **Research and monitoring**: study what channels say over time, across languages.
- **SEO and accessibility**: subtitles as SRT or VTT, and the spoken words of videos you embed on your pages.

### Example

A short NASA explainer, fetched on 4 October 2026 (row trimmed: the full text, all segments and the full SRT are
in the real output):

```json
{
  "videoId": "MDMM9MZNcco",
  "url": "https://www.youtube.com/watch?v=MDMM9MZNcco",
  "status": "ok",
  "title": "How Do We Communicate With Spacecraft? We Asked a NASA Expert",
  "channelName": "NASA",
  "channelId": "UCLA_DiR1FfKNvjuUpBHmylQ",
  "durationSeconds": 119,
  "publishDate": "2023-02-08T19:50:42Z",
  "viewCount": 58556,
  "language": "en",
  "languageName": "English",
  "isAutoGenerated": false,
  "audioLanguage": "en",
  "availableLanguages": [
    { "code": "en", "name": "English", "isAutoGenerated": false, "isTranslatable": true },
    { "code": "en", "name": "English", "isAutoGenerated": true, "isTranslatable": true }
  ],
  "text": "How do we communicate with spacecraft? NASA. We Asked a NASA Technologist. We communicate with spacecraft in a lot of different ways. We have a network of antennas all over the world across all seven continents, along with satellites in space that help transmit these radio waves. ...",
  "segments": [
    { "start": 2.402, "duration": 2.002, "text": "How do we communicate with spacecraft?" },
    { "start": 4.938, "duration": 2.502, "text": "NASA." },
    { "start": 7.44, "duration": 1.569, "text": "We Asked a NASA Technologist." }
  ],
  "srt": "1\n00:00:02,402 --> 00:00:04,404\nHow do we communicate with spacecraft?\n\n2\n00:00:04,938 --> 00:00:07,440\nNASA.\n...",
  "wordCount": 303,
  "charged": true,
  "fetchedAt": "2026-10-04T14:19:42Z"
}
```

In our tests on 4 October 2026, through residential connections, 356 of 356 recent videos with captions from 16
public channels (science, technology, universities, news) returned a transcript: 200 videos with 10 at a time
and 160 with 30 at a time, about 1.6 seconds per video (median). The videos without captions came back as
`no-captions`.

### Input

```json
{
  "videos": [
    "https://www.youtube.com/watch?v=MDMM9MZNcco",
    "https://youtu.be/arj7oStGLkU",
    "https://www.youtube.com/shorts/VV_JW4iCni0"
  ],
  "channelsOrPlaylists": ["https://www.youtube.com/@NASA"],
  "maxVideosPerSource": 20,
  "languages": ["en"],
  "outputFormats": ["text", "segments", "srt"]
}
```

- `videos`: links or 11-character video IDs. Watch, youtu.be, Shorts, embed, live and mobile links all work.
- `channelsOrPlaylists`: channel links (@handle, /channel/..., /c/..., /user/...) or playlist links.
  `maxVideosPerSource` sets how many of the newest videos are taken from each; `channelContent` chooses Videos
  (default), Shorts, Live stream replays or All uploads. A channel link ending in /shorts or /streams does the
  same.
- `languages`: language codes in order of preference. Leave empty for the language spoken in the video.
  `fallbackToAnyLanguage` (on by default) returns the video's own language when none of yours is available.
- `captionType`: manual first (default), auto-generated first, manual only or auto-generated only.
- `translateTo`: a language code. If the video has captions in that language they are used; otherwise YouTube's
  own auto-translation is requested.
- `outputFormats`: `text`, `segments`, `srt`, `vtt`. `includeTimestamps` puts a time at the start of every text
  line; `wordTimestamps` adds each word's start time to the segments of auto-generated captions.
- `includeMetadata`: likes, category, full description, tags, thumbnail and channel link (title, channel,
  duration, publish date and views are always included).
- `proxyConfiguration`: residential by default (see FAQ).

### Output

One row per video. The fields you will use most:

| Field | What it holds |
|---|---|
| `status` | `ok`, `no-captions`, `language-unavailable`, `unavailable`, `private`, `age-restricted`, `members-only`, `region-blocked`, `live`, `upcoming`, `blocked`, `invalid-input` |
| `error` | why there is no transcript, in plain words |
| `text` | the transcript as one text (or one line per caption with `includeTimestamps`) |
| `segments` | `[{ "start": 7.47, "duration": 2.899, "text": "..." }]`, seconds |
| `srt`, `vtt` | subtitle files as text, when asked for |
| `language`, `languageName`, `isAutoGenerated` | which captions were returned |
| `isTranslated`, `translatedFrom`, `translationError` | auto-translation, when asked for |
| `audioLanguage` | the language spoken in the video, as YouTube's speech recognition sees it |
| `isFromDubbedAudio` | the captions belong to one of YouTube's AI-dubbed audio tracks, not the original voice |
| `availableLanguages` | every caption track the video has |
| `wordCount` | words in the transcript (Chinese and Japanese characters count one each) |
| `charged` | `true` only for rows you paid for |

Very long videos: if a row would pass Apify's 9 MB item limit, its SRT, VTT (and if needed segments and text)
are stored in the run's key-value store and linked from the row as `srtUrl`, `vttUrl`, `segmentsUrl` or
`textUrl`. In our tests a 10.5-hour video with all formats needed this; a 5-hour one did not. The key-value store
record `SUMMARY` has the run's counts by status.

### Use it from code, automations and AI agents

- **API**: run it and get the results in one call: `POST https://api.apify.com/v2/acts/zaiq~youtube-transcript-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN` with the input as JSON. The API tab on this page has ready-made Python, JavaScript and cURL examples.
- **Python** (`pip install apify-client`):

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_TOKEN")
run = client.actor("zaiq/youtube-transcript-scraper").call(run_input={'videos': ['https://www.youtube.com/watch?v=UF8uR6Z6KLc', 'https://youtu.be/arj7oStGLkU'], 'languages': ['en'], 'outputFormats': ['text', 'segments']})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)
```

- **No code**: connect it to Google Sheets, Zapier, Make or n8n from the Integrations tab, schedule runs in Apify Console, and get a webhook when a run finishes.
- **AI agents (MCP)**: add it as a tool to Claude, Cursor or any MCP client through Apify's MCP server: `https://mcp.apify.com?tools=zaiq/youtube-transcript-scraper`.

### Pricing

You pay only for transcripts delivered. Apify's platform usage is included.

| Event | Price |
|---|---|
| Transcript delivered | $0.01 ($10 per 1,000 videos) |
| Translated transcript delivered (YouTube auto-translation into your `translateTo` language) | $0.02 |

Plus Apify's standard run start fee of $0.00005.

Free: videos without captions, captions not in your languages (with fallback off), private, removed, unavailable,
age-restricted, members-only and region-blocked videos, live streams still in progress, videos YouTube blocked on
every try, invalid links, and every retry. A row is charged only when its `charged` field is `true`. If an
auto-translation fails, you get the original transcript at the normal price and `translationError` says why.
Your spending limit for the run is respected: the run stops cleanly when it is reached.

### Limits

- Only captions that exist on YouTube are returned; the Actor does not transcribe audio itself. Most videos with
  speech have YouTube's auto-generated captions; music-only videos and some short clips have none.
- Age-restricted, private and members-only videos need a signed-in account, which this Actor never uses. They come
  back with their status, free.
- YouTube rate-limits auto-translation heavily (in our tests about one request in eight succeeded), so
  translations are retried from fresh connections and take 10 to 35 seconds each. Some can still fail; those
  rows keep the original transcript.
- For "the video's own language", the language comes from YouTube's speech recognition. For music-only videos it
  is sometimes wrong; set `languages` if you know what you need.
- YouTube shows visitors who are not signed in at most the latest 40 Shorts of a channel.
- YouTube changes its systems often. The Actor tries several of YouTube's own app interfaces in turn, so one
  change rarely stops it, but a large change can need an update.
- Use it for videos you are allowed to process, and follow YouTube's terms and the rights of the video owners.

### FAQ

**Do I need a YouTube account, API key or cookies?** No. The Actor reads only what YouTube shows any visitor who
is not signed in.

**Why residential proxies?** YouTube answers data-centre addresses with "Sign in to confirm you're not a bot" far
more often. Every blocked try is retried from a new address and costs you nothing; if all tries are blocked, the
row says `blocked` and is free.

**Manual or auto-generated captions?** By default manual captions (uploaded by the channel) are preferred, then
YouTube's auto-generated ones. `isAutoGenerated` tells you which you got; `captionType` lets you insist on one kind.

**Can I get every language a video has?** `availableLanguages` lists them. Run the Actor again with other
`languages` to fetch them.

**How fast is it?** In our tests a typical video took 1 to 3 seconds and 30 videos ran at once; 160 videos
finished in under half a minute.

# Actor input Schema

## `videos` (type: `array`):

Video links or 11-character video IDs, one per line. Watch, youtu.be, Shorts, embed and live links all work. Playlist and channel links pasted here are listed too.

## `channelsOrPlaylists` (type: `array`):

Optional. Channel links (@handle, /channel/UC..., /c/..., /user/...) or playlist links (...?list=PL...). Their videos are listed newest first, up to the limit below for each.

## `maxVideosPerSource` (type: `integer`):

The newest videos are taken first.

## `channelContent` (type: `string`):

Which of a channel's uploads to list. A channel link ending in /shorts or /streams overrides this. YouTube lists at most the latest 40 Shorts per channel to visitors who are not signed in.

## `languages` (type: `array`):

Language codes in order of preference, for example en, es, de, pt-BR. The first one the video has is returned. Leave empty for the language spoken in the video.

## `fallbackToAnyLanguage` (type: `boolean`):

Off: a video without captions in your languages comes back as language-unavailable (free), with the languages it does have.

## `captionType` (type: `string`):

Manual captions are uploaded by the channel; auto-generated ones come from YouTube's speech recognition.

## `translateTo` (type: `string`):

Optional. Return the transcript in this language. If the video has captions in it, those are used; otherwise YouTube's own auto-translation is requested. YouTube rate-limits auto-translation, so some may fail; then the original transcript is returned and charged at the normal price.

## `outputFormats` (type: `array`):

Plain text, timed segments (start and duration in seconds), SRT and WebVTT subtitle files. Very long SRT or VTT files are stored in the run's key-value store and linked from the row.

## `includeTimestamps` (type: `boolean`):

Start each line of the plain text with its time, like \[01:23]. Off: one continuous text.

## `includeMetadata` (type: `boolean`):

Add publish and upload dates, likes, category, full description, tags, thumbnail and channel link. Title, channel, duration, publish date and views are always included.

## `wordTimestamps` (type: `boolean`):

For auto-generated captions, add each word's start time inside the timed segments.

## `proxyConfiguration` (type: `object`):

YouTube asks data-centre addresses to sign in far more often, so residential proxies are the default. Pick a country if you need videos available only there.

## `maxConcurrency` (type: `integer`):

How many videos are fetched at the same time.

## `maxTries` (type: `integer`):

When YouTube blocks a request, the video is tried again from a new address, up to this many times. Blocked tries are free.

## Actor input object example

```json
{
  "videos": [
    "https://www.youtube.com/watch?v=UF8uR6Z6KLc",
    "https://youtu.be/arj7oStGLkU"
  ],
  "maxVideosPerSource": 20,
  "channelContent": "videos",
  "languages": [
    "en"
  ],
  "fallbackToAnyLanguage": true,
  "captionType": "manual-first",
  "outputFormats": [
    "text",
    "segments"
  ],
  "includeTimestamps": false,
  "includeMetadata": true,
  "wordTimestamps": false,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  },
  "maxConcurrency": 10,
  "maxTries": 5
}
```

# Actor output Schema

## `results` (type: `string`):

Dataset with one row per video.

## `summary` (type: `string`):

Counts by status, rows charged and proxy traffic for the run.

## `files` (type: `string`):

Key-value store holding SRT, VTT or segment files too large for a dataset row (linked from the row as srtUrl, vttUrl, segmentsUrl or textUrl).

# 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 = {
    "videos": [
        "https://www.youtube.com/watch?v=UF8uR6Z6KLc",
        "https://youtu.be/arj7oStGLkU"
    ],
    "languages": [
        "en"
    ],
    "outputFormats": [
        "text",
        "segments"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("zaiq/youtube-transcript-scraper").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 = {
    "videos": [
        "https://www.youtube.com/watch?v=UF8uR6Z6KLc",
        "https://youtu.be/arj7oStGLkU",
    ],
    "languages": ["en"],
    "outputFormats": [
        "text",
        "segments",
    ],
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("zaiq/youtube-transcript-scraper").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 '{
  "videos": [
    "https://www.youtube.com/watch?v=UF8uR6Z6KLc",
    "https://youtu.be/arj7oStGLkU"
  ],
  "languages": [
    "en"
  ],
  "outputFormats": [
    "text",
    "segments"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call zaiq/youtube-transcript-scraper --silent --output-dataset

```

## MCP server setup

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

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/a0QdMyxd5YGUVF6t3/builds/KAnt0wt8ZL9XQeTOt/openapi.json
