# YouTube Transcript Scraper: Channels, Playlists, SRT, Text (`datagleaner/youtube-transcript-scraper`) Actor

YouTube transcript scraper and API: get captions for any video, Short, channel, playlist or search as text, timestamped segments or SRT. $3 per 1,000 transcripts, charged only when a transcript is returned. No API key, no login.

- **URL**: https://apify.com/datagleaner/youtube-transcript-scraper.md
- **Developed by:** [Data Gleaner](https://apify.com/datagleaner) (community)
- **Categories:** Videos, AI, For creators
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.40 / 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: captions, SRT and timestamps for videos, channels and playlists

**Paste YouTube video links, a channel, a playlist or a search phrase and get the transcript of every video as clean text, timestamped segments or an SRT subtitle file, plus title, channel, views and publish date.** It reads the captions YouTube already holds, so there is no API key, no login and no audio transcription. **$2 per 1,000 transcripts, and you are charged only for videos that return a transcript.**

#### Why this one

- **A clear price you can read in one line.** $0.002 per video that returns a transcript, with the proxy cost included. Videos with no captions, unavailable videos and errors cost nothing.
- **A video without captions gives an answer, not silence.** You get a row with `error: "no-captions"` (or `unavailable`, `blocked`) so a missing transcript is visible in your data, and it is never charged.
- **Built for YouTube blocking Apify's servers.** Every video is tried directly first; if YouTube refuses, the Actor retries up to 3 times through Apify's residential proxy by itself, at no extra charge.
- **Whole channels, playlists and searches.** `channelUrl`, `playlistUrl` and `searchQuery` list the videos, then fetch each transcript; `publishedAfter` and `publishedBefore` narrow them. Duplicates are fetched once.
- **Scheduled monitoring.** With `onlyNew`, a daily run delivers only videos you have not received yet.
- **Your language when it exists.** Set `language`; if the video has no such track the original or auto-generated one comes back and `languageFallback` says so.

#### Minimal input

```json
{"videos": ["https://www.youtube.com/watch?v=dQw4w9WgXcQ"], "outputFormat": "text"}
```

Leave everything empty to run three sample videos.

#### Sample output row

A real row from a platform run on 2026-10-11 (long fields shortened; `outputFormat: "segments"`). `likeCount` is `null` when YouTube does not show it.

```json
{
  "videoId": "wsstlwuE5XY",
  "url": "https://www.youtube.com/watch?v=wsstlwuE5XY",
  "title": "How to Clean Up the World’s Dirtiest Industry | Joojin Kim | TED",
  "channel": "TED",
  "channelId": "UCAuUUnT6oDeKwE6v1NGQxug",
  "channelHandle": "@TED",
  "channelUrl": "https://www.youtube.com/@TED",
  "subscriberCount": 27900000,
  "publishedAt": "2026-10-09",
  "durationSeconds": 459,
  "viewCount": 64551,
  "likeCount": null,
  "isLive": false,
  "description": "Climate strategist Joojin Kim spent years convincing banks across South Korea to...",
  "language": "en",
  "isAutoGenerated": false,
  "languageFallback": false,
  "availableLanguages": [
    {
      "languageCode": "ar",
      "name": "Arabic",
      "isAutoGenerated": false
    },
    {
      "languageCode": "my",
      "name": "Burmese",
      "isAutoGenerated": false
    }
  ],
  "transcriptText": "For those of us who are worried about climate change, it's no doubt challenging times. It feels like the end of the light of the tunnel gets...",
  "wordCount": 999,
  "source": "channel:https://www.youtube.com/@TED",
  "scrapedAt": "2026-10-11T06:10:46Z"
}
```

#### Price

**$2 per 1,000 transcripts** ($0.002 each). A channel of 200 videos where 180 have captions costs 180 x $0.002 = $0.36; a 1,000-video playlist with captions on every video costs $2.00. Set a maximum charge and the run stops cleanly when it is reached.

### What you get

- One item per video with title, channel (`channelHandle`, `channelUrl`, `subscriberCount`, `channelThumbnail`), publish date, duration, `viewCount`, `likeCount`, `description`, `keywords`, `thumbnailUrl`, the language used, whether it is auto-generated, every language available, the full transcript text and the word count.
- Timed `segments` (`start`, `duration` and `end` in seconds) and an `srt` string when you ask for them.
- Your preferred language when the video has it. When it does not, the video's original (or auto-generated) track is used and the item says so in `language` and `languageFallback`.
- Several routes to YouTube with automatic fallback and a fresh session on retry, because reliability is what a transcript scraper is for.

### Use cases

- Feed videos into an LLM or a RAG index (summaries, Q\&A, search over a channel).
- Research and content analysis across a whole channel, playlist or topic search.
- Subtitles (SRT) for editing, translation or accessibility.
- Quote and keyword monitoring across creators.

### Input

```json
{
  "videos": ["https://www.youtube.com/watch?v=dQw4w9WgXcQ", "https://www.youtube.com/shorts/jNQXAC9IVRw", "arj7oStGLkU"],
  "channelUrl": "https://www.youtube.com/@TED",
  "maxVideos": 10,
  "language": "en",
  "includeTimestamps": true,
  "outputFormat": "segments"
}
```

| Field | What it does |
| --- | --- |
| `videos` | Video URLs (watch, youtu.be, Shorts, embed, live) or 11-character IDs. Channel and playlist URLs pasted here are listed too. Also accepted as `startUrls`, `youtube_url`, `youtube_urls`, `videoUrls` or `urls`. |
| `channelUrl`, `playlistUrl`, `searchQuery` | List videos from a channel, playlist or YouTube search, then fetch each transcript. Use any mix with `videos`; duplicates are fetched once. `channel_url`, `playlist_url` and `search_query` are also accepted. |
| `maxVideos` | Videos taken from each of the channel, playlist and search (default 10, max 1000; `max_videos` also accepted). Does not limit `videos`. |
| `language` | Preferred language code, default `en`. |
| `includeTimestamps` | `start`, `duration` and `end` on each segment (default on). Off gives segments with the text only. |
| `outputFormat` | `segments` (default), `text`, `srt` or `all`. `transcriptText` is always included. |
| `proxyConfiguration` | Optional. Leave empty: blocked videos switch to Apify residential proxy automatically at no extra charge; see Limits. |
| `maxConcurrency` | Videos fetched at once (default 5, max 20). |
| `delaySecs` | Random start delay per video, up to this many seconds (default 0.3). |
| `requestTimeoutSecs` | Wait for one YouTube response (default 25). |

Run it with nothing filled in and it fetches three sample videos in a few seconds.

### Output

```json
{
  "videoId": "jNQXAC9IVRw",
  "url": "https://www.youtube.com/watch?v=jNQXAC9IVRw",
  "title": "Me at the zoo",
  "channel": "jawed",
  "channelId": "UC4QobU6STFB0P71PMvOGN5A",
  "channelHandle": "jawed",
  "channelUrl": "https://www.youtube.com/@jawed",
  "subscriberCount": 3470000,
  "channelThumbnail": "https://yt3.ggpht.com/...",
  "publishedAt": "2005-04-23",
  "durationSeconds": 19,
  "viewCount": 441370197,
  "likeCount": 15000000,
  "isLive": false,
  "description": "The first video on YouTube. ...",
  "keywords": [],
  "thumbnailUrl": "https://i.ytimg.com/vi/jNQXAC9IVRw/...",
  "language": "en",
  "isAutoGenerated": false,
  "requestedLanguage": "en",
  "languageFallback": false,
  "availableLanguages": [{"languageCode": "en", "name": "English", "isAutoGenerated": false}],
  "transcriptText": "All right, so here we are, in front of the elephants the cool thing about these guys is ...",
  "segments": [{"text": "All right, so here we are, in front of the elephants", "start": 1.2, "duration": 2.16, "end": 3.36}],
  "wordCount": 39,
  "route": "android",
  "source": "video",
  "scrapedAt": "2026-10-08T16:23:22Z"
}
```

A video without captions returns `{"videoId": "...", "error": "no-captions", ...}`. Other error codes: `unavailable` (private, removed or age-gated), `blocked` (YouTube refused every route; retry or add a proxy), `invalid-input`, `no-videos`, and `error` for an unexpected failure on one video. Error items are never charged.

### Pricing

Pay per event, proxy included: **$0.002 per video that returns a transcript** (event `transcript`). No charge for videos without captions, unavailable videos, or errors.

Worked example: a channel of 200 videos where 180 have captions costs 180 x $0.002 = **$0.36**. A 1,000-video playlist where every video has captions costs **$2.00**.

Set a maximum charge on the run and it stops cleanly when the budget is used.

### Limits

- Needs captions. Videos with no uploaded or auto-generated captions return `no-captions`.
- `publishedAt` is a date (YouTube shows the day only). It can be `null` when YouTube does not answer that one extra request; the transcript is still returned.
- YouTube blocks many datacenter IP addresses, which includes Apify's own servers. Every video is tried directly first. A video that gets blocked is retried through Apify's residential proxy, up to 3 times with a new IP each time. The proxy fee is included in the $0.002 price; you pay nothing extra for it. If you set your own `proxyConfiguration`, that proxy is used for every request and the automatic residential retry is off.
- Live streams in progress and members-only videos have no transcript available.

### FAQ

**Is a residential proxy used, and do I pay for it?** For some videos, yes. Videos that YouTube blocks on the first direct try are fetched through Apify's residential proxy, which is what makes the success rate high. The price per transcript already includes it, and videos without a transcript are never charged.

**Do I need a YouTube API key?** No.

**Does it download audio or transcribe speech?** No. It returns the captions YouTube already holds, so it is fast and cheap.

**Why is `languageFallback` true?** The video has no track in your preferred language, so the original or auto-generated one was returned. `language` tells you which.

**Does it accept Shorts?** Yes, Shorts URLs and IDs work like any video.

### Related Actors

- [YouTube Search Scraper](https://apify.com/datagleaner/youtube-search-scraper): find the videos first. Search YouTube by keyword with date, length and sort filters, get views, likes and descriptions, then pass the video URLs to this Actor for the transcripts.
- [YouTube Shorts & Channel Scraper](https://apify.com/datagleaner/youtube-channel-scraper): list every video and Short of a channel, most viewed or newest first, then pass the `videoId` values to this Actor for the transcripts.
- [YouTube Channel Email & Influencer Contacts Finder](https://apify.com/datagleaner/youtube-channel-contacts): public emails, websites and socials for the channels behind the videos.

### Responsible use

This Actor reads public caption data that YouTube shows to anyone. Respect creators' rights and YouTube's terms for how you use transcripts, and handle any personal data in them under the laws that apply to you; those obligations rest with you.

# Actor input Schema

## `videos` (type: `array`):

Video URLs (watch, youtu.be, Shorts, embed, live) or 11-character video IDs, one per line. A channel or playlist URL pasted here works too. Leave every source empty to run three sample videos.

## `channelUrl` (type: `string`):

Optional. A channel URL, @handle or channel ID (UC...). The latest videos are listed, then each transcript is fetched.

## `playlistUrl` (type: `string`):

Optional. A playlist URL (with `list=`) or playlist ID. Videos are listed in playlist order.

## `searchQuery` (type: `string`):

Optional. Search YouTube for videos and fetch the transcript of each result.

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

How many videos to take from the channel, the playlist and the search (each one separately). Does not limit the `videos` list.

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

Optional. Only videos published on or after this day, for channel, playlist and search sources (the `videos` list is never filtered). A date such as `2026-01-31` or a relative age such as `7 days`. Videos outside the range are skipped and not charged. Max videos then counts matching videos only. A `1 day` window covers yesterday and today, so on daily runs turn on `onlyNew` to avoid receiving (and paying for) the same video twice.

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

Optional. Only videos published on or before this day, for channel, playlist and search sources. A date such as `2026-03-31` or a relative age such as `30 days`. Videos outside the range are skipped and not charged.

## `onlyNew` (type: `boolean`):

For scheduled runs. Remembers every video this Actor delivered for each channel, playlist, search or video list and skips those next time, before any request and before any charge. Videos with no captions yet are not remembered, so they are retried on the next run. Ignored when no source is given (the sample run).

## `stateKey` (type: `string`):

Only used with `onlyNew`. Leave empty to use the name `default`. Runs that share a name share their memory of delivered videos; give each monitor its own name (for example `ai-channels`).

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

Language code of the transcript you want, such as `en`, `es`, `de`, `ja` or `pt-BR`. Without it the video's original or auto-generated track is used, and the item says so in `language` and `languageFallback`.

## `includeTimestamps` (type: `boolean`):

Put `start` and `duration` (in seconds) on every segment. Turn off to get segments with the text only.

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

`transcriptText` is always included. Segments add a timed list; SRT adds a subtitle file as one string.

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

Optional. Leave it empty: direct requests come first, and if YouTube blocks them the Actor switches to Apify residential proxy by itself, at no extra charge to you. A proxy set here is used for every request instead.

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

Videos fetched at the same time.

## `delaySecs` (type: `number`):

Each video waits a random time up to this long before its first request, to spread the load.

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

How long to wait for one YouTube response.

## Actor input object example

```json
{
  "videos": [
    "https://www.youtube.com/watch?v=jNQXAC9IVRw",
    "https://youtu.be/dQw4w9WgXcQ",
    "https://www.youtube.com/watch?v=arj7oStGLkU"
  ],
  "maxVideos": 10,
  "onlyNew": false,
  "language": "en",
  "includeTimestamps": true,
  "outputFormat": "segments",
  "maxConcurrency": 5,
  "delaySecs": 0.3,
  "requestTimeoutSecs": 25
}
```

# 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 = {
    "videos": [
        "https://www.youtube.com/watch?v=jNQXAC9IVRw",
        "https://youtu.be/dQw4w9WgXcQ",
        "https://www.youtube.com/watch?v=arj7oStGLkU"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("datagleaner/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 = { "videos": [
        "https://www.youtube.com/watch?v=jNQXAC9IVRw",
        "https://youtu.be/dQw4w9WgXcQ",
        "https://www.youtube.com/watch?v=arj7oStGLkU",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("datagleaner/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 '{
  "videos": [
    "https://www.youtube.com/watch?v=jNQXAC9IVRw",
    "https://youtu.be/dQw4w9WgXcQ",
    "https://www.youtube.com/watch?v=arj7oStGLkU"
  ]
}' |
apify call datagleaner/youtube-transcript-scraper --silent --output-dataset

```

## MCP server setup

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