# YouTube Transcript Scraper - Subtitles to Text, Pay per Video (`leekung125/youtube-transcript-scraper`) Actor

Transcripts and subtitles for any list of YouTube videos: full text, timestamped segments, language fallback, translation, title and channel. No browser, residential proxy rotation on blocks, charged only for videos that return a transcript.

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

## Pricing

$5.00 / 1,000 transcript delivereds

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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## YouTube Transcript Scraper — subtitles to text, one row per video, pay only for transcripts you get

Turn any list of YouTube videos into **clean transcript text** with **timestamped segments**, the **language**
used, whether it was **auto-generated**, plus **title and channel**. Give it watch URLs, Shorts, youtu.be
links or bare video IDs. Get JSON, CSV or Excel back, or call it from the API, a schedule, an integration or an AI agent.

- **Charged per transcript delivered.** A video with captions disabled, a private video, or a block that
  survives every retry is reported as a row with a `status`, and costs nothing.
- **No browser.** Plain HTTP against YouTube's caption endpoints, so a video takes about a second.
- **Blocks are handled.** YouTube blocks datacenter IPs for transcripts; this Actor rotates residential
  proxy sessions and retries a fixed number of times, then reports `blocked` instead of looping.
- **Language fallback that makes sense.** Your preferred languages first, manually created captions
  before auto-generated ones, optional translation into any language YouTube supports.

### Output

One row per video:

| field | meaning |
|---|---|
| `status` | `ok`, `no_transcript`, `captions_disabled`, `unavailable`, `blocked`, `error` |
| `text` | the full transcript as one string |
| `segments[]` | `start`, `duration`, `text` for every caption line (optional) |
| `language`, `source_language`, `is_generated` | what you got and where it came from |
| `available_languages[]` | every transcript YouTube offers for the video |
| `word_count`, `segment_count`, `duration_seconds` | size of the transcript |
| `title`, `channel`, `channel_url`, `thumbnail` | public metadata (optional) |
| `video_id`, `url` | the video |

Sample row (segments shortened):

```json
{"video_id":"dQw4w9WgXcQ","url":"https://www.youtube.com/watch?v=dQw4w9WgXcQ","status":"ok","language":"en","source_language":"en","is_generated":false,"segment_count":61,"duration_seconds":211.6,"word_count":372,"text":"[♪♪♪] We're no strangers to love ...","segments":[{"start":18.8,"duration":3.6,"text":"We're no strangers to love"}],"title":"Rick Astley - Never Gonna Give You Up","channel":"Rick Astley"}
```

### Input

| field | default | notes |
|---|---|---|
| `videoUrls` | — | URLs or 11-character IDs, one per line |
| `languages` | `["en"]` | preference order, e.g. `["de", "en"]` |
| `allowAutoGenerated` | `true` | fall back to YouTube's automatic captions |
| `translateTo` | `""` | e.g. `en` to translate a Spanish transcript |
| `includeSegments` | `true` | timestamped lines next to the full text |
| `includeMetadata` | `true` | title, channel, thumbnail |
| `concurrency` | `5` | videos in parallel (1–20) |
| `maxRetries` | `4` | proxy sessions to try per video when blocked |
| `proxyConfiguration` | Apify residential | recommended; datacenter IPs get blocked |

Minimal input:

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

### Pricing

Pay per transcript delivered (see the Pricing tab). Rows with any status other than `ok` are free.

### Use cases

Summaries and notes from lectures or podcasts · feeding videos into an LLM or RAG pipeline · subtitles for
translation · keyword and topic mining across a channel · quote finding with timestamps · accessibility text.

### Honesty notes and limits

- Transcripts are what YouTube provides: manually uploaded captions when they exist, otherwise automatic
  captions, whose accuracy depends on audio quality. `is_generated` tells you which you got.
- Live streams without captions, members-only and age-restricted videos may have no transcript.
- Public data only. No login, no personal data collected.
- If YouTube blocks every retry for a video, you get a `blocked` row and no charge; run again later or raise `maxRetries`.

### Support

Open an issue on the Issues tab. Runs that fail say why, per video, in the `error` field.

# Actor input Schema

## `videoUrls` (type: `array`):

YouTube video URLs (watch, shorts, youtu.be, embed) or bare 11-character video IDs, one per line.

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

Language codes in order of preference, e.g. en, en-US, de, es. The first available one wins; manually created captions are preferred over auto-generated.

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

If no manually created transcript exists in your languages, use YouTube's auto-generated captions (and, as a last resort, any available transcript).

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

Optional language code to translate the transcript into (uses YouTube's own translation, e.g. en, de, fr, es, ja). Leave empty for the original language.

## `includeSegments` (type: `boolean`):

Add a segments array (start, duration, text) next to the full text.

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

Add title, channel name, channel URL and thumbnail (public oEmbed data).

## `concurrency` (type: `integer`):

Videos processed in parallel (1-20).

## `maxRetries` (type: `integer`):

Fresh proxy sessions to try when YouTube blocks a request. A video that fails every attempt is reported as blocked and not charged.

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

YouTube blocks datacenter IPs for transcript requests. Apify residential proxies are the default and recommended.

## Actor input object example

```json
{
  "videoUrls": [
    "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
  ],
  "languages": [
    "en"
  ],
  "allowAutoGenerated": true,
  "translateTo": "",
  "includeSegments": true,
  "includeMetadata": true,
  "concurrency": 5,
  "maxRetries": 4,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

## `dataset` (type: `string`):

No description

## `summary` (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 = {
    "videoUrls": [
        "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
    ],
    "languages": [
        "en"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("leekung125/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 = {
    "videoUrls": ["https://www.youtube.com/watch?v=dQw4w9WgXcQ"],
    "languages": ["en"],
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

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

```

## MCP server setup

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