# YouTube Transcript Scraper - Captions & Subtitles for AI/RAG (`cprussin/youtube-transcripts`) Actor

Get YouTube transcripts and captions as plain text, timestamped segments, SRT or VTT, with video metadata. Videos, channels and playlists; language preference, auto-generated fallback and translation. Pay only per transcript.

- **URL**: https://apify.com/cprussin/youtube-transcripts.md
- **Developed by:** [Connor Prussin](https://apify.com/cprussin) (community)
- **Categories:** Videos, AI
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.50 / 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.

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: captions and subtitles for AI and RAG

**YouTube Transcript Scraper** extracts the transcript (captions / subtitles) of any public YouTube video as **clean plain text**, **timestamped segments**, **SRT** or **WebVTT**, together with the video's metadata. Paste video links, or a whole **channel** or **playlist**, and get one tidy JSON record per video that you can drop straight into an LLM prompt, a vector database or a spreadsheet.

- ✅ **Videos, Shorts, live replays, channels and playlists.** Channel and playlist URLs are expanded to their newest videos.
- ✅ **Language control.** Pick preferred languages in order; human-made captions win over auto-generated ones. Optional fallback to any language, and optional **YouTube machine translation** into a target language.
- ✅ **Four output formats**: plain text, `[{start, duration, text}]` segments, SRT and VTT.
- ✅ **Metadata included**: title, channel, publish date, duration, views, likes, category, keywords, description, thumbnail.
- ✅ **Pay only for transcripts you get.** Videos with no captions, private or deleted videos cost nothing.
- ✅ **Residential proxies built in** and included in the price, with automatic retries when YouTube pushes back.

### What can I use YouTube transcripts for?

- **RAG and AI assistants**: index a channel's talks, lectures or podcasts in a vector store and answer questions with citations (segments carry timestamps, so you can link to the exact second).
- **Summaries and notes**: feed the `transcript` field to ChatGPT, Claude or Gemini to summarize videos, extract action items or write blog posts and show notes.
- **LLM fine-tuning and evaluation datasets** built from spoken-language content.
- **Content research and SEO**: find what competitors say, mine keywords and topics, repurpose your own videos into articles.
- **Subtitles**: download SRT/VTT to re-edit, burn in or translate captions.
- **Market and academic research**: analyze what is said across many videos, channels or languages.

### How does the YouTube transcript scraper work?

1. Each URL is parsed. Channel and playlist URLs are expanded to up to `maxVideosPerSource` of their newest videos.
2. For every video the actor asks YouTube's own player API which caption tracks exist, picks the best track for your language settings and downloads it.
3. Captions are cleaned (HTML entities, formatting tags and line breaks removed) and returned in the formats you chose.
4. If YouTube rate-limits a request, the actor retries with a new residential IP and a different app client.

The actor does not download audio or run speech-to-text. If a video has no captions at all (neither human-made nor auto-generated), you get a free item with `errorCode: "noCaptions"`.

### How do I choose which YouTube videos to get transcripts for?

| Field                   | Description                                                                                                  | Default                |
| ----------------------- | ------------------------------------------------------------------------------------------------------------ | ---------------------- |
| `urls`                  | Video URLs or IDs, channel URLs (`@handle`, `/channel/UC…`, `/videos`, `/shorts`, `/streams`), playlist URLs | two sample videos      |
| `maxVideosPerSource`    | Newest videos to take per channel or playlist                                                                | `20`                   |
| `languages`             | Preferred caption languages, in order                                                                        | `["en"]`               |
| `allowAutoGenerated`    | Use auto-generated captions when no human-made track matches                                                 | `true`                 |
| `fallbackToAnyLanguage` | If no preferred language exists, return the default track instead of an error                                | `true`                 |
| `translateTo`           | Language code to machine-translate into (YouTube's translation), e.g. `en`                                   | none                   |
| `formats`               | Any of `text`, `segments`, `srt`, `vtt`                                                                      | `["text", "segments"]` |
| `includeMetadata`       | Add channel, publish date, duration, views, description and more                                             | `true`                 |
| `proxyConfiguration`    | Proxy settings                                                                                               | Apify residential      |

Example: the 50 newest long-form videos of a channel, English (or translated to English), text and SRT:

```json
{
  "urls": ["https://www.youtube.com/@TED/videos"],
  "maxVideosPerSource": 50,
  "languages": ["en"],
  "translateTo": "en",
  "formats": ["text", "srt"]
}
```

### What data do you get for each YouTube transcript?

One dataset item per video (shortened):

```json
{
  "videoId": "dQw4w9WgXcQ",
  "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
  "title": "Rick Astley - Never Gonna Give You Up (Official Video) (4K Remaster)",
  "channelName": "Rick Astley",
  "channelId": "UCuAXFkgsw1L7xaCfnd5JJOw",
  "channelUrl": "http://www.youtube.com/@RickAstleyYT",
  "publishDate": "2009-10-24T23:57:33-07:00",
  "durationSec": 213,
  "viewCount": 1821336715,
  "likeCount": 19430714,
  "category": "Music",
  "keywords": ["rick astley", "Never Gonna Give You Up", "..."],
  "description": "The official video for “Never Gonna Give You Up” by Rick Astley. ...",
  "thumbnailUrl": "https://i.ytimg.com/vi/dQw4w9WgXcQ/sddefault.jpg",
  "language": "en",
  "languageName": "English",
  "isAutoGenerated": false,
  "isTranslated": false,
  "availableLanguages": [
    { "code": "en", "name": "English", "isAutoGenerated": false },
    {
      "code": "en",
      "name": "English (auto-generated)",
      "isAutoGenerated": true
    },
    { "code": "de-DE", "name": "German (Germany)", "isAutoGenerated": false }
  ],
  "transcript": "[♪♪♪] ♪ We're no strangers to love ♪ ♪ You know the rules and so do I ♪ ...",
  "wordCount": 487,
  "segments": [
    { "start": 1.36, "duration": 1.68, "text": "[♪♪♪]" },
    {
      "start": 18.64,
      "duration": 3.24,
      "text": "♪ We're no strangers to love ♪"
    }
  ],
  "source": "https://youtu.be/dQw4w9WgXcQ",
  "error": null,
  "errorCode": null
}
```

With `formats` including `srt` or `vtt`, the item also has an `srt` or `vtt` string, ready to save as a subtitle file.

Videos without a transcript are still returned, **free of charge**, with `transcript: null` and an explanation:

| `errorCode`               | Meaning                                                                                 |
| ------------------------- | --------------------------------------------------------------------------------------- |
| `noCaptions`              | The video has no captions of any kind                                                   |
| `languageNotAvailable`    | No track in your languages and fallback is off (`availableLanguages` lists what exists) |
| `translationNotAvailable` | YouTube doesn't offer translation for this track                                        |
| `videoUnavailable`        | Deleted, wrong ID or region-blocked                                                     |
| `privateVideo`            | Private video                                                                           |
| `loginRequired`           | Age-restricted or members-only                                                          |
| `blocked`                 | YouTube kept refusing the request after several retries on new IPs                      |
| `requestFailed`           | Network or unexpected error                                                             |

### How much does it cost to download YouTube transcripts?

Pay per event, no subscription:

| Event                                    | Price                         |
| ---------------------------------------- | ----------------------------- |
| Transcript (one video with a transcript) | **$0.0045** ($4.50 per 1,000) |
| Actor start                              | $0.00005                      |

Proxy and compute costs are included. Videos that return an error are not charged. If you set a **maximum cost per run**, the actor stops cleanly when it is reached.

### YouTube transcripts FAQ

**Does it work for auto-generated captions?** Yes. Auto-generated (speech recognition) tracks are used when no human-made captions exist in your languages. `isAutoGenerated` tells you which one you got.

**Can I get a transcript in a language the video doesn't have?** Set `translateTo`. If the video has captions in that language they're used as-is; otherwise YouTube's own machine translation is returned and `isTranslated` is `true`.

**What about videos without any captions?** YouTube has no transcript to give, so you get a free item with `errorCode: "noCaptions"`. This actor does not do speech-to-text.

**How many videos can I process?** There is no fixed limit. Large channels are paginated; set `maxVideosPerSource` to how many of the newest videos you need.

**Why residential proxies?** YouTube often answers requests from cloud servers with "Sign in to confirm you're not a bot". Residential IPs avoid most of that. You can switch to your own proxy or no proxy in the Advanced section, but expect more `blocked` items without one.

**Private, age-restricted or members-only videos?** These need a signed-in account, which this actor does not use, so they return an error item and are not charged.

**Can AI agents use it?** Yes, through the Apify API, the Apify MCP server or any Apify integration (Make, Zapier, n8n, LangChain, LlamaIndex).

**Is this legal?** The actor only reads publicly available captions, the same data YouTube shows in its "Show transcript" panel. Transcripts are the creators' content: respect copyright and YouTube's Terms of Service in how you use them.

**Disclaimer:** This actor is not affiliated with, endorsed by or sponsored by YouTube or Google.

### Related actors

- [substack-scraper](https://apify.com/cprussin/substack-scraper): Substack newsletter posts, content and public stats.
- [bilibili-scraper](https://apify.com/cprussin/bilibili-scraper): Bilibili videos, comments, trending and search results.
- [telegram-channel-scraper](https://apify.com/cprussin/telegram-channel-scraper): Posts, views and reactions from public Telegram channels.

# Actor input Schema

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

Video URLs (watch, youtu.be, Shorts, live, embed) or 11-character video IDs. Channel URLs (@handle, /channel/UC…, add /videos, /shorts or /streams to pick a tab) and playlist URLs are expanded into their videos.

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

How many of the newest videos to take from each channel or playlist URL. Individual video URLs are always included.

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

Caption languages in order of preference, as ISO codes (en, es, pt-BR, …). For each language, human-made captions are preferred over auto-generated ones.

## `allowAutoGenerated` (type: `boolean`):

Use YouTube's automatic (speech recognition) captions when no human-made captions exist in a preferred language.

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

If no preferred language exists, return the video's default caption track instead of an error.

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

Optional language code (e.g. en, es, de). If the video has no captions in this language, YouTube's machine translation of the best track is returned.

## `formats` (type: `array`):

What to include for each video: plain text (`transcript`), timestamped `segments`, and/or ready-to-use `srt` / `vtt` subtitle files.

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

Add channel, publish date, duration, views, likes, category, keywords, description and thumbnail to each item.

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

YouTube blocks many datacenter IPs, so residential proxies are the default and recommended. Proxy costs are included in the price.

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

Videos fetched in parallel.

## Actor input object example

```json
{
  "urls": [
    "https://www.youtube.com/watch?v=UF8uR6Z6KLc",
    "https://youtu.be/dQw4w9WgXcQ"
  ],
  "maxVideosPerSource": 20,
  "languages": [
    "en"
  ],
  "allowAutoGenerated": true,
  "fallbackToAnyLanguage": true,
  "formats": [
    "text",
    "segments"
  ],
  "includeMetadata": true,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  },
  "maxConcurrency": 5
}
```

# Actor output Schema

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

No description

# 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=UF8uR6Z6KLc",
        "https://youtu.be/dQw4w9WgXcQ"
    ],
    "maxVideosPerSource": 20,
    "languages": [
        "en"
    ],
    "formats": [
        "text",
        "segments"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("cprussin/youtube-transcripts").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=UF8uR6Z6KLc",
        "https://youtu.be/dQw4w9WgXcQ",
    ],
    "maxVideosPerSource": 20,
    "languages": ["en"],
    "formats": [
        "text",
        "segments",
    ],
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("cprussin/youtube-transcripts").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=UF8uR6Z6KLc",
    "https://youtu.be/dQw4w9WgXcQ"
  ],
  "maxVideosPerSource": 20,
  "languages": [
    "en"
  ],
  "formats": [
    "text",
    "segments"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call cprussin/youtube-transcripts --silent --output-dataset

```

## MCP server setup

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

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/VNPSIDCRf9F0iY2pD/builds/3VPGamnWvSwvEptgb/openapi.json
