# YouTube Transcript Scraper: Captions & Subtitles to Text (`santiagomv/youtube-transcripts`) Actor

Get transcripts of YouTube videos, Shorts, live replays, playlists and channels as clean text, timestamped lines or timed segments. Pick the language and human or auto-generated captions. Title, channel, views and duration included. No API key or login.

- **URL**: https://apify.com/santiagomv/youtube-transcripts.md
- **Developed by:** [Santiago Marcos](https://apify.com/santiagomv) (community)
- **Categories:** Videos, AI, Social media
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$4.00 / 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.

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 and subtitles to text

Get the transcript of any **YouTube video, Short or live replay**, or of the videos in a **playlist or channel**, as clean text. Paste links and get one row per video with:

- the **full transcript** as one plain-text block, ready for an LLM, search index or document,
- the same transcript as **timestamped lines** (`[01:05] and that's the idea`),
- optional **timed segments** (start and duration in seconds) for subtitles, clipping or search,
- the video's **title, channel, views, duration, description, keywords and thumbnail**,
- **every caption language** the video has, so you know what else you can request.

No YouTube API key, no Google account, no browser. The actor's event fee applies only to transcripts you actually get.

### Why this one

- **Human captions first.** When the creator uploaded captions, you get those. Otherwise you get YouTube's auto-generated captions, joined into clean sentences instead of word fragments. Prefer auto-generated? Flip one switch.
- **Any language, any regional variant.** Ask for `en` and you also get `en-GB` or `en-US`; ask for `es` and you get `es-419` if that's what the video has. If your language isn't available, you get the video's default captions and the `language` field tells you which.
- **Every link format.** `watch?v=`, `youtu.be/`, `/shorts/`, `/embed/`, `/live/`, `music.youtube.com`, or the bare 11-character ID.
- **Playlists and channels.** Paste a playlist or channel link (`@handle` or `/channel/UC…`) and get transcripts of its videos, with hard caps on videos and pages so a big channel can't run away with your budget.
- **No charge for failures.** Videos without captions, private or removed videos and invalid links are listed in `FAILURES` and aren't charged a transcript fee.
- **Fast.** 10 videos in about 18 seconds, 5 in parallel by default.

### What you can use it for

- **AI and RAG**: feed lectures, podcasts, interviews and tutorials into ChatGPT, Claude or a vector database.
- **Summaries and notes**: turn long videos into articles, newsletters, show notes or study notes.
- **Content repurposing**: blog posts, social posts and quotes from your own channel's videos.
- **Research**: analyse what creators, brands, politicians or competitors say, across many videos.
- **SEO and accessibility**: publish transcripts next to embedded videos.
- **Subtitles and clipping**: timed segments map every line to the second it's spoken.

### Input

| Field | Example | Notes |
|---|---|---|
| Videos | `https://www.youtube.com/watch?v=aircAruvnKk` | URLs or IDs, one per line. Duplicates are removed. |
| Playlists and channels | `https://www.youtube.com/playlist?list=UU_x5XG1OV2P6uZZ5FSM9Ttw` | Playlist, `@handle` or `/channel/UC…` URLs. See below. |
| Max videos per run | `100` | Unique videos in total, from both fields. Up to 10,000. |
| Max playlist/channel pages | `10` | YouTube listing pages read in total. Up to 100. |
| Preferred language | `en`, `es`, `pt-BR` | Falls back to the video's default captions when unavailable. |
| Prefer auto-generated captions | off | On = speech-recognition captions even when human ones exist. |
| Include timed segments | off | Adds `segments: [{ start, duration, text }]`. |
| Videos in parallel | `5` | Up to 20. |

Fill in Videos, Playlists and channels, or both. If you paste more than 100 video links, raise **Max videos per run**.

#### Playlists and channels

- **Playlists**: `youtube.com/playlist?list=…` links. A `watch?v=…&list=…` link in **Videos** is just that one video; put it in **Playlists and channels** to get the whole playlist.
- **Channels**: `youtube.com/@handle` (or `/@handle/videos`) and `youtube.com/channel/UC…` links, using the channel's **Videos** tab only (no Shorts, live or podcast tabs). Example: `https://www.youtube.com/@GoogleDevelopers`. A handle is looked up once per run, with up to 4 tries; looking it up is free and doesn't use up **Max playlist/channel pages**. If YouTube won't resolve a handle, it shows up in `FAILURES` and the rest of the run carries on; paste the `/channel/UC…` link instead. Old `/c/` and `/user/` links aren't supported.
- **Order and duplicates**: videos you pasted come first, then each playlist or channel in the order you listed them. A video found twice is fetched once.
- **Caps**: expansion stops at **Max videos per run** or **Max playlist/channel pages**, whichever comes first. A playlist page holds up to about 100 videos and a channel page about 30, so with the defaults you get up to 100 videos. It reads what YouTube lists; it doesn't promise every video of a large channel or a particular order.
- **Report**: `SOURCE_EXPANSION` in the key-value store shows, for each playlist or channel, the pages read, videos added, whether a cap stopped it, and any error or notice from YouTube. Playlists or channels that fail are also listed in `FAILURES`, next to videos without captions.

### Output

```json
{
  "videoId": "jNQXAC9IVRw",
  "url": "https://www.youtube.com/watch?v=jNQXAC9IVRw",
  "title": "Me at the zoo",
  "channelName": "jawed",
  "channelId": "UC4QobU6STFB0P71PMvOGN5A",
  "channelUrl": "https://www.youtube.com/channel/UC4QobU6STFB0P71PMvOGN5A",
  "durationSeconds": 19,
  "viewCount": 445773513,
  "isLive": false,
  "description": "...",
  "keywords": ["me at the zoo", "jawed karim", "first youtube video"],
  "thumbnail": "https://i.ytimg.com/vi/jNQXAC9IVRw/hqdefault.jpg?...",
  "language": "en",
  "languageName": "English",
  "isAutoGenerated": false,
  "availableLanguages": [
    { "code": "en", "name": "English", "isAutoGenerated": false },
    { "code": "de", "name": "German", "isAutoGenerated": false }
  ],
  "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 ...",
  "transcriptWithTimestamps": "[00:01] All right, so here we are, in front of the elephants\n[00:05] the cool thing about these guys is that they have really...\n...",
  "wordCount": 39,
  "scrapedAt": "2026-10-22T10:08:42.164Z"
}
```

With **Include timed segments** on, each row also has `segments: [{ "start": 1.2, "duration": 4.08, "text": "All right, so here we are, in front of the elephants" }, ...]`.

### Use it from code

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("santiagomv/youtube-transcripts").call(run_input={
    "videos": ["https://www.youtube.com/watch?v=aircAruvnKk"],
    "language": "en",
})
for video in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(video["title"], video["wordCount"])
    print(video["transcript"][:500])
```

For a playlist or channel, pass `"sources": ["https://www.youtube.com/@GoogleDevelopers"]` and set `"maxVideos"`.

More examples (Python, Node.js, cURL, n8n) are in the [code examples repository](https://github.com/SantiagoMV42/linkedin-jobs-scraper), including a [playlist and channel tutorial](https://github.com/SantiagoMV42/linkedin-jobs-scraper/tree/main/youtube).

### Which YouTube actor do I need?

- **Captions as text**: this actor. It only works for videos that have captions available; videos without them go to `FAILURES`.
- **Just the video list** (URLs, titles, views, durations, approximate upload dates): [YouTube Channel Videos](https://apify.com/santiagomv/youtube-channel-videos) is cheaper per video. It reads the channel's Videos tab only, and views and dates are rounded or approximate as YouTube lists them.
- **Comments and replies**: [YouTube Comments](https://apify.com/santiagomv/youtube-comments).

All three accept channel and playlist links directly.

### Pricing

**$4 per 1,000 transcripts** ($0.004 each), charged as an actor event for each transcript returned. Listing the videos of a playlist or channel and looking up `@handles` aren't charged as events, and neither are videos without captions, private, removed or age-restricted videos, failed playlists or channels and invalid links. Your run's maximum cost limit is always respected.

### FAQ

**Does every video have a transcript?** No. Most videos in common languages have auto-generated captions, but some creators turn captions off, and music videos often have none. Those videos show up in `FAILURES` and aren't charged a transcript fee.

**Can it transcribe audio itself?** No. It reads the captions YouTube already has. It doesn't download audio or run speech recognition.

**Can it translate?** Not in this version. Ask for a language the video offers (see `availableLanguages`), or translate the text afterwards.

**Do I need a YouTube API key or proxies?** No. Proxies are included in the price.

**Channels and playlists?** Yes, with limits: playlist links and channel links (`@handle` or `/channel/UC…`, Videos tab only), capped by Max videos per run and Max playlist/channel pages. 100 transcripts cost $0.40; set a run cost limit for big channels. `/c/` and `/user/` links aren't supported; open an issue if you need them.

**Is this allowed?** Read this before using it. This actor collects publicly available captions without logging in. Automated access may be against YouTube's Terms of Service, and YouTube may block or rate-limit the IP addresses or accounts involved. Transcripts are the copyrighted work of their creators. You are responsible for how you use this actor and the data, including copyright, personal data and YouTube's terms.

This actor is not affiliated with, endorsed by or sponsored by YouTube or Google LLC. YouTube is a trademark of Google LLC.

# Actor input Schema

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

YouTube video URLs or 11-character video IDs, one per line. Watch, youtu.be, Shorts, embed, live and music.youtube.com links all work. A watch link that also has a list= parameter counts as that one video; put the playlist in Playlists and channels to get all of it.

## `sources` (type: `array`):

Playlist URLs (youtube.com/playlist?list=…) or channel URLs (youtube.com/@handle or youtube.com/channel/UC…), one per line. Channels use their Videos tab only (no Shorts or live tabs). /c/ and /user/ URLs aren't supported. Videos from here come after the Videos above, duplicates removed. Listing videos is free; you pay only for transcripts saved.

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

Total unique videos to fetch in this run, counting Videos first and then videos found in Playlists and channels. Raise it if you paste more than 100 video links.

## `maxSourcePages` (type: `integer`):

Total YouTube listing pages to read across all playlists and channels in this run. A playlist page holds up to about 100 videos, a channel page about 30. Expansion stops at this limit or at Max videos, whichever comes first.

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

Language code such as en, es, de, pt-BR. Any regional variant counts (en matches en-GB). If the video has no captions in this language, you get its default captions instead; check the language field.

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

By default, captions written by the creator are used when they exist and auto-generated (speech recognition) captions otherwise. Turn this on to prefer auto-generated ones, which follow the spoken words more literally.

## `includeSegments` (type: `boolean`):

Add a segments array with start and duration in seconds for every caption line (for subtitles, clipping or search). The plain transcript and the timestamped text are always included.

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

How many videos to fetch at the same time.

## Actor input object example

```json
{
  "videos": [
    "https://www.youtube.com/watch?v=aircAruvnKk",
    "https://youtu.be/jNQXAC9IVRw"
  ],
  "maxVideos": 100,
  "maxSourcePages": 10,
  "language": "en",
  "preferAutoGenerated": false,
  "includeSegments": false,
  "maxConcurrency": 5
}
```

# Actor output Schema

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

All transcripts saved in this run, as a table.

## `failures` (type: `string`):

Videos without captions, private or removed videos, and invalid inputs. These are not charged.

# 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=aircAruvnKk",
        "https://youtu.be/jNQXAC9IVRw"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("santiagomv/youtube-transcripts").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=aircAruvnKk",
        "https://youtu.be/jNQXAC9IVRw",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("santiagomv/youtube-transcripts").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=aircAruvnKk",
    "https://youtu.be/jNQXAC9IVRw"
  ]
}' |
apify call santiagomv/youtube-transcripts --silent --output-dataset

```

## MCP server setup

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

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/5K6ei1HkcHrE6RHqG/builds/nHYRfxPlt7L7nr06O/openapi.json
