# YouTube Transcript Scraper — Captions, SRT & Text (`crawloop/youtube-transcript-scraper`) Actor

Scrape YouTube transcripts and subtitles without a Data API key: timestamped captions, full text, SRT/VTT, language fallback, and video metadata. Batch watch, Shorts, and youtu.be URLs for SEO, RAG, and MCP agents. Export JSON via API, Python, or Node.js.

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

## Pricing

from $1.49 / 1,000 transcript scrapeds

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?

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 — Captions, SRT & Text

> Unofficial tool for publicly accessible YouTube caption data. YouTube and related trademarks belong to their respective owners. Not affiliated with, sponsored by, or endorsed by YouTube or Google. Provided for informational use only; users must comply with applicable terms and laws.

| YouTube suite | Social listening |
| :--- | :--- |
| YouTube Transcript Scraper ◄── you are here | [Bluesky Scraper](https://apify.com/crawloop/bluesky-scraper) |
| [YouTube Comments Scraper](https://apify.com/crawloop/youtube-comments-scraper) | [Product Hunt Scraper](https://apify.com/crawloop/producthunt-scraper) |

**YouTube Transcript Scraper** for Apify — a **YouTube captions scraper** and **YouTube subtitles scraper** that turns public videos and Shorts into structured JSON: timestamped caption segments, full plain text, optional SRT / WebVTT, language metadata, and video title / channel / duration. No YouTube login, no Data API quota. A practical **YouTube transcript API alternative** for SEO repurposing, RAG corpora, and agent workflows — run from **Python**, **Node.js**, **cURL**, or **MCP** / AI assistants and export the dataset as JSON / CSV / Excel.

### When to use

- Turn a video or lecture into blog copy, show notes, or SEO landing text
- Build an LLM / RAG corpus from a list of YouTube URLs (Python or Node.js)
- Pull captions in a preferred language (manual first, auto-generated captions fallback)
- Export SRT or WebVTT for editors and players
- Batch watch, Shorts, embed, and youtu.be links in one YouTube scraper run

### When not to use

- Private, age-gated, or members-only videos (public Innertube only)
- Videos with **captions disabled** — those rows get `errorCode: TRANSCRIPTS_DISABLED`
- Full channel, playlist, or search crawls — this Actor is **URL-list transcripts**, not a YouTube catalog scraper
- Comment threads — use [YouTube Comments Scraper](https://apify.com/crawloop/youtube-comments-scraper) instead

### Key features

- **Transcripts-first slice** — purpose-built YouTube transcript / captions scraper, not a bloated “everything” YouTube tool
- **ANDROID Innertube player** — same caption track list the mobile client uses; no Data API key
- **No login** — Chrome-impersonated TLS via `curl_cffi`
- **Language priority** — `en` matches `en-US`; manual captions win over auto-generated when type is `any`
- **YouTube translation** — optional `translateTo` uses built-in `tlang` when the source track allows it
- **LLM-ready text + timestamps** — concatenated `text` plus `segments[{start, duration, text}]`
- **SRT / VTT on demand** — extra string fields for editors without a second formatter
- **Video metadata** — title, channel, duration, views, thumbnail from the player response
- **Structured errors** — failed videos stay in the dataset (`errorCode`) and are **not billed**; the rest of the batch continues
- **Shorts + youtu.be** — watch URLs, Shorts, embeds, live paths, and bare 11-char video IDs
- **Proxy-ready** — Apify RESIDENTIAL when datacenter IPs hit a consent wall or empty timedtext

### Input

| Parameter | Description |
|-----------|-------------|
| `videoUrls` | Watch / Shorts / youtu.be URLs or 11-char video IDs |
| `languages` | Preference list of language codes (default `["en"]`) |
| `transcriptType` | `any` (manual first), `manual`, or `generated` |
| `translateTo` | Optional YouTube translation target (e.g. `es`) |
| `includeMetadata` | Title, channel, duration, views, thumbnail (default on) |
| `includeSrt` / `includeVtt` | Add formatted subtitle strings |
| `includeErrorRows` | Keep failed videos in the dataset (default on; not billed) |
| `maxVideos` | Run cap (default 50, hard cap 250) |
| `hl` / `gl` | YouTube language and country (default `en` / `US`) |
| `requestDelaySecs` | Throttle between player / timedtext calls |
| `proxyConfiguration` | Optional Apify proxy (RESIDENTIAL if blocked) |

English captions, metadata on:

```json
{
  "videoUrls": ["https://www.youtube.com/watch?v=dQw4w9WgXcQ"],
  "languages": ["en"],
  "transcriptType": "any",
  "includeMetadata": true
}
```

Batch + SRT + language fallback:

```json
{
  "videoUrls": [
    "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
    "https://youtu.be/dQw4w9WgXcQ",
    "https://www.youtube.com/shorts/dQw4w9WgXcQ"
  ],
  "languages": ["es", "en"],
  "transcriptType": "any",
  "includeSrt": true,
  "maxVideos": 20,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": ["RESIDENTIAL"],
    "apifyProxyCountry": "US"
  }
}
```

### Output

One dataset row per video — ready for CSV / Excel / JSON export.

| Field | Description |
|-------|-------------|
| `type` / `ok` | Always `transcript`; `ok` is false on structured errors |
| `videoId` / `videoUrl` | Canonical watch URL |
| `title` / `channelName` / `channelId` / `channelUrl` | Player metadata |
| `durationSeconds` / `viewCount` / `thumbnailUrl` | Length, views, best thumbnail |
| `languageCode` / `languageName` | Caption language actually returned |
| `isGenerated` / `isTranslated` | Auto-generated ASR / YouTube `tlang` |
| `segmentCount` | Number of timestamped cues |
| `text` | Full transcript, one string (LLM / SEO) |
| `segments` | `{start, duration, text}` cues in seconds |
| `srt` / `vtt` | Present when the matching include flag is on |
| `availableLanguages` | Caption tracks on the video (`languageCode`, `isGenerated`) |
| `errorCode` / `errorMessage` | Set when `ok` is false |
| `scrapedAt` | UTC timestamp |

```json
{
  "type": "transcript",
  "ok": true,
  "videoId": "dQw4w9WgXcQ",
  "videoUrl": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
  "title": "Rick Astley - Never Gonna Give You Up (Official Video) (4K Remaster)",
  "channelName": "Rick Astley",
  "channelId": "UCuAXFkgsw1L7xaCfnd5JJOw",
  "durationSeconds": 213,
  "languageCode": "en",
  "languageName": "English",
  "isGenerated": false,
  "isTranslated": false,
  "segmentCount": 42,
  "text": "We're no strangers to love You know the rules and so do I",
  "segments": [
    {"start": 18.64, "duration": 3.24, "text": "We're no strangers to love"}
  ],
  "availableLanguages": [
    {"languageCode": "en", "languageName": "English", "isGenerated": false},
    {"languageCode": "en", "languageName": "English (auto-generated)", "isGenerated": true}
  ],
  "errorCode": null,
  "scrapedAt": "2026-08-21T13:00:00Z"
}
```

### Use cases

- Repurpose webinars and product videos into indexed articles
- Feed lecture or podcast YouTube uploads into a vector store
- Localize captions with `translateTo` for a target market
- QA whether a channel ships manual subtitles vs auto-captions
- Give an MCP agent a YouTube URL and get the spoken text back

### Integration examples

#### Node.js

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('crawloop/youtube-transcript-scraper').call({
  videoUrls: ['https://www.youtube.com/watch?v=dQw4w9WgXcQ'],
  languages: ['en'],
  includeSrt: true,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items[0].text);
```

#### Python

```python
from apify_client import ApifyClient

client = ApifyClient()
run = client.actor("crawloop/youtube-transcript-scraper").call(
    run_input={
        "videoUrls": ["https://www.youtube.com/watch?v=dQw4w9WgXcQ"],
        "languages": ["en"],
        "includeSrt": True,
    }
)
items = client.dataset(run["defaultDatasetId"]).list_items().items
print(items[0]["text"])
```

#### cURL

```bash
curl -X POST "https://api.apify.com/v2/acts/crawloop~youtube-transcript-scraper/runs?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"videoUrls":["https://www.youtube.com/watch?v=dQw4w9WgXcQ"],"languages":["en"]}'
```

### MCP and AI assistants

Use this Actor from AI tools via [Apify MCP](https://docs.apify.com/platform/integrations/mcp).
Connect your Apify account, then call this Actor by its Store ID / name (`crawloop/youtube-transcript-scraper`).

Example prompts:

- "Run YouTube Transcript Scraper for https://www.youtube.com/watch?v=dQw4w9WgXcQ and return the full text"
- "Scrape English transcripts for this URL list, include SRT, and summarize each video in 5 bullets"
- "Chain YouTube Transcript Scraper then YouTube Comments Scraper for this video to compare spoken claims vs audience replies"

### Suite next step

After exporting captions, run [YouTube Comments Scraper](https://apify.com/crawloop/youtube-comments-scraper) on the same URLs to attach audience replies, likes, and pinned comments.

### FAQ

**How do I scrape YouTube transcripts without the official API?**\
This Actor is a **YouTube transcript API alternative**: it reads the public caption track list and fetches timedtext JSON. No Data API key or OAuth.

**Do I need a YouTube login or cookies?**\
No for public videos with captions. Enable Apify RESIDENTIAL proxy if you see bot checks or empty tracks from datacenter IPs.

**Manual vs auto-generated captions?**\
`transcriptType: any` prefers uploaded captions, then auto-generated captions (ASR). Use `manual` or `generated` to force one.

**Does this scrape YouTube subtitles and SRT?**\
Yes. You get timestamped `segments`, concatenated `text`, and optional `srt` / `vtt` strings for editors and web players.

**Can I scrape YouTube Shorts transcripts?**\
Yes. Shorts URLs and bare video IDs work the same as watch URLs when YouTube exposes captions.

**What if a video has no captions?**\
The row is marked `ok: false` with `errorCode: TRANSCRIPTS_DISABLED` (when error rows are on). Other videos in the batch still run. Failed rows are not billed.

**Does this download video or audio?**\
No. Captions only — no media files, no speech-to-text fallback.

**Channel or playlist URL?**\
Not in this Actor. Expand the list yourself (or another catalog scraper), then pass video URLs here.

### Related Actors

| Actor | Use after / beside |
| :--- | :--- |
| [YouTube Comments Scraper](https://apify.com/crawloop/youtube-comments-scraper) | Replies, likes, pinned / hearted comments on the same videos |
| [Bluesky Scraper](https://apify.com/crawloop/bluesky-scraper) | Cross-platform social listening |
| [Product Hunt Scraper](https://apify.com/crawloop/producthunt-scraper) | Launch-day comments when the video is a product drop |

# Actor input Schema

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

YouTube watch / Shorts / youtu.be URLs, or bare 11-character video IDs. One dataset row per video.

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

ISO 639-1 / BCP-47 codes in preference order (e.g. en, es, de-DE). First match wins. Prefix match: en also matches en-US.

## `transcriptType` (type: `string`):

Prefer manually uploaded captions, auto-generated only, or either (manual first).

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

If set, use YouTube's built-in translation into this language code (e.g. es, ja). Source track must be translatable.

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

Add title, channel, duration, view count, and thumbnail from the player response.

## `includeSrt` (type: `boolean`):

Add an srt string field (SubRip) on each row for video editors.

## `includeVtt` (type: `boolean`):

Add a vtt string field (WebVTT) on each row for web players.

## `includeErrorRows` (type: `boolean`):

Push a row with errorCode when a video has no captions or the language is missing. Failed rows are not billed.

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

Cap on videos processed in this run. 0 = Actor hard cap.

## `hl` (type: `string`):

YouTube UI language (e.g. en, de, es).

## `gl` (type: `string`):

YouTube country / region code (e.g. US, DE, GB).

## `requestDelaySecs` (type: `number`):

Throttle player and timedtext calls. Raise if you see HTTP 429.

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

Optional. Enable Apify RESIDENTIAL proxy if datacenter IPs hit consent walls or empty caption tracks.

## Actor input object example

```json
{
  "videoUrls": [
    "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
  ],
  "languages": [
    "en"
  ],
  "transcriptType": "any",
  "includeMetadata": true,
  "includeSrt": false,
  "includeVtt": false,
  "includeErrorRows": true,
  "maxVideos": 50,
  "hl": "en",
  "gl": "US",
  "requestDelaySecs": 0.25,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}
```

# Actor output Schema

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

Default dataset items.

# 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"
        ],
        "apifyProxyCountry": "US"
    }
};

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

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

```

## MCP server setup

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