# YouTube Transcript Scraper (Bulk Channels & Videos) (`t0b0t/youtube-transcript-scraper`) Actor

Fast bulk YouTube transcript scraper. Download captions from channels, playlists, Shorts, or URLs into Clean Text, SRT, VTT, and RAG chunks.

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

## Pricing

from $6.00 / 1,000 result 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

<p align="center">
  <img src="logo.jpg" alt="YouTube Transcript Scraper" width="130" style="border-radius: 20px;" />
</p>

## YouTube Transcript & Subtitle Scraper (Bulk Channels, Playlists & Videos)

[![Apify Actor](https://img.shields.io/badge/Apify-Actor-orange?logo=apify)](https://apify.com/t0b0t/youtube-transcript-scraper)
[![Pay Per Event](https://img.shields.io/badge/Pricing-Pay--Per--Event-blue)](https://apify.com/t0b0t/youtube-transcript-scraper)
[![Status](https://img.shields.io/badge/Status-Active-brightgreen)](https://apify.com/t0b0t/youtube-transcript-scraper)

A fast, production-grade tool to **download YouTube transcripts, captions, and subtitles in bulk**. Extract data from individual video URLs, YouTube Shorts, full playlists, or entire channel back-catalogues (`@handle` or channel ID).

Exports directly to **Plain Text, SubRip (`.srt`), WebVTT (`.vtt`), JSON timestamps**, and **character-bounded RAG chunks** ready for vector databases and LLMs.

***

### ⚡ Why Use This Scraper?

| Feature | **This Scraper** | **Pintostudio** *(Top Competitor)* | **Official YouTube API v3** |
|---|---|---|---|
| **Input Flexibility** | **Bulk URLs, Shorts, Channels (`@handle`), Playlists** | Single video URL only (1 video per run) | Complex OAuth & Video IDs |
| **Export Formats** | **5 Formats** (`plaintext`, `srt`, `vtt`, `chunks`, `timestamped`) | 1 format (raw JSON snippet array) | Raw XML timedtext |
| **AI Text Formatting** | **Built-in** (Punctuation, casing, paragraphs) | ❌ Raw unpunctuated text only | ❌ None |
| **RAG & Vector Search Ready** | **Built-in overlapping timestamped chunks** | ❌ Requires manual preprocessing | ❌ None |
| **Pricing (per video)** | **$0.006** *(Undercuts competitors)* | $0.007 – $0.010 | Strict 10,000 quota units / day |
| **Failed / Deleted Videos** | **100% FREE ($0.00 charge)** | Charged full price | Consumes API quota |
| **Proxy Architecture** | **Sticky Residential Proxy Pool** (Zero 429 IP blocks) | Datacenter proxies (frequent blocks) | Subject to IP limits |

***

### 🚀 Key Capabilities

- **Bulk Channel & Playlist Scraping**: Enter a single channel handle like `@hubermanlab` or a playlist URL to process dozens or hundreds of videos in one automated run.
- **5 Clean Export Formats**:
  - `plaintext`: Continuous, readable text stream ideal for LLM prompts, summarization, and NLP.
  - `timestamped`: Precision cue array with millisecond start times and durations.
  - `chunks`: Overlapping character-bounded segments with `startTime` and `endTime` for RAG vector databases.
  - `srt`: Industry-standard SubRip subtitle file for Premiere, DaVinci Resolve, Final Cut, and VLC.
  - `vtt`: WebVTT format for HTML5 `<track>` tags and modern web players.
- **Smart Formatting Pass (`cleanTranscript`)**: Fixes auto-generated captions by inserting proper capitalization, punctuation, and natural paragraph breaks without removing spoken words.
- **Date & Language Filters**: Pull only recent videos (`publishedAfter` / `publishedBefore`) and automatically translate captions into any target language (e.g. `es`, `de`, `fr`, `ja`).
- **Fair Billing Guarantee**: You are billed strictly for delivered transcripts. If a video is private, deleted, or captions are disabled, it is saved to your dataset with zero charge.

***

### 📖 How to Scrape YouTube Transcripts in 3 Steps

1. **Add Your Sources**: Paste video links, Shorts URLs, playlist links, or YouTube channel handles (`@channelname`) into the `startUrls` field.
2. **Choose Output Formats**: Select which formats you need (`plaintext`, `timestamped`, `chunks`, `srt`, `vtt`). Optionally toggle `cleanTranscript` for readable punctuated text.
3. **Run & Export**: Click **Start**. Once finished, download your transcripts in **JSON, CSV, Excel, or XML**, or sync directly to your webhook, Zapier, Make, or AWS S3.

***

### 📥 Supported Input Formats

You can mix and match any of these formats inside `startUrls`:

| Target Type | Example Input | What It Extracts |
|---|---|---|
| **Channel Handle** | `@lexfridman` or `https://www.youtube.com/@lexfridman` | All videos from the channel |
| **Channel ID** | `UC_x5XG1OV2P6uZZ5FSM9Ttw` | All videos from the channel |
| **Playlist** | `https://www.youtube.com/playlist?list=PLrAXtmErZgOdP_8GztsuKi9nrraNbKKp4` | Every video in the playlist |
| **Standard Video** | `https://www.youtube.com/watch?v=dQw4w9WgXcQ` | Individual video transcript |
| **YouTube Shorts** | `https://www.youtube.com/shorts/dQw4w9WgXcQ` | Individual Shorts transcript |
| **Short URL** | `https://youtu.be/dQw4w9WgXcQ` | Individual video transcript |
| **Bare Video ID** | `dQw4w9WgXcQ` | Individual video transcript |

***

### ⚙️ Input Configuration Reference

| Option | Type | Default | Description |
|---|---|---|---|
| `startUrls` | Array | *Required* | List of YouTube video URLs, channel handles (`@name`), channel IDs, or playlists. |
| `maxVideos` | Integer | `50` | Maximum number of videos to scrape across all inputs. Set `0` for unlimited. |
| `outputFormats` | Array | `["timestamped", "plaintext"]` | Formats to include: `timestamped`, `plaintext`, `chunks`, `srt`, `vtt`. |
| `languages` | Array | `["en"]` | Priority order of caption languages to search for (e.g. `["en", "es"]`). |
| `preferManuallyCreated` | Boolean | `true` | Prefer creator-uploaded subtitles over automatic speech recognition (ASR). |
| `translateTo` | String | `null` | Target language code (e.g. `es`, `de`, `fr`) to translate transcripts into. |
| `publishedAfter` | String | `null` | Filter channel uploads: only include videos on or after `YYYY-MM-DD`. |
| `publishedBefore` | String | `null` | Filter channel uploads: only include videos on or before `YYYY-MM-DD`. |
| `cleanTranscript` | Boolean | `false` | Optional AI formatting pass to add punctuation, casing, and paragraphs (`textCleaned`). |
| `includeVideoDetails` | Boolean | `false` | Include view count, like count, upload date, description, tags, and category. |
| `maxConcurrency` | Integer | `5` | Simultaneous videos processed (1 to 25). |
| `proxyConfiguration` | Object | Residential | Apify Residential proxies enabled by default to prevent IP rate limits. |

***

### 📊 Sample Output

Every processed video generates a structured record in the dataset:

```json
{
  "videoId": "dQw4w9WgXcQ",
  "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
  "title": "Rick Astley - Never Gonna Give You Up (Official Music Video)",
  "channel": "Rick Astley",
  "channelId": "UCuAXFkgsw1L7xaCfnd5JJOw",
  "channelUrl": "https://www.youtube.com/@RickAstleyYT",
  "duration": 213,
  "status": "ok",
  "hasTranscript": true,
  "language": "English",
  "languageCode": "en",
  "isGenerated": false,
  "isTranslated": false,
  "availableTranscripts": [
    { "language": "English", "languageCode": "en", "isGenerated": false },
    { "language": "Spanish", "languageCode": "es", "isGenerated": false }
  ],
  "snippetCount": 61,
  "wordCount": 487,
  "charCount": 2610,
  "transcript": [
    { "text": "We're no strangers to love", "start": 18.64, "duration": 3.6 },
    { "text": "You know the rules and so do I", "start": 22.84, "duration": 4.1 }
  ],
  "text": "We're no strangers to love You know the rules and so do I ...",
  "chunks": [
    {
      "index": 0,
      "text": "We're no strangers to love You know the rules and so do I ...",
      "startTime": 18.64,
      "endTime": 65.2,
      "charCount": 1180
    }
  ],
  "srt": "1\n00:00:18,640 --> 00:00:22,240\nWe're no strangers to love\n\n2\n00:00:22,840 --> 00:00:26,940\nYou know the rules and so do I\n",
  "vtt": "WEBVTT\n\n00:00:18.640 --> 00:00:22.240\nWe're no strangers to love\n\n00:00:22.840 --> 00:00:26.940\nYou know the rules and so do I\n",
  "textCleaned": "We're no strangers to love. You know the rules, and so do I...",
  "publishedAt": "2009-10-25",
  "error": null
}
```

***

### 💰 Transparent Pay-Per-Event (PPE) Pricing

You only pay for successful results. There are **no hidden subscription fees**:

| Event | Rate | When Charged |
|---|---|---|
| **`video-transcribed`** | **$0.006** / video | Charged only when a full transcript is delivered. |
| **`video-metadata-only`** | **$0.002** / video | Video exists and metadata resolved, but captions are disabled. |
| **`transcript-cleaned`** | **$0.010** / 1k words | Optional AI formatting pass (adds punctuation & paragraphs). |
| **Unavailable / Private / Deleted** | **$0.00 (FREE)** | Zero charge. Detailed status saved to dataset. |

#### Pricing Examples:

- **100 YouTube Videos (Transcripts only)**: `100 × $0.006` = **$0.60**
- **10-Minute Video with AI Formatting**: `$0.006` (base) + `2 × $0.010` (cleanup) = **$0.026 (2.6 cents)**
- **50 Videos with 10 Deleted**: You are only billed for the 40 successful ones (**$0.24**). The 10 deleted videos cost **$0.00**.

***

### 💻 Developer API & Integrations

#### Python (`apify-client`)

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")

## Run scraper on a channel handle
run = client.actor("t0b0t/youtube-transcript-scraper").call(
    run_input={
        "startUrls": ["https://www.youtube.com/@hubermanlab"],
        "maxVideos": 25,
        "outputFormats": ["plaintext", "chunks", "srt"],
        "cleanTranscript": True,
    }
)

## Fetch dataset items
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    if item["hasTranscript"]:
        print(f"[{item['videoId']}] {item['title']}")
        print(item["textCleaned"][:250] + "...\n")
```

#### JavaScript / Node.js

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

const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });

const run = await client.actor('t0b0t/youtube-transcript-scraper').call({
    startUrls: ['https://www.youtube.com/playlist?list=PLrAXtmErZgOdP_8GztsuKi9nrraNbKKp4'],
    maxVideos: 50,
    outputFormats: ['plaintext', 'vtt'],
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(`Extracted ${items.length} transcripts successfully!`);
```

#### cURL

```bash
curl -X POST "https://api.apify.com/v2/acts/t0b0t~youtube-transcript-scraper/runs?token=YOUR_APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "startUrls": ["https://www.youtube.com/@Apify"],
    "maxVideos": 10,
    "outputFormats": ["plaintext", "srt"]
  }'
```

***

### 🤖 RAG & Vector Search Integration (LangChain & LlamaIndex)

The `chunks` output format is designed specifically for Large Language Model retrieval pipelines:

- **Timestamp Citations**: Each chunk contains `startTime` and `endTime`, so your AI chatbot can link directly to the exact second in the video (`https://youtu.be/<id>?t=<startTime>`).
- **Sliding Overlap Window**: Balanced 1,200-character segments with 150-character overlaps ensure that thoughts and sentences spanning boundaries are never cut mid-idea.

***

### ❓ Frequently Asked Questions (FAQ)

#### How do I scrape an entire YouTube channel?

Simply paste the channel handle (e.g. `@Veritasium` or `https://www.youtube.com/@Veritasium`) into the `startUrls` array. The scraper automatically resolves the channel and crawls all available video uploads.

#### Can I extract transcripts from YouTube Shorts?

Yes! Standard URLs (`youtube.com/watch?v=...`), share links (`youtu.be/...`), and Shorts (`youtube.com/shorts/...`) are all recognized and processed seamlessly.

#### How does translation work?

Set the `translateTo` field to your desired two-letter ISO language code (e.g. `es`, `de`, `fr`, `ja`, `zh`). YouTube's automated translation engine translates the subtitle stream directly before export.

#### Why do other scrapers get blocked by YouTube?

YouTube enforces aggressive IP rate-limiting on its `timedtext` caption endpoints, immediately blocking standard datacenter IPs with HTTP 429 errors. This Actor runs an intelligent **sticky residential proxy pool** with automatic error retirement, ensuring 99%+ delivery reliability.

#### Is scraping YouTube transcripts legal?

Yes. Transcripts and subtitles published on YouTube are publicly accessible information. This scraper operates in full compliance with public web scraping legal precedents by extracting publicly available caption tracks without bypassing paywalls or authentication.

***

### 💬 Support & Feature Requests

Have questions, need custom features, or want to report an issue? Check the **Issues** tab on the Actor page or reach out via Apify Console. We respond quickly!

# Actor input Schema

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

Video URLs, Shorts URLs, playlist URLs, channel URLs, bare 11-character video IDs, channel handles (@handle), or playlist IDs (PL...). Channels and playlists are expanded automatically up to 'Max videos' (set 0 for all).

## `maxVideos` (type: `integer`):

Hard cap on how many videos are processed in total, across all inputs. Protects you from accidentally expanding a 5,000-video channel. Set 0 for no limit.

## `publishedAfter` (type: `string`):

Only keep videos published on or after this date (YYYY-MM-DD). Requires one extra metadata request per candidate video, so it is slower and uses more proxy traffic. Leave empty to disable.

## `publishedBefore` (type: `string`):

Only keep videos published on or before this date (YYYY-MM-DD). Same cost note as "Published after".

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

Language codes in priority order, e.g. en, de, es. The first available one wins. If none match, the actor falls back to any available transcript rather than failing.

## `preferManuallyCreated` (type: `boolean`):

Prefer manually created captions over YouTube's auto-generated ones when both exist. Auto-generated captions are still used as a fallback.

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

Optional. Language code to translate the transcript into using YouTube's own translation (e.g. 'de'). Leave empty to keep the original language.

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

Which representations to include in each result. 'srt' and 'vtt' return ready-to-save subtitle files as strings.

## `includeVideoDetails` (type: `boolean`):

Fetch view count, publish date, description and tags for each video. Slower and uses more proxy traffic. When off, you still get title, channel and duration.

## `cleanTranscript` (type: `boolean`):

Optional AI pass to add punctuation, capitalization, and paragraph breaks to raw unpunctuated captions. Adds a "textCleaned" field.

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

How many videos to process in parallel. Lower this if you see rate limiting.

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

YouTube blocks datacenter IPs on the transcript endpoint. Residential proxies are strongly recommended and are the default.

## Actor input object example

```json
{
  "startUrls": [
    "@Apify",
    "https://www.youtube.com/playlist?list=PLrAXtmErZgOdP_8GztsuKi9nrraNbKKp4"
  ],
  "maxVideos": 50,
  "languages": [
    "en"
  ],
  "preferManuallyCreated": true,
  "outputFormats": [
    "timestamped",
    "plaintext"
  ],
  "includeVideoDetails": false,
  "cleanTranscript": false,
  "maxConcurrency": 5,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

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

Extracted YouTube transcripts, subtitles, and video details.

# 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"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("t0b0t/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"] }

# Run the Actor and wait for it to finish
run = client.actor("t0b0t/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"
  ]
}' |
apify call t0b0t/youtube-transcript-scraper --silent --output-dataset

```

## MCP server setup

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