# YouTube Transcript Scraper - Subtitles & Captions to Text (`dami_studio/youtube-transcript-scraper`) Actor

Extract transcripts, subtitles and closed captions from any public YouTube video as text, SRT or VTT. Auto-generated and manual captions, any language, with timestamps. No login or API key. Failed or caption-less videos are never charged.

- **URL**: https://apify.com/dami\_studio/youtube-transcript-scraper.md
- **Developed by:** [Dami's Studio](https://apify.com/dami_studio) (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 $2.50 / 1,000 transcript extracteds

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/platform/actors/running/actors-in-store#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

Extract complete YouTube transcripts in batches without an API key, cookies, or a login.

Each successful video produces one dataset row containing:

- Full plain text
- Timestamped segments with start, duration, and end times in seconds
- Ready-to-save SRT subtitles
- Ready-to-save WebVTT subtitles
- Detected and requested language
- Available transcript languages and automatic-caption status
- Video ID, canonical URL, title, author, channel ID, duration, view count, and thumbnail when YouTube provides them

The actor accepts standard YouTube watch URLs, Shorts URLs, live URLs, embed URLs, `youtu.be` links, and raw 11-character video IDs. Duplicate videos are processed once.

### Why use this actor?

- No YouTube Data API key
- No account, cookies, or login
- Batch input with controlled concurrency
- Language preference fallback
- Manual and automatically generated captions
- Full text plus structured segments, SRT, and VTT in one result
- Per-video diagnostics: one unavailable video does not fail the batch
- Optional proxy, disabled by default to keep costs low
- Pay only for successful transcript rows

### Input

#### `videoUrls`

An array of YouTube URLs or video IDs.

```json
{
  "videoUrls": [
    "https://www.youtube.com/watch?v=M7lc1UVf-VE",
    "https://youtu.be/dQw4w9WgXcQ",
    "aqz-KE-bpKQ"
  ]
}
```

#### `videoIds`

An optional second array for raw video IDs. This is convenient for API clients that store IDs separately from URLs.

#### `languagePreferences`

BCP 47 language codes in priority order. The actor uses the first available exact or base-language match. If none match, it falls back to another available transcript instead of failing the video.

```json
{
  "videoUrls": ["M7lc1UVf-VE"],
  "languagePreferences": ["fr-CA", "fr", "en"]
}
```

#### Reliability controls

- `maxConcurrency`: parallel videos, from 1 to 10. Default: `2`.
- `retries`: retries for temporary rate limits and server errors, from 0 to 5. Default: `2`.
- `requestTimeoutSecs`: total timeout per video, from 10 to 180 seconds. Default: `45`.

Low concurrency is intentional. It reduces throttling and keeps compute usage small.

#### `proxyConfiguration`

Proxy use is optional and off by default. Direct requests are normally the most profitable option because transcript payloads are small and public. Enable Apify Proxy only when YouTube rate-limits the run IP.

Residential proxy traffic may cost more than the `$0.0025` target transcript event when used inefficiently. If a proxy is enabled, the actor keeps one sticky proxy session per video so YouTube sees a consistent IP across metadata and caption requests.

### Output

A successful row has this shape:

```json
{
  "ok": true,
  "_sample": false,
  "_diagnostic": false,
  "videoId": "M7lc1UVf-VE",
  "url": "https://www.youtube.com/watch?v=M7lc1UVf-VE",
  "title": "YouTube Developers Live: Embedded Web Player Customization",
  "detectedLanguage": "en",
  "requestedLanguage": "en",
  "isAutoGenerated": false,
  "segmentCount": 42,
  "text": "Complete transcript text...",
  "segments": [
    {
      "text": "First caption",
      "start": 0.25,
      "duration": 2.5,
      "end": 2.75
    }
  ],
  "srt": "1\n00:00:00,250 --> 00:00:02,750\nFirst caption",
  "vtt": "WEBVTT\n\n00:00:00.250 --> 00:00:02.750\nFirst caption",
  "author": "Google for Developers",
  "channelId": "UC_x5XG1OV2P6uZZ5FSM9Ttw",
  "durationSeconds": 183,
  "viewCount": 1000,
  "thumbnailUrl": "https://i.ytimg.com/...",
  "availableLanguages": [
    {
      "languageCode": "en",
      "languageName": "English",
      "isAutoGenerated": false
    }
  ]
}
```

Metadata fields can be `null` when YouTube does not expose them. Transcript fields are present for every successful row.

### Charging and diagnostics

The actor calls the `transcript` pay-per-event charge once after each successful transcript row is stored. The intended Store price is `$0.0025` per successful transcript.

These rows are never charged:

- Invalid URL or video ID diagnostics
- Private, removed, or region-blocked videos
- Videos with captions disabled or no transcript
- Rate-limit, timeout, or network diagnostics
- The explicit `_sample: true` row returned for an empty input

An empty/default Apify automated test exits successfully with exactly one sample row. This prevents a no-input health check from placing the actor under maintenance.

### Common diagnostic codes

| Code | Meaning |
| --- | --- |
| `INVALID_VIDEO` | The input is not a supported YouTube URL or video ID. |
| `VIDEO_UNAVAILABLE` | The video is private, removed, blocked, or unavailable. |
| `TRANSCRIPT_DISABLED` | The owner disabled captions. |
| `NO_TRANSCRIPT` | YouTube returned no transcript segments. |
| `LANGUAGE_UNAVAILABLE` | A requested language could not be fetched and no fallback worked. |
| `RATE_LIMITED` | YouTube throttled the run IP. |
| `TIMEOUT` | The per-video deadline expired. |
| `NETWORK` | A network or proxy request failed. |

Diagnostic rows include a concise error and a practical hint. The run remains successful so valid results from the same batch are preserved.

### Run locally

Node.js 20 or newer is required.

```bash
npm ci
npm test
npm run test:live
```

Run the actor with Apify local storage:

```bash
apify actor:run
```

Or set an input in `storage/key_value_stores/default/INPUT.json` and run:

```bash
npm start
```

The live test uses `M7lc1UVf-VE` by default. Override it with `LIVE_VIDEO_ID`.

### Technical notes

The actor uses the maintained `youtube-transcript-plus` package and YouTube's public, undocumented caption interfaces. It retries temporary HTTP failures with exponential backoff, discovers available languages, falls back between requested languages, and normalizes subtitle timestamps itself.

Because the caption interface is undocumented, YouTube can change it. Per-video diagnostics are designed to make those changes visible without losing other batch results.

Use this actor only for videos and transcript content you are allowed to process. Respect YouTube's terms, copyright, privacy, and applicable law.

# Actor input Schema

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

YouTube watch, Shorts, live, embed, or youtu.be URLs. Raw 11-character video IDs are also accepted. Duplicate videos are processed once.

## `videoIds` (type: `array`):

Optional additional list of raw 11-character YouTube video IDs. This is useful for programmatic runs that already have IDs.

## `languagePreferences` (type: `array`):

BCP 47 language codes in priority order, for example en, es, fr, or pt-BR. The first available preference is used. If none match, the actor falls back to another available transcript.

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

Number of videos processed at once. Keep this low to reduce YouTube throttling and compute cost.

## `retries` (type: `integer`):

Retries for temporary rate limits and YouTube server errors. Backoff is automatic.

## `requestTimeoutSecs` (type: `integer`):

Maximum time for all transcript requests for one video.

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

Off by default to keep costs low. Enable Apify Proxy only if YouTube rate-limits cloud traffic. Residential proxy traffic can cost more than the transcript event, so use it only when needed.

## Actor input object example

```json
{
  "videoUrls": [],
  "videoIds": [],
  "languagePreferences": [
    "en"
  ],
  "maxConcurrency": 2,
  "retries": 2,
  "requestTimeoutSecs": 45,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

One charged row is stored for each successfully processed video. Invalid videos, unavailable transcripts, and run diagnostics are stored as uncharged rows.

## `dataset` (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 = {};

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

# Run the Actor and wait for it to finish
run = client.actor("dami_studio/youtube-transcript-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{}' |
apify call dami_studio/youtube-transcript-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=dami_studio/youtube-transcript-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/F1UkNTQcvlSLhvJQd/builds/vfadiO1Ibt8h1G0Og/openapi.json
