# YouTube Transcript & Subtitles Scraper - Bulk & Channels (`skillmerc/youtube-transcript-scraper-pro`) Actor

Get YouTube transcripts and subtitles as clean text with timestamps, from videos, Shorts, playlists or whole channels. Any language. $2 per 1,000, and videos without captions are never charged.

- **URL**: https://apify.com/skillmerc/youtube-transcript-scraper-pro.md
- **Developed by:** [Kyle Pretorius](https://apify.com/skillmerc) (community)
- **Categories:** Videos, AI, Social media
- **Features:** Limited permissions
- **Stats:** 5 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.40 / 1,000 results

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 & Subtitles Scraper

Turn YouTube videos into text. Paste video, Shorts, playlist or channel URLs and get back one JSON row per video with the full transcript, timestamped segments and video details. Works with any caption language, manual or auto-generated.

**$2 per 1,000 transcripts. Videos that have no captions, or that fail, are never charged.**

### Sample output

Three real rows from a run on 22 Oct 2026 (one JSON row per video; `segments` holds the timestamped lines):

| video_id | channel | language | auto-generated | word_count | segments |
|---|---|---|---|---|---|
| jNQXAC9IVRw | jawed | en | false | 39 | 6 |
| dQw4w9WgXcQ | Rick Astley | en | false | 487 | 61 |
| 9bZkp7q19f0 | officialpsy | ko | true | 194 | 67 |

`transcript_text` for the first row starts: "All right, so here we are, in front of the elephants..."

### What you get

For each video:

- The full transcript as one clean string, ready for an LLM, a search index or a document
- Timestamped segments (`start`, `duration`, `text`) for subtitles, quotes and chapter work
- Title, channel, channel ID, duration and view count
- Which caption language you got, whether it was auto-generated, and every language the video offers

### Quick start

1. Paste one or more YouTube links into **Video URLs or IDs**.
2. Set **Language** to the caption language you want (for example `en`, `es`, `de`, `pt`, `ko`).
3. Click **Start**. Download the results as JSON, CSV, Excel or HTML, or read them through the Apify API.

#### Input example

```json
{
  "videoUrls": [
    "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
    "https://youtu.be/jNQXAC9IVRw",
    "https://www.youtube.com/shorts/VIDEO_ID",
    "https://www.youtube.com/playlist?list=PLAYLIST_ID",
    "https://www.youtube.com/@3blue1brown"
  ],
  "language": "en",
  "preferManual": true,
  "includeTimestamps": true,
  "plainText": true,
  "expandPlaylists": true,
  "maxItems": 200
}
```

Accepted links: `watch?v=`, `youtu.be/`, `/shorts/`, `/embed/`, `/live/` and bare 11-character video IDs. Playlist links (`?list=`) and channel links (`/@handle` or `/channel/UC...`) are expanded into their videos when **Expand Playlists** is on.

### Input options

| Field | Default | What it does |
|---|---|---|
| `videoUrls` | one sample video | Video links or IDs. Playlist and channel links when `expandPlaylists` is on |
| `language` | `en` | Preferred caption language code |
| `preferManual` | `true` | Use human-written captions before auto-generated ones |
| `includeTimestamps` | `true` | Return the timestamped `segments` list |
| `plainText` | `true` | Return the whole transcript as one string in `transcript_text` |
| `expandPlaylists` | `false` | Expand playlist and channel links into individual videos |
| `maxItems` | `100` | Maximum number of videos per run (up to 10,000) |

### Output example

```json
{
  "video_id": "dQw4w9WgXcQ",
  "video_url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
  "title": "Rick Astley - Never Gonna Give You Up (Official Video) (4K Remaster)",
  "channel": "Rick Astley",
  "channel_id": "UCuAXFkgsw1L7xaCfnd5JJOw",
  "duration_seconds": 213,
  "view_count": 1815620592,
  "published_at": null,
  "language": "en",
  "is_auto_generated": false,
  "available_languages": ["en", "en (auto)", "de-DE"],
  "transcript_text": "[music] We're no strangers to love You know the rules and so do I ...",
  "segments": [
    { "start": 0.0, "duration": 2.4, "text": "[music]" },
    { "start": 18.88, "duration": 3.28, "text": "We're no strangers to love" }
  ],
  "word_count": 487,
  "route": "direct",
  "error": null
}
```

#### Output fields

| Field | Description |
|---|---|
| `video_id`, `video_url` | The video |
| `title`, `channel`, `channel_id` | Video and channel details |
| `duration_seconds`, `view_count` | Length and views |
| `is_live` | True for live streams and past live streams |
| `language` | Language code of the transcript you received |
| `is_auto_generated` | True if YouTube generated the captions automatically |
| `available_languages` | Every caption track on the video, auto tracks marked `(auto)` |
| `transcript_text` | Full transcript as one string (null if `plainText` is off) |
| `segments` | List of `{start, duration, text}` (null if `includeTimestamps` is off) |
| `word_count` | Number of words in the transcript |
| `route` | Network route used for the video (`direct`, `datacenter` or `residential`) |
| `published_at` | Always null. YouTube's mobile API does not return it |
| `error` | Always null in the results. Failed videos go to a separate free list (see below) |

### Pricing

You pay $2.00 per 1,000 transcripts delivered, which is $0.002 per video. There is also a start fee of $0.00005 per GB of run memory, which is a fraction of a cent per run.

You are only charged for rows in the results dataset. Videos that can't be transcribed (no captions, private, deleted, age-restricted, or blocked by YouTube after every retry) are never written there. Instead they are listed with the reason in the run's `OUTPUT` record and in a separate `failed-videos` dataset, at no charge.

### Use cases

- **LLM and RAG pipelines.** Feed clean transcript text into ChatGPT, Claude or a vector database to summarise videos or answer questions about them.
- **Content repurposing.** Turn videos and podcasts into blog posts, newsletters, show notes and social posts.
- **Research.** Pull every talk from a channel or playlist and search, quote or analyse what was said.
- **Subtitles and quotes.** Use the timestamped segments to find the exact moment something was said.
- **SEO and competitor research.** See what topics and keywords other channels cover.

### How it works

Transcripts come from YouTube's own caption tracks through its mobile player API, not from scraping the web page. YouTube often blocks cloud servers, so when a request is refused the actor retries that video through fresh proxy IPs (datacenter first, then residential). Retries are included in the price.

In tests on 27 September 2026 it delivered 30 of 33 random videos across 7 languages. The 3 misses had no captions at all. A 3.5-hour podcast came back at 29,466 words, and transcripts matched YouTube's own transcript view at about 97%.

### Limitations

- Videos with no captions at all can't be transcribed. This is common for music videos and sports highlights. They are reported free of charge.
- If the language you ask for isn't available, you get the best track the video has (manual first, then auto-generated). The `language` field tells you which one you got.
- Channel links return the channel's latest uploads from its Videos tab.
- A link that contains both a video and a playlist (`watch?v=...&list=...`) is treated as that single video. Use the `playlist?list=` link to get the whole playlist.

### Calling it from code or AI agents

Run it from the Apify API, the JavaScript or Python clients, Make, Zapier or n8n, or from AI agents through Apify's MCP server. Each run returns a dataset you can read as JSON or CSV.

# Actor input Schema

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

YouTube video URLs or IDs. Also accepts playlist and channel URLs when expandPlaylists is true.

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

Preferred caption language code, e.g. en, es, de. Falls back to auto-generated then to the first available.

## `preferManual` (type: `boolean`):

Prefer human-written captions over auto-generated (ASR)

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

Include per-segment start and duration in output

## `plainText` (type: `boolean`):

Also return the whole transcript as one cleaned string

## `expandPlaylists` (type: `boolean`):

Expand playlist/channel URLs into their individual videos

## `maxItems` (type: `integer`):

Maximum number of videos to process

## Actor input object example

```json
{
  "videoUrls": [
    "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
  ],
  "language": "en",
  "preferManual": true,
  "includeTimestamps": true,
  "plainText": true,
  "expandPlaylists": false,
  "maxItems": 100
}
```

# 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",
    "maxItems": 100
};

// Run the Actor and wait for it to finish
const run = await client.actor("skillmerc/youtube-transcript-scraper-pro").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",
    "maxItems": 100,
}

# Run the Actor and wait for it to finish
run = client.actor("skillmerc/youtube-transcript-scraper-pro").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",
  "maxItems": 100
}' |
apify call skillmerc/youtube-transcript-scraper-pro --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,skillmerc/youtube-transcript-scraper-pro"
        }
    }
}
```

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/5HpQ7CK3huXPLHLkc/builds/IsgwyqJfwhXfmcT15/openapi.json
