# YouTube Transcripts: Any Language, Auto-Captions, SRT (`dottti/youtube-transcripts-reliable`) Actor

Get YouTube transcripts in any language, including Arabic. Picks manual or auto captions in your order, can use YouTube's translation, expands playlists and channels, adds chapters. Videos without captions are free.

- **URL**: https://apify.com/dottti/youtube-transcripts-reliable.md
- **Developed by:** [Dottti](https://apify.com/dottti) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.80 / 1,000 transcripts

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 — Any Language, Playlists, SRT

Get the transcript of any public YouTube video. Paste video links, a playlist or a channel. Pick the languages you want, in order. You get one row per video with the text, the timed lines and a clear status.

### What it does

- Reads the same caption tracks the YouTube player shows.
- Picks the first language in your list that the video has. Inside one language, it picks the manual track before the auto-generated one.
- Works with any language code YouTube uses, including Arabic (`ar`), right-to-left text included.
- Can ask YouTube for its own machine translation when your language is missing.
- Expands playlists and channels into their videos, up to a limit you set.
- Splits the transcript by the chapter timestamps in the description. Each chapter gets its first and last spoken line and a word count. No AI is used.
- Gives every video a status, so one video without captions never fails the run.

### Input

| Field | What it does |
|---|---|
| `urls` | Video links or IDs, playlist links, or channel links (`@handle`, `/channel/UC...`). |
| `preferredLanguages` | Language codes in order, for example `["ar", "en"]`. Default `["en"]`. |
| `allowAutoGenerated` | Use YouTube speech-recognition captions when no manual track exists. Default on. |
| `translateTo` | Optional. Output language. A native track is used if it exists; otherwise YouTube translation is requested. |
| `allowTranslation` | Ask YouTube to translate when your languages are missing. Default on. |
| `fallbackToAnyLanguage` | If the wanted language is missing, return the video's own track. Default on. |
| `outputFormat` | `segments` (timed lines plus text), `text`, `srt`, or `all`. |
| `includeChapters` | Chapters with first and last line per chapter. Default on. |
| `maxVideos`, `maxVideosPerList` | Caps on videos, and therefore on cost. |

Example:

```json
{
  "urls": [
    "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
    "https://www.youtube.com/playlist?list=PL2jdOr9oJl6L3SZwO8atLeLOJN52BgPpE"
  ],
  "preferredLanguages": ["ar", "en"],
  "outputFormat": "segments",
  "maxVideosPerList": 20
}
```

### Output

One row per video:

| Field | Meaning |
|---|---|
| `status` | `ok`, `no_captions`, `blocked` or `unavailable`. |
| `videoId`, `url`, `title`, `channel`, `channelId`, `durationSec` | Video details. |
| `language` | Language of the returned text. |
| `sourceLanguage` | Track it came from. Differs from `language` only when translated. |
| `isAutoGenerated` | `true` for YouTube speech-recognition captions. |
| `isTranslated` | `true` when YouTube machine translation was used. |
| `segments` | `[{ "start": 18.64, "duration": 3.24, "text": "..." }]`, times in seconds. |
| `text` | The whole transcript as one string. |
| `srt` | SubRip subtitle text, when asked for. |
| `wordCount`, `segmentCount` | Size of the transcript. |
| `firstLine`, `lastLine` | First and last spoken line. |
| `chapters` | `[{ title, startSec, endSec, firstLine, lastLine, wordCount }]`. |
| `chaptersSource` | `description` when the video has chapter stamps, `fixed_blocks` for 5-minute blocks. |
| `availableLanguages` | Every track the video has. `(auto)` marks auto-generated ones. |
| `message` | Plain explanation when something differs from what you asked. |

Sample row (shortened):

```json
{
  "videoId": "4i7c8g5lA5U",
  "title": "العبقري الذي أضاء العالم | الدحيح",
  "status": "ok",
  "language": "ar",
  "isAutoGenerated": false,
  "isTranslated": false,
  "availableLanguages": ["ar", "ar (auto)", "en"],
  "wordCount": 4435,
  "durationSec": 1483,
  "segments": [{ "start": 7.061, "duration": 2.913, "text": "السلام عليكم ورحمة الله وبركاته،" }]
}
```

### Statuses and what you pay

You pay only for rows with status `ok`. Other rows are free.

| Status | Meaning |
|---|---|
| `ok` | Transcript delivered. |
| `no_captions` | The video has no captions, or none that match your language and auto-caption settings. |
| `unavailable` | Private, removed, age-restricted or region-blocked video, or an invalid ID. |
| `blocked` | YouTube refused every proxy tier. Run it again later. |

Pricing is per delivered transcript. You can set a maximum cost per run; the run stops cleanly when it is reached.

### Good to know

- **Translation.** YouTube sometimes refuses machine-translation requests. When that happens you get the original track, `isTranslated` is `false`, and `message` says so. Turn off `fallbackToAnyLanguage` if you prefer `no_captions` in that case.
- **Proxies.** The run starts without a proxy. If YouTube refuses requests, it moves to datacenter proxy, then residential proxy, and stays there for the rest of the run. Proxy cost is included in the price.
- **Chapters.** Chapters come from timestamps in the video description. Videos without stamps are split into 5-minute blocks.
- **Run summary.** The `RUN_SUMMARY` record lists counts per status, timing, and requests and bytes per video.
- **Live streams** that are still live have no final captions yet.

# Actor input Schema

## `urls` (type: `array`):

YouTube video URLs or 11-character IDs, playlist URLs, or channel URLs (@handle, /channel/UC...). Watch, youtu.be, Shorts, embed and live links all work. A playlist or channel is expanded into its videos, up to the per-list limit below.

## `preferredLanguages` (type: `array`):

Language codes such as en, ar, fr, es, pt-BR, zh-Hans. The first language that the video has wins. Inside one language, a manual track is chosen before an auto-generated one. Any code YouTube uses works.

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

Use YouTube's speech-recognition captions when no manual track exists in a wanted language. Turn off to accept manual captions only.

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

Language code for the output. If the video has a track in this language, that track is used. If not, YouTube's own machine translation of the preferred-language track is requested. Leave empty to keep the original language.

## `allowTranslation` (type: `boolean`):

When no track exists in your preferred languages, ask YouTube for a machine translation into the first one. YouTube refuses some translation requests; see fallback below.

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

When the wanted language is missing and translation is off or refused, return the video's own track and say so in `language` and `message`. Turn off to get status no_captions instead (not charged).

## `outputFormat` (type: `string`):

segments: timed lines (start, duration, text) plus plain text. text: plain text only. srt: SubRip subtitle file text. all: everything.

## `includeChapters` (type: `boolean`):

Split the transcript by the chapter timestamps in the video description, with the first and last spoken line and a word count per chapter. Videos without chapter stamps are split into 5-minute blocks. No AI is used.

## `includeChapterText` (type: `boolean`):

Adds each chapter's full text. Makes rows larger.

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

Hard cap on videos processed in this run, and therefore on cost.

## `maxVideosPerList` (type: `integer`):

How many videos to take from each playlist or channel (newest first for channels).

## `autoProxyFallback` (type: `boolean`):

Start without a proxy. If YouTube refuses the request, switch to datacenter proxy, then to residential proxy, and stay there for the rest of the run. Proxy cost is included in the price.

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

Set this only to force one proxy for every request. It replaces the automatic fallback.

## `requestDelayMs` (type: `integer`):

Pause before each request. Raise it if the log shows refusals.

## Actor input object example

```json
{
  "urls": [
    "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
    "https://www.youtube.com/watch?v=4i7c8g5lA5U"
  ],
  "preferredLanguages": [
    "ar",
    "en"
  ],
  "allowAutoGenerated": true,
  "allowTranslation": true,
  "fallbackToAnyLanguage": true,
  "outputFormat": "segments",
  "includeChapters": true,
  "includeChapterText": false,
  "maxVideos": 100,
  "maxVideosPerList": 50,
  "autoProxyFallback": true,
  "proxyConfiguration": {
    "useApifyProxy": false
  },
  "requestDelayMs": 300
}
```

# Actor output Schema

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

Video ID, URL, title, channel, duration, status, language, auto-generated and translated flags, segments, plain text, SRT, word count, and chapters.

## `runSummary` (type: `string`):

Counts per status, success rate, timing, and requests and bytes per video.

# 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 = {
    "urls": [
        "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
        "https://www.youtube.com/watch?v=4i7c8g5lA5U"
    ],
    "preferredLanguages": [
        "ar",
        "en"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("dottti/youtube-transcripts-reliable").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 = {
    "urls": [
        "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
        "https://www.youtube.com/watch?v=4i7c8g5lA5U",
    ],
    "preferredLanguages": [
        "ar",
        "en",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("dottti/youtube-transcripts-reliable").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 '{
  "urls": [
    "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
    "https://www.youtube.com/watch?v=4i7c8g5lA5U"
  ],
  "preferredLanguages": [
    "ar",
    "en"
  ]
}' |
apify call dottti/youtube-transcripts-reliable --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,dottti/youtube-transcripts-reliable"
        }
    }
}
```

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/d0o6Akk4HOPX9gXXQ/builds/rV41mXQsfc4bwkCvS/openapi.json
