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

Get transcripts from YouTube videos, Shorts, live streams, channels, playlists and search results as text, timestamped segments, SRT or WebVTT. No login or API key. You are charged only for rows that return a transcript.

- **URL**: https://apify.com/rastriq/youtube-transcript-scraper.md
- **Developed by:** [Rastriq — Structured data from the world](https://apify.com/rastriq) (community)
- **Categories:** Videos, Social media, AI
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $1.20 / 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

**YouTube Transcript Scraper** extracts captions from YouTube videos, Shorts, live streams, channels, playlists and search results, and returns them as clean text, timestamped segments, SRT or WebVTT. No API key, no login, no browser. Export to JSON, CSV or Excel.

> 🚀 **First test:** keep the prefilled input (one video and the TED channel, 5 videos) and click **Start**. You only pay for rows that return a transcript.

### 🎯 Why use YouTube Transcript Scraper?

- 🤖 **LLM and RAG pipelines**: feed transcripts to an LLM or a vector store. The timestamped text format (`[00:01:23] ...`) is made for prompts and lets you cite the exact moment.
- 🔎 **Content research**: pull every talk from a channel, playlist or search query and analyze topics, quotes and keywords in bulk.
- 📈 **SEO and content repurposing**: turn videos into blog posts, summaries, chapters and subtitle files.
- ♿ **Accessibility**: export ready-to-use SRT and WebVTT files for subtitles.
- 📡 **Channel monitoring**: schedule runs with `publishedAfter` set to `7 days` to capture new uploads.

### 📋 What data does YouTube Transcript Scraper extract?

| Group | Fields |
|---|---|
| **Video** | `videoId`, `url`, `title`, `durationSeconds`, `viewCount`, `isLive` (live right now), `isLiveContent` (live stream or past stream) |
| **Channel** | `channelName`, `channelId`, `channelUrl` |
| **Transcript** | `transcriptStatus`, `transcriptLanguage`, `originalLanguage`, `isAutoGenerated`, `segmentCount`, `wordCount` |
| **Languages** | `availableLanguages` (code, name, isAutoGenerated for each track) |
| **Transcript formats** (you choose) | plain text, segments with `start` / `duration` / `end`, timestamped text `[hh:mm:ss] ...`, SRT, WebVTT |
| **Metadata** (on by default) | `publishedAt`, `likeCount`, `description`, `keywords`, `thumbnailUrl` |

### ⚙️ Input

| Parameter | Type | Default | Description |
|---|---|---|---|
| `urls` | array | prefilled | Video, Shorts or live URLs, `youtu.be` links, bare video IDs, channel URLs (`@handle`, `/channel/UC...`; add `/shorts` or `/streams` to pick a tab) and playlist URLs. Mix them freely. |
| `searchQueries` | array | none | Transcribe the top videos for each YouTube search query. |
| `maxVideosPerSource` | integer | 20 | Limit per channel, playlist or search. Direct video URLs are not limited. |
| `maxVideos` | integer | 0 | Hard cap for the whole run. 0 means no cap. |
| `channelTabs` | array | `videos` | `videos`, `shorts`, `streams`. Used when a channel URL has no tab. |
| `publishedAfter` / `publishedBefore` | string | none | `YYYY-MM-DD` or relative (`30 days`). Applies to channels, playlists and searches. Channels stop paging once older videos are reached. |
| `languages` | array | `en` in the prefill | Language codes in order of preference. The first available one wins. |
| `fallbackToAnyLanguage` | boolean | `true` | If no preferred language exists, return the default track instead of a `LANGUAGE_NOT_AVAILABLE` row. |
| `preferAutoGenerated` | boolean | `false` | By default, creator-uploaded captions win over auto-generated ones. |
| `outputFormats` | array | `text`, `segments` | Also available: `timestamped`, `srt`, `vtt`. |
| `includeMetadata` | boolean | `true` | Adds publish date, likes, description, keywords and thumbnail. |
| `maxConcurrency` | integer | 5 | Videos processed in parallel (1-20). |
| `proxyConfiguration` | object | Apify residential | Required by YouTube. Its cost is included in the price. |

**Single video, plain text:**

```json
{
  "urls": ["https://www.youtube.com/watch?v=jNQXAC9IVRw"],
  "outputFormats": ["text"]
}
```

**A channel, last 30 days, Spanish preferred, with SRT:**

```json
{
  "urls": ["https://www.youtube.com/@TED"],
  "publishedAfter": "30 days",
  "languages": ["es", "en"],
  "outputFormats": ["text", "srt"],
  "maxVideosPerSource": 100
}
```

### 📦 Output example

Real row from a run on the TED channel (description and text shortened):

```json
{
  "videoId": "PpEUTpzWt2M",
  "url": "https://www.youtube.com/watch?v=PpEUTpzWt2M",
  "inputSource": "https://www.youtube.com/@TED",
  "title": "How the Wealth Gap Is Reshaping Politics | Xochitl Gonzalez | TED",
  "channelName": "TED",
  "channelId": "UCAuUUnT6oDeKwE6v1NGQxug",
  "channelUrl": "https://www.youtube.com/channel/UCAuUUnT6oDeKwE6v1NGQxug",
  "durationSeconds": 636,
  "viewCount": 98795,
  "isLive": false,
  "publishedAt": "2026-10-13",
  "likeCount": 652,
  "description": "Wealth inequality gets talked about as an economic problem. Writer Xochitl Gonzalez makes the case that it's a civic one, too ...",
  "keywords": ["TEDTalk", "TEDTalks", "TED"],
  "thumbnailUrl": "https://i.ytimg.com/vi/PpEUTpzWt2M/sddefault.jpg",
  "availableLanguages": [
    { "code": "en", "name": "English (auto-generated)", "isAutoGenerated": true }
  ],
  "transcriptStatus": "OK",
  "transcriptLanguage": "en",
  "originalLanguage": "en",
  "isAutoGenerated": true,
  "segmentCount": 275,
  "wordCount": 1703,
  "text": "I'm here to tell you a story about getting a cup of coffee. Or I should say attempting to get a cup of coffee because when I went to pay for it, I couldn't. ..."
}
```

### 🚦 Status per row

Every video the actor processes produces a row with a `transcriptStatus`; invalid URLs and channels that cannot be found get an `INVALID_INPUT` row. Videos skipped by your date filter or limits are not listed.

| Status | Meaning | Charged |
|---|---|---|
| `OK` | Transcript returned | Yes |
| `NO_TRANSCRIPT` | The video has no captions | No |
| `LANGUAGE_NOT_AVAILABLE` | None of your preferred languages exist and fallback is off | No |
| `UNAVAILABLE` | Private, removed, age-restricted or geo-blocked | No |
| `BLOCKED` | YouTube blocked the request | No |
| `ERROR` | Unexpected error | No |
| `INVALID_INPUT` | The input is not a recognizable YouTube URL, ID or query | No |

### 💰 How much does YouTube Transcript Scraper cost?

YouTube Transcript Scraper uses **pay per event** pricing: you pay per transcript returned.

- **$2.00 per 1,000 transcripts** on the Free plan. Paid plans get a lower rate, so see the Pricing tab for your plan's rate.
- **No charge** for rows without a transcript (`NO_TRANSCRIPT`, `UNAVAILABLE`, and the other non-OK statuses).
- **No start fee.** The residential proxy cost is included.

**Example:** 500 transcripts = 500 x $2.00 / 1,000 = **$1.00**.

**Speed:** in a run measured on Apify (15 Oct 2026), 315 videos finished in about 3 minutes at 256 MB with concurrency 8, with 0 errors. About 94 % of the videos returned a transcript. The rest had no captions or were unavailable, and those rows were free.

### 🔌 API usage

Run YouTube Transcript Scraper from your code with the Apify API.

```bash
curl -X POST "https://api.apify.com/v2/acts/rastriq~youtube-transcript-scraper/run-sync-get-dataset-items?token=YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"urls": ["https://www.youtube.com/watch?v=jNQXAC9IVRw"], "outputFormats": ["text"]}'
```

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_API_TOKEN")
run = client.actor("rastriq/youtube-transcript-scraper").call(run_input={
    "urls": ["https://www.youtube.com/@TED"],
    "publishedAfter": "30 days",
    "languages": ["en"],
    "outputFormats": ["timestamped"],
})
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
    if row["transcriptStatus"] == "OK":
        print(row["title"], row["wordCount"])
```

You can also use schedules, webhooks, Make, Zapier, n8n and Google Sheets, or call the actor from AI agents through the Apify MCP server.

### ❓ FAQ

**What if a video has no captions?**
You get a `NO_TRANSCRIPT` row at no cost. The actor does not generate transcripts from audio (no speech-to-text), and it does not offer machine translation.

**Auto-generated or creator-uploaded captions?**
Creator-uploaded captions are preferred by default. Set `preferAutoGenerated` to `true` to reverse that. The `isAutoGenerated` field tells you which one you got.

**How do languages work?**
List codes in order, for example `["es", "en"]`. The first available one wins. If none exists, the default track is returned (turn off `fallbackToAnyLanguage` to get a `LANGUAGE_NOT_AVAILABLE` row instead). `availableLanguages` lists every track of the video.

**Does it work with Shorts and live streams?**
Yes. Shorts and live URLs work as inputs, and you can add `/shorts` or `/streams` to a channel URL or use `channelTabs`.

**Can it transcribe age-restricted videos?**
No. They require a login, which this actor does not use, so they return `UNAVAILABLE` and are not charged.

**How do I control volume and cost?**
Set `maxVideosPerSource` for each channel, playlist or search, and `maxVideos` for the whole run. You can also set a maximum cost per run in the Apify run options, so a run stops when it reaches your budget.

**Is it legal?**
The actor reads publicly available captions only. You are responsible for complying with YouTube's Terms of Service, copyright and data protection law when you use the output. This actor is not affiliated with or endorsed by YouTube or Google.

**Where do I get support?**
Open a ticket in the Issues tab and include the run ID.

### 🔗 Related Rastriq actors

| Actor | What it does |
|---|---|
| [TikTok Ad Library Scraper](https://apify.com/rastriq/tiktok-ad-library-scraper) | Extract ads from the TikTok ad library |
| [Google Ads Transparency Scraper](https://apify.com/rastriq/google-ads-scraper) | Extract ads from Google Ads Transparency Center |
| [Facebook Ad Library Scraper](https://apify.com/rastriq/fb-adlibrary-scraper) | Extract ads from the Facebook Ad Library |

# Actor input Schema

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

Video, Shorts or live URLs, video IDs, channel URLs (@handle, /channel/UC..., add /shorts or /streams to pick a tab) and playlist URLs. Mix them freely.

## `searchQueries` (type: `array`):

Optional. Transcribe the top videos for each YouTube search query.

## `maxVideosPerSource` (type: `integer`):

Direct video URLs are not limited by this.

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

Hard cap for the whole run. 0 = no cap.

## `channelTabs` (type: `array`):

Which tabs to read when a channel URL has no tab in it.

## `publishedAfter` (type: `string`):

YYYY-MM-DD or relative, e.g. '30 days'. Applies to channels, playlists and searches. Channels stop paging once older videos are reached.

## `publishedBefore` (type: `string`):

YYYY-MM-DD or relative, e.g. '7 days'.

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

Language codes in order of preference (e.g. es, en). The first one available wins. Leave empty for the video's default captions.

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

If none of the preferred languages exists, return the default track instead of a LANGUAGE_NOT_AVAILABLE row.

## `preferAutoGenerated` (type: `boolean`):

By default creator-uploaded captions win over auto-generated ones.

## `outputFormats` (type: `array`):

Fields added to each transcript row. Plain text and segments are on by default; timestamped text is handy for LLM prompts; SRT/WebVTT are ready-to-use subtitle files.

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

Publish date, likes, description, keywords and thumbnail. Title, channel, duration and views are always included.

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

Videos processed in parallel. 5 is a good default.

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

YouTube blocks transcript requests from datacenter IPs. Residential proxy is required and its cost is included in the price.

## Actor input object example

```json
{
  "urls": [
    "https://www.youtube.com/watch?v=jNQXAC9IVRw",
    "https://www.youtube.com/@TED"
  ],
  "maxVideosPerSource": 5,
  "maxVideos": 0,
  "channelTabs": [
    "videos"
  ],
  "languages": [
    "en"
  ],
  "fallbackToAnyLanguage": true,
  "preferAutoGenerated": false,
  "outputFormats": [
    "text",
    "segments"
  ],
  "includeMetadata": true,
  "maxConcurrency": 5,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

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

Every processed video, with transcripts and status rows.

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

Video ID, title, language and transcript text.

## `issues` (type: `string`):

Rows whose status is not OK, with the reason.

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

Counts per status, proxy sessions used and stop reason.

# 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=jNQXAC9IVRw",
        "https://www.youtube.com/@TED"
    ],
    "maxVideosPerSource": 5,
    "languages": [
        "en"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("rastriq/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 = {
    "urls": [
        "https://www.youtube.com/watch?v=jNQXAC9IVRw",
        "https://www.youtube.com/@TED",
    ],
    "maxVideosPerSource": 5,
    "languages": ["en"],
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("rastriq/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 '{
  "urls": [
    "https://www.youtube.com/watch?v=jNQXAC9IVRw",
    "https://www.youtube.com/@TED"
  ],
  "maxVideosPerSource": 5,
  "languages": [
    "en"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call rastriq/youtube-transcript-scraper --silent --output-dataset

```

## MCP server setup

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