# YouTube Transcript Scraper (`exciting_pawpaw/youtube-transcript-scraper`) Actor

Extract YouTube transcripts & captions from videos, Shorts, channels and playlists. Plain text, timestamped segments, SRT or VTT, language choice, auto-translation and video metadata. Pay only for successful transcripts; failed videos are free.

- **URL**: https://apify.com/exciting_pawpaw/youtube-transcript-scraper.md
- **Developed by:** [Markus Essl](https://apify.com/exciting_pawpaw) (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

Pay per event

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

Get the full transcript (captions/subtitles) of any public YouTube video, Short, channel or playlist as plain text, timestamped segments, SRT or WebVTT, together with the video's title, channel, publish date, duration and view count. You only pay for transcripts that were actually delivered. Videos with no captions are reported as free error records.

### What data can you get

| Field | Example | Notes |
|---|---|---|
| `transcriptText` | "Good morning. How are you? ..." | Full transcript as one string |
| `segments` | `[{"start": 27.1, "duration": 2.58, "text": "Good morning. How are you?"}]` | Timestamped lines, in seconds |
| `srt` / `vtt` | `1\n00:00:27,103 --> 00:00:29,678\n...` | Ready-to-use subtitle files (optional) |
| `transcriptLanguage` | `en` | Language of the returned transcript |
| `isAutoGenerated` | `false` | `true` = YouTube speech recognition, `false` = captions provided by the uploader |
| `isTranslated`, `translatedFrom` | `true`, `ko` | Set when you ask for automatic translation |
| `videoLanguage` | `fr-FR` | Original spoken language when YouTube exposes it |
| `availableLanguages` | `[{"languageCode": "de", "isAutoGenerated": false}, ...]` | Every caption track available on the video |
| `title`, `description`, `keywords`, `category` | "Do schools kill creativity?" | Video metadata |
| `channelName`, `channelId`, `channelHandle`, `channelUrl` | TED, `@TED` | Channel info |
| `publishedAt`, `durationSeconds`, `viewCount` | `2007-01-07T05:00:34Z`, `1203`, `25027894` | Publish date in ISO 8601 (UTC) |
| `wordCount`, `segmentCount` | `3170`, `427` | Quick size stats |
| `status`, `errorCode`, `errorMessage` | `error`, `NO_TRANSCRIPT` | Every video gets a record, including failures (free) |

### How to use

1. Paste YouTube links into **YouTube URLs or video IDs**. You can mix videos (`watch?v=`, `youtu.be/`, `/shorts/`, `/live/`), bare 11-character video IDs, channels (`@handle`, `/channel/UC…`, plus `/videos`, `/shorts` or `/streams` for a specific tab) and playlists.
2. Optionally pick preferred languages, an auto-translation target, and the output formats you want.
3. Click **Start**, then download the results as JSON, CSV or Excel, or read them through the API.

#### Input example

```json
{
  "startUrls": [
    "https://www.youtube.com/watch?v=iG9CE55wbtY",
    "https://youtu.be/9bZkp7q19f0",
    "https://www.youtube.com/@veritasium/videos",
    "https://www.youtube.com/playlist?list=PLrAXtmErZgOeiKm4sgNOknGvNjby9efdf"
  ],
  "languages": ["en"],
  "translateTo": "",
  "outputFormats": ["text", "segments", "srt"],
  "maxVideosPerSource": 20,
  "maxItems": 100
}
```

#### Output example

Real output from a test run (long fields shortened):

```json
{
  "status": "success",
  "videoId": "iG9CE55wbtY",
  "url": "https://www.youtube.com/watch?v=iG9CE55wbtY",
  "title": "Do schools kill creativity? | Sir Ken Robinson | TED",
  "channelName": "TED",
  "channelId": "UCAuUUnT6oDeKwE6v1NGQxug",
  "channelHandle": "@TED",
  "channelUrl": "https://www.youtube.com/channel/UCAuUUnT6oDeKwE6v1NGQxug",
  "publishedAt": "2007-01-07T05:00:34Z",
  "durationSeconds": 1203,
  "viewCount": 25027894,
  "category": "Education",
  "transcriptLanguage": "en",
  "transcriptLanguageName": "English",
  "isAutoGenerated": false,
  "isTranslated": false,
  "videoLanguage": "en",
  "segmentCount": 427,
  "wordCount": 3170,
  "transcriptText": "Good morning. How are you? (Audience) Good. It's been great, hasn't it? I've been blown away by the whole thing. In fact, I'm leaving. (Laughter) ...",
  "segments": [
    { "start": 27.103, "duration": 2.575, "text": "Good morning. How are you?" },
    { "start": 29.702, "duration": 1.403, "text": "(Audience) Good." },
    { "start": 31.129, "duration": 1.668, "text": "It's been great, hasn't it?" }
  ],
  "srt": "1\n00:00:27,103 --> 00:00:29,678\nGood morning. How are you?\n\n2\n...",
  "inputUrl": "https://www.youtube.com/watch?v=iG9CE55wbtY",
  "scrapedAt": "2026-10-09T14:01:43Z"
}
```

A video without captions gets a free error record:

```json
{
  "status": "error",
  "videoId": "l9McIeQxwqY",
  "url": "https://www.youtube.com/watch?v=l9McIeQxwqY",
  "errorCode": "NO_TRANSCRIPT",
  "errorMessage": "This video has no captions/transcript (none uploaded and none auto-generated).",
  "scrapedAt": "2026-10-09T14:01:50Z"
}
```

### Pricing

**$4.00 per 1,000 transcripts ($0.004 per video).** You pay only for transcripts that were delivered. Error records cost nothing: videos with no captions, private, deleted, members-only or age-restricted videos, and languages that aren't available. Listing videos from channels and playlists is also free. Set a maximum charge per run in the run options if you want a hard spending cap.

### Language and translation options

- **Preferred transcript languages**: a priority list such as `["en", "es"]`. `en` also matches `en-US`, `en-GB` and other regional variants.
- **Default (empty list)**: the transcript comes in the video's original spoken language. Auto-dubbed videos are handled correctly, so a French video with an English AI dub still returns the French transcript.
- **Fall back to original language**: when none of your languages exists, you get the original-language transcript instead of an error. Turn this off to get a free `LANGUAGE_NOT_AVAILABLE` record instead.
- **Auto-translate to language**: if the video has a native track in that language, the native track is used. Otherwise the transcript is machine-translated (Google Translate) and the timestamps are kept. `isTranslated` and `translatedFrom` are set on those records.
- **Prefer human-made captions**: when the uploader provided captions, they are used before YouTube's auto-generated speech recognition.

### Use cases

- **AI and LLM pipelines**: feed transcripts to summarizers, RAG indexes, chatbots or AI agents. The Actor works with the [Apify MCP server](https://mcp.apify.com), so agents such as Claude or ChatGPT can call it as a tool.
- **Content repurposing**: turn videos into blog posts, newsletters, show notes or social posts.
- **Research and monitoring**: analyze what channels, competitors, politicians or news outlets say, and search across thousands of videos.
- **Subtitles and accessibility**: download SRT or VTT files for editing, translation or re-upload.
- **SEO and marketing**: extract keywords and topics from top-ranking videos in your niche.
- **Education**: build study notes and searchable archives of lectures and talks.

### FAQ

**Is it legal to scrape YouTube transcripts?**
This Actor reads only publicly available data: the same captions anyone can see under a public video. It does not log in, use cookies or touch private data. You are responsible for how you use the content, for example copyright and YouTube's terms. When in doubt, consult a lawyer.

**Why did a video return `NO_TRANSCRIPT`?**
The uploader didn't add captions, and YouTube didn't auto-generate any (common for music-only videos, live streams, very new uploads and some Shorts). The Actor checks three times from different connections before reporting this, so the record is reliable. It's free.

**Which videos can't be processed?**
Private, deleted, members-only and age-restricted videos (those need a signed-in account), and premieres or live streams that haven't started yet. Each one gets a clear `errorCode` and costs nothing.

**How many videos does a channel return?**
The newest `maxVideosPerSource` uploads (default 20). Add `/videos`, `/shorts` or `/streams` to the channel URL to limit the results to that tab. `maxItems` caps the whole run.

**How accurate are auto-generated transcripts?**
They come from YouTube's own speech recognition. Quality is usually good for clear speech and weaker for music, heavy accents or crosstalk. The `isAutoGenerated` field tells you which kind each record contains.

**Error codes**: `NO_TRANSCRIPT`, `LANGUAGE_NOT_AVAILABLE`, `TRANSLATION_FAILED`, `VIDEO_UNAVAILABLE`, `VIDEO_PRIVATE`, `AGE_RESTRICTED`, `MEMBERS_ONLY`, `LIVE_NOT_STARTED`, `VIDEO_UNPLAYABLE`, `CHANNEL_NOT_FOUND`, `PLAYLIST_NOT_FOUND`, `NO_VIDEOS_FOUND`, `INVALID_INPUT`, `BLOCKED` (temporary; please retry).

# Actor input Schema

## `startUrls` (type: `array`):

Video URLs (watch, youtu.be, shorts, live, embed), bare 11-character video IDs, channel URLs (@handle, /channel/UC..., /c/..., /user/...; add /videos, /shorts or /streams to pick a tab) or playlist URLs. Channels and playlists are expanded to their videos (newest first for channels).

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

Language codes in order of preference, e.g. \["en", "de"]. "en" also matches regional variants such as en-US/en-GB. Leave empty to get the transcript in the video's original language.

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

If none of the preferred languages is available, return the transcript in the video's original language instead of an error record.

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

Optional language code (e.g. "en", "es", "ja"). If the video has no native transcript in that language, YouTube's machine translation of the selected transcript is returned.

## `preferManualCaptions` (type: `boolean`):

When both uploader-provided and auto-generated captions exist in the chosen language, use the uploader-provided ones.

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

Which transcript representations to include in each result: plain text, timestamped segments, SRT and/or WebVTT subtitles.

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

How many of the newest videos to take from each channel or playlist URL.

## `maxItems` (type: `integer`):

Upper limit on the number of videos processed in this run.

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

How many videos to process in parallel.

## Actor input object example

```json
{
  "startUrls": [
    "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
    "https://youtu.be/kJQP7kiw5Fk"
  ],
  "languages": [],
  "fallbackToAnyLanguage": true,
  "translateTo": "",
  "preferManualCaptions": true,
  "outputFormats": [
    "text",
    "segments"
  ],
  "maxVideosPerSource": 20,
  "maxItems": 100,
  "maxConcurrency": 8
}
```

# 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 = {
    "startUrls": [
        "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
        "https://youtu.be/kJQP7kiw5Fk"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("exciting_pawpaw/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 = { "startUrls": [
        "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
        "https://youtu.be/kJQP7kiw5Fk",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("exciting_pawpaw/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 '{
  "startUrls": [
    "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
    "https://youtu.be/kJQP7kiw5Fk"
  ]
}' |
apify call exciting_pawpaw/youtube-transcript-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,exciting_pawpaw/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/QRX84JwuYKO8luTTe/builds/fRimAYAsScubPAdsa/openapi.json
