# YouTube Transcript Extractor - Timestamps (`datascraperes/youtube-transcript-scraper`) Actor

Extract complete, timestamped transcripts from up to 5,000 public YouTube videos in 47 selectable languages. Get one structured Dataset item per video with title, language, segment timings, and explicit error status for AI, RAG, SEO, and research workflows.

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

## Pricing

from $2.25 / 1,000 complete 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

Extract complete, timestamped captions from up to 5,000 public YouTube videos. **Get full transcript text, timed segments and optional SRT subtitles** in one Dataset item per video, with actual language, caption source and a clear success or error status.

### What this Actor does

Submit public YouTube video URLs and choose one of 47 requested languages. The Actor preserves every text-bearing segment of the selected caption track, joins those segments into plain text, and includes available video and channel metadata. It does not summarize or shorten the transcript.

Manual captions are preferred. When allowed, automatic captions or YouTube-provided translations can be used; `isAutoGenerated` and `isTranslated` distinguish these cases. A missing or inaccessible track produces an explicit error item, not a partial successful transcript.

### Use cases

- Build searchable video libraries and retrieval-augmented generation (RAG) datasets.
- Process podcasts, lectures and tutorials with timestamps for source attribution.
- Obtain SRT text for subtitle workflows without downloading media.
- Filter caption sources and detect changes using language metadata and a transcript hash.

### How to use

1. Open the Actor in Apify Console.
2. Enter public video URLs and select the transcript language.
3. Optionally require manual captions or enable SRT output.
4. Click **Start** and open the Dataset. Choose the overview, transcript, RAG or subtitles view.

### Input

| Field | Meaning |
|---|---|
| `videoUrls` | Required: 1–5,000 public watch, short-link, Shorts, live or embed URLs. Duplicate video IDs are processed once, preserving first-seen order. |
| `language` | English (`en`) by default. Choose from the 47-language dropdown. A base code such as `es` can match a regional variant; a regional request remains specific. The returned `languageUsed` identifies the actual track. |
| `captionPolicy` | `any` (default) prefers manual captions, then permits automatic or translated tracks. `manual_only` requires a manually provided track in the requested language; it does not substitute automatic captions or generated translations. |
| `includeSrt` | `false` by default. Set to `true` to include SRT text derived from every returned segment, at no additional transcript event charge. |

Unsupported language codes and invalid input are rejected. The Actor does not silently return an unrelated language. This example enables SRT:

```json
{
  "includeSrt": true,
  "language": "en",
  "captionPolicy": "any",
  "videoUrls": [
    "https://www.youtube.com/watch?v=jNQXAC9IVRw"
  ]
}
```

### Output

One item is written per unique video as processing finishes. Existing `segments` remain available, and `transcript` is the complete ordered text joined with spaces.

| Fields | Interpretation |
|---|---|
| `videoId`, `videoUrl`, `title` | Video identity, canonical URL and public title when available. |
| `segments`, `segmentCount` | Complete selected caption track with start, duration and text per segment. |
| `transcript`, `wordCount`, `characterCount` | Plain text, whitespace-separated token count and Unicode character count. The word count is not a linguistic tokenizer, especially for languages without spaces. |
| `transcriptHash` | SHA-256 of the UTF-8 plain text, useful for detecting text changes; it does not include timing or metadata. |
| `languageRequested`, `languageUsed` | Requested language and actual selected track language. |
| `isAutoGenerated`, `isTranslated` | Whether the source uses automatic speech recognition and whether YouTube translated the selected track. |
| `availableLanguages` | Distinct language codes advertised by the source, including translation targets. Availability does not guarantee that a later download will succeed. The list may include codes outside the input dropdown. |
| `durationSeconds`, `channelName`, `channelId` | Public video and channel metadata when exposed. |
| `publishedAt`, `uploadDate`, `thumbnailUrl` | Source publication timestamp, upload date and image URL. Unknown values are null; a date alone is not turned into an invented timestamp. |
| `srt`, `exportWarnings` | Optional complete SRT string. If any cue lacks a usable end time, SRT is null with `SRT_UNAVAILABLE_UNKNOWN_END`; the full transcript remains available. |
| `status`, `errorCode`, `errorMessage`, `attemptCount` | Result status and number of extraction attempts: normally 1, at most 2, or 0 when the run budget prevents processing. |

Use `overview` for monitoring, `transcript` for the complete segment array, `rag` for plain text and provenance, and `subtitles` for SRT. JSON preserves nested arrays and multiline SRT most naturally; Apify also supports CSV, Excel and other Dataset exports. SRT is a field in the item, not a separate downloadable file.

This is the complete Dataset item from a real successful run:

```json
{
  "videoId": "jNQXAC9IVRw",
  "videoUrl": "https://www.youtube.com/watch?v=jNQXAC9IVRw",
  "status": "success",
  "title": "Me at the zoo",
  "languageRequested": "en",
  "segmentCount": 6,
  "segments": [
    {
      "start": 1.2,
      "duration": 2.16,
      "text": "All right, so here we are, in front of the elephants"
    },
    {
      "start": 5.318,
      "duration": 2.656,
      "text": "the cool thing about these guys is that they have really..."
    },
    {
      "start": 7.974,
      "duration": 4.642,
      "text": "really really long trunks"
    },
    {
      "start": 12.616,
      "duration": 1.751,
      "text": "and that's cool"
    },
    {
      "start": 14.421,
      "duration": 1.312,
      "text": "(baaaaaaaaaaahhh!!)"
    },
    {
      "start": 16.881,
      "duration": 2,
      "text": "and that's pretty much all there is to say"
    }
  ],
  "errorCode": null,
  "errorMessage": null,
  "languageUsed": "en",
  "isAutoGenerated": false,
  "isTranslated": false,
  "availableLanguages": [
    "aa",
    "ab",
    "af",
    "ak",
    "am",
    "ar",
    "as",
    "ay",
    "az",
    "ba",
    "be",
    "bg",
    "bho",
    "bn",
    "bo",
    "br",
    "bs",
    "ca",
    "ceb",
    "co",
    "crs",
    "cs",
    "cy",
    "da",
    "de",
    "dv",
    "dz",
    "ee",
    "el",
    "en",
    "eo",
    "es",
    "et",
    "eu",
    "fa",
    "fi",
    "fil",
    "fj",
    "fo",
    "fr",
    "fy",
    "ga",
    "gaa",
    "gd",
    "gl",
    "gn",
    "gu",
    "gv",
    "ha",
    "haw",
    "he",
    "hi",
    "hmn",
    "hr",
    "ht",
    "hu",
    "hy",
    "id",
    "ig",
    "is",
    "it",
    "iu",
    "ja",
    "jv",
    "ka",
    "kha",
    "kk",
    "kl",
    "km",
    "kn",
    "ko",
    "kri",
    "ku",
    "ky",
    "la",
    "lb",
    "lg",
    "ln",
    "lo",
    "lt",
    "lua",
    "luo",
    "lv",
    "mfe",
    "mg",
    "mi",
    "mk",
    "ml",
    "mn",
    "mr",
    "ms",
    "mt",
    "my",
    "ne",
    "new",
    "nl",
    "no",
    "nso",
    "ny",
    "oc",
    "om",
    "or",
    "os",
    "pa",
    "pam",
    "pl",
    "ps",
    "pt",
    "pt-PT",
    "qu",
    "rn",
    "ro",
    "ru",
    "rw",
    "sa",
    "sd",
    "sg",
    "si",
    "sk",
    "sl",
    "sm",
    "sn",
    "so",
    "sq",
    "sr",
    "ss",
    "st",
    "su",
    "sv",
    "sw",
    "ta",
    "te",
    "tg",
    "th",
    "ti",
    "tk",
    "tn",
    "to",
    "tr",
    "ts",
    "tt",
    "tum",
    "ug",
    "uk",
    "ur",
    "uz",
    "ve",
    "vi",
    "war",
    "wo",
    "xh",
    "yi",
    "yo",
    "zh-Hans",
    "zh-Hant",
    "zu"
  ],
  "durationSeconds": 19,
  "channelName": "jawed",
  "channelId": "UC4QobU6STFB0P71PMvOGN5A",
  "publishedAt": "2005-04-24T03:31:52Z",
  "uploadDate": "2005-04-24",
  "thumbnailUrl": "https://i.ytimg.com/vi/jNQXAC9IVRw/hqdefault.jpg?sqp=-oaymwEmCOADEOgC8quKqQMa8AEB-AG-AoAC8AGKAgwIABABGFUgWShlMA8=&rs=AOn4CLA9eLBatYv9WbkD4BbZ2Im-biSPTw",
  "transcript": "All right, so here we are, in front of the elephants the cool thing about these guys is that they have really... really really long trunks and that's cool (baaaaaaaaaaahhh!!) and that's pretty much all there is to say",
  "wordCount": 39,
  "characterCount": 217,
  "transcriptHash": "083a5f4a06797b6eca382a74707c681a5094f25129bc8447dd0c3557f24ac0cf",
  "srt": "1\n00:00:01,200 --> 00:00:03,360\nAll right, so here we are, in front of the elephants\n\n2\n00:00:05,318 --> 00:00:07,974\nthe cool thing about these guys is that they have really...\n\n3\n00:00:07,974 --> 00:00:12,616\nreally really long trunks\n\n4\n00:00:12,616 --> 00:00:14,367\nand that's cool\n\n5\n00:00:14,421 --> 00:00:15,733\n(baaaaaaaaaaahhh!!)\n\n6\n00:00:16,881 --> 00:00:18,881\nand that's pretty much all there is to say\n",
  "exportWarnings": [],
  "attemptCount": 1
}
```

Error items have empty `segments` and `transcript`, zero counts, and null hash and SRT. Available metadata can remain present. Common codes include `NO_TRANSCRIPT`, `MUSIC_ONLY_CAPTIONS`, `LANGUAGE_UNAVAILABLE`, `MANUAL_TRANSCRIPT_UNAVAILABLE`, `VIDEO_UNAVAILABLE`, `LIVE_TRANSCRIPT_UNAVAILABLE`, `TRANSCRIPT_REQUEST_FAILED`, `REQUEST_TIMEOUT`, `REQUEST_FAILED`, `INCOMPLETE_TRANSCRIPT` and `CHARGE_LIMIT_REACHED`.

### Pricing

One event is charged per complete successful transcript delivered to the Dataset. Plain text, metadata and optional SRT are included in that same event. Tracks containing only music cues return `MUSIC_ONLY_CAPTIONS` and are not charged as transcript events. Other error items, unavailable or missing-language results, empty or rejected incomplete transcripts, and charge-limit results are also not charged as transcript events.

| Apify tier | Price per complete transcript | Equivalent per 1,000 complete transcripts |
|---|---:|---:|
| Free | $0.003000 | $3.00 |
| Bronze | $0.002700 | $2.70 |
| Silver | $0.002400 | $2.40 |
| Gold | $0.002250 | $2.25 |
| Platinum | $0.002250 | $2.25 |
| Diamond | $0.002250 | $2.25 |

The billing unit is one delivered transcript; the 1,000-unit column is a comparison equivalent. Apify resolves the applicable account tier. A transcript charge is recorded after the complete item is written. See the Pricing tab and your account terms for any additional platform charges.

### Related Actors

| Actor | Best for |
|---|---|
| [Social Blade Multi-Platform Analytics Scraper](https://apify.com/datascraperes/socialblade-scraper) | Add public creator audience, views, rankings and growth metrics to YouTube content research. |
| [PDF Text Extractor - OCR, Markdown & JSON](https://apify.com/datascraperes/pdf-text-extractor) | Add text extracted from documents to a search or RAG workflow that also uses video transcripts. |

### Limits and data quality

The limit is 5,000 input video URLs, not 5,000 segments per video. Processing is sequential with results saved as they finish; allow enough run time and budget for large inputs. A run stopped or timed out by the platform contains only the results already written.

“Complete” means all text-bearing segments of the selected caption response, not a guarantee that the source captions cover every spoken word. The Actor does not transcribe audio or correct recognition errors. A track made only of recognizable music cues is reported as `MUSIC_ONLY_CAPTIONS` with no transcript charge. When a track also has written words, its music cues, intentional repetitions and overlaps are preserved. Whitespace is normalized without summarizing the text.

Ongoing, upcoming or still-processing live streams return `LIVE_TRANSCRIPT_UNAVAILABLE` because their captions cannot yet represent a finished video. Finished recordings can be processed when captions are available. Private, removed, age-restricted or network-restricted videos may fail.

No successful transcript is truncated to fit a size limit. Caption responses above 16 MB or complete serialized results above the 9 MB safety budget return `INCOMPLETE_TRANSCRIPT` without partial text. Optional SRT increases result size; disabling it can help a large result fit while keeping all transcript segments.

Source durations are preserved when supplied. Otherwise the next segment start is used; a final unknown duration is zero. SRT is withheld if any cue has no positive usable duration; no end time is invented. SRT text escapes literal angle brackets and ampersands for subtitle rendering.

Temporary request failures receive at most two extraction attempts. When a caption response is rate-limited, the Actor may also try another format of the same caption track and language; it never silently switches languages. YouTube rate limits and caption availability can still cause errors. Review error rows and retry those videos later rather than treating a successful run status as proof that every video succeeded.

### Frequently asked questions

#### What happens if my language is unavailable?

The item has `LANGUAGE_UNAVAILABLE`, with advertised languages when available. A base language may use a matching regional track, but an unrelated language is never silently substituted. YouTube-translated tracks can be used under `any` and are labeled `isTranslated: true`; this is not an independent translation service.

#### Can I require human-provided subtitles?

Yes. Select `manual_only`. If no matching manual track exists, the result has `MANUAL_TRANSCRIPT_UNAVAILABLE` when a matching automatic or translated alternative is advertised. If no track in the language exists, it has `LANGUAGE_UNAVAILABLE`.

#### Does this download video or generate AI summaries?

No. It retrieves available captions and metadata, not video or audio media. It does not generate summaries, embeddings or speech-to-text transcripts. Plain text and timestamps can be used in your own downstream tools.

#### Why is SRT null on a successful transcript?

SRT is disabled by default. If enabled but a cue lacks a usable end time, `exportWarnings` explains why SRT could not be produced. All transcript text and segments remain intact.

#### What happens when the run budget is exhausted?

Remaining videos receive `CHARGE_LIMIT_REACHED` without transcript content. No new caption request is made when the remaining budget is insufficient before processing starts.

### Responsible use

Use public content in accordance with applicable source terms, copyright and privacy requirements. You are responsible for ensuring that retrieval and reuse of transcript text is lawful and appropriately attributed where required.

### Support

For unexpected results, open an issue in the Actor's **Issues** tab with the run ID, a small reproducible public video URL, selected language and error code. Do not include credentials, cookies or private content.

For web usage, API examples and a sanitized sample Dataset, see the [YouTube transcript scraper repository](https://github.com/datacrawler-edu/youtube-transcript-scraper-python).

# Actor input Schema

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

Public YouTube watch, youtu.be, Shorts, or embed URLs. Duplicate video IDs are processed once in the order first seen.

## `language` (type: `string`):

Choose the requested caption language. Base codes also match regional variants; the actual track is reported as languageUsed. No fallback to unrelated languages.

## `captionPolicy` (type: `string`):

Prefer manual captions, allowing automatic captions and YouTube-translated tracks if needed (any), or require a manually provided track (manual\_only). The returned isAutoGenerated and isTranslated fields identify the selected source.

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

Add a complete SRT string from the same timestamped segments. If an end time is unknown, srt is null with an export warning; the complete transcript is still returned. No additional transcript charge.

## Actor input object example

```json
{
  "videoUrls": [
    "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
  ],
  "language": "en",
  "captionPolicy": "any",
  "includeSrt": false
}
```

# Actor output Schema

## `results` (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 = {
    "videoUrls": [
        "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
    ],
    "language": "en",
    "captionPolicy": "any",
    "includeSrt": false
};

// Run the Actor and wait for it to finish
const run = await client.actor("datascraperes/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"],
    "language": "en",
    "captionPolicy": "any",
    "includeSrt": False,
}

# Run the Actor and wait for it to finish
run = client.actor("datascraperes/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"
  ],
  "language": "en",
  "captionPolicy": "any",
  "includeSrt": false
}' |
apify call datascraperes/youtube-transcript-scraper --silent --output-dataset

```

## MCP server setup

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