# YouTube Transcript Scraper (`datara/youtube-transcript-scraper`) Actor

Get timestamped transcripts and captions from YouTube videos and Shorts. Pick a language or the original audio track. Pay only for transcripts delivered: videos without captions and failed lookups are free.

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

## Pricing

from $6.00 / 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.

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

Get the full transcript of any YouTube video or Short: timestamped segments, clean plain text, or both. Paste in one URL or thousands. You pay only for transcripts actually delivered.

### Why this one

- **Pay per transcript, nothing else.** Videos with no captions, private or deleted videos, bad links and temporary errors are all free. They still appear in your results with a reason, so you know what happened.
- **No surprise bills.** `Maximum videos` is a hard cap, and the actor checks your *maximum cost per run* before every request. It stops cleanly when the budget is used rather than going over.
- **Any language, including translations.** Ask for a caption language (`en`, `es`, `pt-BR` …). If the video doesn't have it, YouTube's automatic translation is returned where available and flagged with `autoTranslated: true`. Or ask for the original spoken language only, skipping auto-dubbed tracks.
- **AI-ready text.** Optionally strip caption annotations such as `[music]` and `[applause]` before the text reaches your LLM or RAG pipeline.
- **Every link format.** `watch?v=`, `youtu.be`, Shorts, live replays, embeds, mobile and YouTube Music links, or bare video IDs. Duplicates are removed before anything is fetched.
- **Fast.** Fetches several videos in parallel, retries transient errors automatically, and serves recently fetched transcripts from cache.

### Input

| Field | Description | Default |
|---|---|---|
| `videoUrls` | Video or Shorts URLs, or video IDs | required |
| `language` | Caption language code; empty means YouTube's default track | empty |
| `originalAudio` | Return captions in the original spoken language only (overrides `language`) | `false` |
| `outputFormat` | `both`, `segments` or `text` | `both` |
| `removeSoundTags` | Strip `[music]`, `[applause]` and similar annotations | `false` |
| `maxVideos` | Hard cap on videos processed per run | `100` |
| `cacheMaxAge` | Accept a cached transcript up to `1d`, `3d`, `7d`, `14d`, `30d`, or `none` for always fresh | `7d` |
| `includeFailures` | Add a free row for each video without a transcript | `true` |
| `maxConcurrency` | Parallel requests (1–20) | `5` |

Example:

```json
{
  "videoUrls": [
    "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
    "https://youtu.be/jNQXAC9IVRw",
    "https://www.youtube.com/shorts/abcdefghijk"
  ],
  "language": "en",
  "outputFormat": "both",
  "maxVideos": 50
}
```

### Output

One row per video.

```json
{
  "status": "ok",
  "videoId": "jNQXAC9IVRw",
  "url": "https://www.youtube.com/watch?v=jNQXAC9IVRw",
  "type": "video",
  "input": "https://youtu.be/jNQXAC9IVRw",
  "language": "English",
  "autoTranslated": false,
  "requestedLanguage": "en",
  "segmentCount": 6,
  "wordCount": 42,
  "durationSeconds": 19,
  "text": "All right, so here we are in front of the ...",
  "segments": [
    { "startMs": 1200, "endMs": 4100, "startTime": "0:01", "text": "All right, so here we are" }
  ],
  "scrapedAt": "2026-10-19T09:40:00.000Z"
}
```

`autoTranslated` is `true` when you asked for a language the video doesn't have and YouTube's machine translation was returned instead. In that case `language` shows the code you requested.

Videos without a transcript come back like this (free):

```json
{
  "status": "no_captions",
  "videoId": "...",
  "url": "...",
  "errorMessage": "No transcript available. The video may have no captions, or may be private, deleted or unavailable.",
  "charged": false
}
```

| `status` | Meaning | Charged |
|---|---|---|
| `ok` | Transcript delivered | Yes |
| `no_captions` | No transcript available: no captions, or the video is private, deleted or unavailable | No |
| `not_found` | Video not found | No |
| `invalid_input` | Not a YouTube video link or ID | No |
| `error` | Temporary failure after retries; safe to re-run | No |

The **Timestamped segments** view in the Output tab gives one row per segment, ready to export as CSV for subtitles or analysis. A run summary is saved to the key-value store as `SUMMARY`.

### Use cases

- Feed video content into LLMs, RAG pipelines and summarisers
- Repurpose videos into blogs, newsletters and social posts
- Search, quote and timestamp-link spoken content
- Research competitors, creators and topics at scale
- Build subtitle files and accessibility tooling

### Pricing

Pay per event: you're charged once per transcript delivered (`status: "ok"`), plus Apify's small per-run start fee. Nothing else. Set *Maximum cost per run* in the run options and the actor will never exceed it.

### Limits

- Only videos that have captions (manual or auto-generated) can return a transcript.
- Age-restricted, members-only and private videos usually have no accessible transcript; they are returned as free failures.
- Accepts individual videos. Channel and playlist URLs aren't expanded; pass video URLs instead.

### Integrations

Works with the Apify API, Make, Zapier, n8n, LangChain and the Apify MCP server. Call it from your own code:

```js
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('datara/youtube-transcript-scraper').call({
  videoUrls: ['https://www.youtube.com/watch?v=dQw4w9WgXcQ'],
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

# Actor input Schema

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

Video or Shorts URLs, one per line. Also accepts youtu.be links, /live/ and /embed/ links, or bare 11-character video IDs. Duplicates are removed automatically.

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

Caption language code, for example en, es, fr or pt-BR. Leave empty for YouTube's default track. If the video has no captions in this language, YouTube's automatic translation is returned where available (marked autoTranslated: true).

## `originalAudio` (type: `boolean`):

Return captions in the video's original spoken language, ignoring auto-dubbed or translated tracks. Overrides the Language field.

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

Timestamped segments, plain text, or both.

## `removeSoundTags` (type: `boolean`):

Strip caption annotations such as \[music], \[applause] or \[laughter] from the text and segments. Useful when feeding transcripts to AI tools.

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

Hard cap on how many videos are processed in this run. Anything beyond it is ignored, so you never pay for more than you expect.

## `cacheMaxAge` (type: `string`):

Transcripts rarely change after upload, so recently fetched copies are returned instantly. Choose 'Always fetch fresh' to bypass this.

## `includeFailures` (type: `boolean`):

Add a free row for each video that had no captions or could not be processed, with the reason. Turn off to keep only successful transcripts.

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

How many videos to fetch at once.

## Actor input object example

```json
{
  "videoUrls": [
    "https://www.youtube.com/watch?v=vQAD3JMx1_E"
  ],
  "language": "",
  "originalAudio": false,
  "outputFormat": "both",
  "removeSoundTags": false,
  "maxVideos": 100,
  "cacheMaxAge": "7d",
  "includeFailures": true,
  "maxConcurrency": 5
}
```

# Actor output Schema

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

All result rows: status, language, word count, plain-text transcript and timestamped segments.

## `segments` (type: `string`):

One row per caption segment with start and end times, for subtitles and time-coded analysis.

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

Counts of delivered transcripts, videos without captions, failures and videos skipped by the cost limit.

# 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=vQAD3JMx1_E"
    ]
};

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

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

```

## MCP server setup

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