# YouTube Transcript Scraper — Channels, Playlists & AI Chunks (`cleancut/youtube-transcript-scraper`) Actor

Extract YouTube transcripts and subtitles in bulk from videos, Shorts, whole channels, playlists or search results. Plain text, timestamped segments, SRT/VTT and RAG-ready chunks with deep links. Pay only for transcripts delivered.

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

## Pricing

from $0.33 / 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.
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, Playlists, Search & AI-ready Chunks

Extract **YouTube transcripts and subtitles in bulk**: single videos, Shorts, **entire channels**, **playlists** or **search results**. Get clean text, timestamped segments, SRT/WebVTT files and **RAG-ready chunks with deep links** to the exact second of the video.

**You only pay for transcripts that are delivered.** Videos without captions, private or unavailable videos are reported in the dataset for free.

### What can it do?

- 🎬 **Any YouTube input** — `watch?v=` URLs, `youtu.be` links, Shorts, live replays, embeds, plain video IDs, channels (`@handle`, `/channel/UC…`, `/c/…`, `/user/…`) and playlists.
- 🔎 **Search mode** — type a keyword and transcribe the top N videos.
- 📺 **Whole channels** — Videos, Shorts and/or Live tabs, with a per-source limit.
- 🌍 **Language control** — preferred languages in order (`es` also matches `es-419`), **caption type** (human-made first, any, human-made only or automatic only), automatic fallback, and **YouTube's built-in translation** to any language.
- 🧭 **Free discovery mode** — list every caption track and translation language of a video *before* paying for transcripts.
- 🤖 **AI-ready output** — transcript chunks of N seconds, each with start time and a link like `…watch?v=ID&t=125s`. Drop them straight into embeddings, vector DBs or LLM prompts.
- 📝 **Formats** — plain text, segments (start/duration/text), SRT and WebVTT.
- 📊 **Video details included, free** — title, channel, duration, views, **likes, publish date, upload date, category**, description, keywords and thumbnail. No extra event, no extra charge.
- 💸 **Lightweight & cheap** — HTTP-only (no browser), requests only the fields it needs.

### Use cases

- Feed **LLMs / RAG pipelines** with video knowledge (courses, podcasts, conferences).
- **Content repurposing**: blog posts, newsletters, social clips from videos.
- **SEO & market research**: what competitors and creators say about a topic.
- **Accessibility & subtitles**: download SRT/VTT files in bulk.
- **Academic research** and media monitoring.

### Input example

```json
{
  "startUrls": [
    "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
    "https://www.youtube.com/@RickAstleyYT",
    "https://www.youtube.com/playlist?list=PLlaN88a7y2_qHDbY9eQbuNTAuEJUSEeuu"
  ],
  "searchQueries": ["how to make sourdough bread"],
  "maxVideosPerSource": 20,
  "languages": ["en", "es"],
  "captionType": "preferManual",
  "outputFormats": ["segments", "chunks"],
  "chunkDurationSeconds": 60,
  "proxyConfiguration": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"] }
}
```

### Output example

```json
{
  "videoId": "dQw4w9WgXcQ",
  "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
  "status": "ok",
  "title": "Rick Astley - Never Gonna Give You Up (Official Video) (4K Remaster)",
  "channelName": "Rick Astley",
  "channelId": "UCuAXFkgsw1L7xaCfnd5JJOw",
  "durationSeconds": 213,
  "durationText": "3:33",
  "viewCount": 1833568509,
  "likeCount": 19491030,
  "publishDate": "2009-10-24T23:57:33-07:00",
  "uploadDate": "2009-10-24T23:57:33-07:00",
  "category": "Music",
  "language": "es-419",
  "languageName": "Spanish (Latin America)",
  "isAutoGenerated": false,
  "isTranslated": false,
  "availableLanguages": [{ "languageCode": "en", "name": "English", "isAutoGenerated": false, "isTranslatable": true }],
  "segmentCount": 60,
  "wordCount": 308,
  "text": "Conocemos bien el amor Tú conoces las reglas, igual que yo…",
  "segments": [{ "start": 18.64, "duration": 3.24, "text": "Conocemos bien el amor" }],
  "chunks": [{
    "index": 0, "start": 18.64, "end": 82.04, "startTime": "0:18",
    "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ&t=18s",
    "text": "Conocemos bien el amor Tú conoces las reglas…", "wordCount": 52
  }],
  "source": { "type": "video", "input": "https://www.youtube.com/watch?v=dQw4w9WgXcQ" }
}
```

#### Output views

The Output tab (and the API, with `?view=`) offers ready-made tables:

| View | What you get |
|---|---|
| **Overview** | One row per video: thumbnail, title, channel, date, duration, views, language and the transcript |
| **Timestamped segments** | One row per caption line with start and duration |
| **AI chunks (RAG)** | One row per chunk with a deep link to that second |
| **Subtitles (SRT / WebVTT)** | Ready-to-save subtitle files |
| **Video details** | Channel, dates, category, views, likes, keywords, thumbnail |
| **Caption languages** | One row per caption track (discovery mode) |
| **Errors (free)** | Why a video could not be transcribed |

Every field is documented in the dataset schema (type, description and example), so AI agents and integrations know exactly what they get. A run summary is saved as `SUMMARY` in the key-value store.

### Errors (free)

Videos that cannot be transcribed produce a free row with `status: "error"` and an `errorCode`:

| errorCode | Meaning |
|---|---|
| `TRANSCRIPTS_DISABLED` | The video has no captions at all |
| `LANGUAGE_NOT_AVAILABLE` | None of your languages exists and fallback is off |
| `CAPTION_TYPE_NOT_AVAILABLE` | You asked for human-made only (or automatic only) and the video has none |
| `VIDEO_UNAVAILABLE` / `VIDEO_UNPLAYABLE` | Deleted, private or region-locked video |
| `AGE_RESTRICTED` | Requires a signed-in account |
| `BLOCKED` | YouTube rejected the IP — use the RESIDENTIAL proxy group |

### Pricing

Pay per event — **you only pay for transcripts delivered**. Listing channels, playlists and search results is free, **discovery mode is free**, video details are free, and so are videos without captions or with errors.

| Your Apify plan | Price per 1,000 transcripts |
|---|---:|
| Free | $0.39 |
| Starter (Bronze) | $0.37 |
| Scale (Silver) | $0.35 |
| Business (Gold) and above | $0.33 |

Plus a start fee of $0.0005 per run (at the default 512 MB of memory). Example: transcribing a whole channel of 500 videos costs **$0.17–$0.20**. Set **“Max transcripts in total”** or the run's maximum cost to cap spending.

### Tips

- **Proxy:** the default is Apify **RESIDENTIAL** proxy, because YouTube blocks most data-center IPs. Each transcript transfers only ~30 KB, so the proxy cost is tiny.
- **Automatic vs human captions:** `isAutoGenerated: true` means YouTube's speech recognition. Use **Caption type** to force human-made only or automatic only.
- **Not sure which languages a video has?** Turn on **“List caption languages only (free)”** first: you get every caption track and the languages it can be translated to, without paying.
- **Translation** uses YouTube's own machine translation, available only for tracks marked as translatable.
- **Integrations:** run it from Make, Zapier, n8n, LangChain or the Apify API, and schedule it to transcribe new channel uploads daily.

### FAQ

**Does it use the official YouTube Data API?** No. The official API only lets channel owners download their own captions. This Actor reads the same publicly available captions that the YouTube player shows.

**Can it transcribe videos without captions?** No. If a video has no captions (neither human nor automatic), it is reported as `TRANSCRIPTS_DISABLED` and not charged.

**Is it legal?** It extracts publicly available captions. You are responsible for how you use the content; respect copyright and YouTube's Terms of Service, and do not republish transcripts you don't have rights to.

### Changelog

- **0.2** — Cleaner input with sections, caption type selector, one “Extra formats” picker, free discovery mode, free likes, publish date, upload date and category, 7 output views and a fully documented output schema.
- **0.1** — First release: videos, Shorts, channels, playlists, search, language preference & translation, SRT/VTT, AI chunks.

### More from Cleancut Data

- 🏨 **[Google Hotels Rate Shopper](https://apify.com/cleancut/google-hotels-price-comparator)**: what every booking site charges for your hotels and dates, with taxes, free cancellation and rate-parity gaps.
- ✈️ **[Google Flights Cheapest Fares](https://apify.com/cleancut/google-flights-cheapest-fares)**: the cheapest Google Flights fare per route and date, the cheapest day of the month and price-drop alerts.

# Actor input Schema

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

Videos (watch, youtu.be, Shorts, live, embed, mobile), channels (@handle, /channel/UC…, /c/…, /user/…) and playlists. Plain video IDs and @handles work too.

## `searchQueries` (type: `array`):

One term per line. Each term is searched on YouTube and its top videos are transcribed (how many is set in “Channel, playlist & search options”).

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

Newest first for channels. Use a small number to test, a large one to transcribe a whole channel.

## `channelTabs` (type: `array`):

Which channel sections to read videos from.

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

Language codes in order of preference (en, es, pt…). “es” also matches regional variants such as “es-419”.

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

Which captions to use when a video has both human-made and automatic (speech recognition) captions.

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

If none of the preferred languages exists, return the best available transcript instead of an error.

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

Language code to translate the transcript into (es, fr, de…). Leave empty to keep the original language. Tip: run “List caption languages only” first to see which languages each video can be translated to.

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

Pick one or more. AI-ready chunks group the transcript into blocks of N seconds, each with a deep link to that second of the video: ideal for LLMs, embeddings and RAG.

## `chunkDurationSeconds` (type: `integer`):

Approximate length of each AI-ready chunk. Only used when “AI-ready chunks” is selected.

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

Adds publish date, upload date, category, like count, description, keywords, thumbnail and live flag (about half a second more per video). Title, channel, duration and views are always included.

## `listLanguagesOnly` (type: `boolean`):

Returns one free row per video with its caption tracks (language, human-made or automatic, translatable) and the languages it can be translated to. No transcript is downloaded and nothing is charged.

## `maxTotalTranscripts` (type: `integer`):

Stop after this many transcripts (0 = no limit). Useful to cap the cost of a run.

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

How many videos to process at the same time.

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

Residential proxy is recommended: YouTube blocks most data-center IPs. Transcripts are tiny (~30 KB per video), so residential traffic costs very little.

## Actor input object example

```json
{
  "startUrls": [
    "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
  ],
  "maxVideosPerSource": 20,
  "channelTabs": [
    "videos"
  ],
  "languages": [
    "en"
  ],
  "captionType": "preferManual",
  "fallbackToAnyLanguage": true,
  "outputFormats": [
    "segments"
  ],
  "chunkDurationSeconds": 60,
  "includeVideoDetails": true,
  "listLanguagesOnly": false,
  "maxTotalTranscripts": 0,
  "maxConcurrency": 5,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

## `transcripts` (type: `string`):

All dataset items: transcript text, language, optional timestamped segments, SRT/WebVTT and AI-ready chunks, video details, and free error rows.

## `chunks` (type: `string`):

One row per AI-ready chunk with start time and a deep link to that second of the video. Filled when “AI-ready chunks” is selected in Extra formats.

## `details` (type: `string`):

Video metadata per video: channel, publish and upload dates, category, views, likes, keywords and thumbnail.

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

JSON record with the number of videos found, transcripts delivered, languages listed and errors by code.

# 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"
    ],
    "languages": [
        "en"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("cleancut/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"],
    "languages": ["en"],
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

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

```

## MCP server setup

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