# YouTube Transcript Scraper: Videos, Channels and Playlists (`oski/youtube-transcript-scraper`) Actor

Bulk YouTube transcripts from videos, Shorts, channels, playlists and searches. Plain text, timestamps and SRT subtitles in your language. No API key, and you pay only for transcripts found.

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

## Pricing

from $2.91 / 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: Videos, Channels and Playlists

Get YouTube transcripts in bulk as clean text with timestamps. Paste video links, Shorts, a whole channel, a playlist or a search, and every video comes back with its full transcript, the caption language, whether the captions are human-written or automatic, and the video's title, channel, length and views. **You are only charged when a transcript actually exists**, so videos without captions, private or removed videos cost nothing.

Tired of a transcript scraper that takes one video per run, fails every few runs and charges you anyway? This one takes as many videos as you like in one run. It retries on a fresh IP when YouTube pushes back and switches to a residential IP automatically when YouTube asks for a bot check, at no extra cost. Every row tells you plainly why a video had no transcript.

> **Also from Oski:** [Telegram Channel Scraper](https://apify.com/oski/telegram-channel-scraper) for public channel posts, and [Website Contact Finder](https://apify.com/oski/website-contact-finder) for a creator's business email from their website.

### What you can use it for

- **AI and RAG builders** turning a channel or playlist into a text corpus for search, chatbots or fine-tuning.
- **Content repurposers** turning videos into blog posts, newsletters, threads and show notes.
- **Researchers and journalists** searching what was said across hundreds of videos, with timestamps to cite.
- **Marketers** studying the scripts of competitors' and top creators' videos, hooks included.
- **Educators and students** getting lecture and tutorial transcripts for notes and study.
- **Accessibility and localisation teams** exporting ready-made SRT subtitle files.

### How to use it

1. Under **YouTube videos, Shorts, channels or playlists**, paste links one per line (or several on one line). Video IDs, `youtu.be` links and `@handles` work too.
2. Optionally add **Search phrases** to get transcripts of the top videos for a topic.
3. Set **Max videos per channel, playlist or search** (single videos always count as one) and your **Preferred languages**, then click **Start**.
4. When it finishes, open the **Output** tab and download as CSV, Excel or JSON.

While it runs, the status line at the top of the run page shows progress (for example "Source 2 of 3: @veritasium. 48 transcripts so far."). When it finishes, the same line says what was found and explains anything that came back without a transcript, so you never have to read the log.

### How much does it cost?

A $0.002 start fee per run, then **$0.003 per transcript**. Videos with no captions, only "\[Music]" captions, or that are private, removed or age-restricted are returned for your records and **not charged**. Every row has a `charged` field so you can see exactly what you paid for.

| Transcripts | Maths | Cost |
|---|---|---|
| 1 | $0.002 + 1 x $0.003 | $0.005 |
| 100 | $0.002 + 100 x $0.003 | about $0.30 |
| 1,000 | $0.002 + 1,000 x $0.003 | about $3.00 |
| 10,000 | $0.002 + 10,000 x $0.003 | about $30.00 |

A whole channel of 1,000 videos costs about three dollars, so put everything into one run rather than paying a start fee per video.

### Input

| Field | What it does |
|---|---|
| YouTube videos, Shorts, channels or playlists | Anything from YouTube: `https://www.youtube.com/watch?v=...`, `youtu.be/...`, `/shorts/...`, `/live/...`, a bare video ID, `@handle`, a channel link, a playlist link or a search results page. |
| Search phrases | Transcripts of the top videos YouTube shows for each phrase. |
| Max videos per channel, playlist or search | Cap per source. Channels go newest first. Default 200. |
| Preferred languages | Language codes in order, such as `en`, `es`, `pt-BR`. Human captions beat auto captions in the same language. If none match, you get the video's own language and the message field says so. |
| Translate when my language is missing | Ask YouTube for an automatic translation into your first language. YouTube sometimes refuses; those videos still come back in the original language, flagged. |
| Caption type | Any (human first), human only, or auto only. |
| Channel tabs | Videos, Shorts and/or Live streams when you paste a channel. |
| Include timestamped segments / SRT / description | Extra fields for each video. |
| Advanced: max videos in total, videos at once, residential fallback, proxy | Budget cap and speed. The defaults suit almost every run. |

Already using another transcript actor through the API? Inputs with `videoUrl`, `videoUrls`, `startUrls`, `youtube_url`, `channel_url`, `targetLanguage` or `language` work here unchanged.

### Output

One row per video:

```json
{
  "video_id": "aircAruvnKk",
  "url": "https://www.youtube.com/watch?v=aircAruvnKk",
  "status": "ok",
  "message": null,
  "title": "But what is a neural network? | Deep learning chapter 1",
  "channel_name": "3Blue1Brown",
  "channel_id": "UCYO_jab_esuFRV4b17AJtAw",
  "channel_url": "https://www.youtube.com/channel/UCYO_jab_esuFRV4b17AJtAw",
  "duration_seconds": 1120,
  "view_count": 24519867,
  "language": "en",
  "language_name": "English",
  "caption_type": "manual",
  "translated_to": null,
  "available_languages": ["ar", "bn", "cs", "de", "el", "en", "en (auto)", "..."],
  "word_count": 3357,
  "segment_count": 286,
  "text": "This is a 3. It's sloppily written and rendered at an extremely low resolution of 28x28 pixels, ...",
  "segments": [{"start": 4.22, "duration": 1.18, "text": "This is a 3."}, "..."],
  "description": "What are the neurons, why are there layers, and what is the math underlying it? ...",
  "source": "https://www.youtube.com/@3blue1brown",
  "charged": true,
  "scraped_at": "2026-09-28T12:00:00Z"
}
```

`status` is one of `ok`, `no_transcript`, `music_only`, `no_matching_type`, `age_restricted`, `private`, `unavailable`, `live` or `blocked`, and `message` explains it in plain words. Only `ok` rows are charged. Add `srt` with the SRT option.

### Tips for bigger runs

- **Put a whole channel in one run.** Paste the `@handle` and set the per-source limit to the channel size; the start fee is paid once.
- **Pick the channel tabs you need.** Channels list Videos, Shorts and Live streams separately; tick all three to get everything a channel posted.
- **Duplicates are removed automatically**, so overlapping channels, playlists and searches cost nothing extra.
- **Schedule a channel daily with a small limit** (say 10) to collect each new video's transcript as it appears.
- **Leave "Videos at once" at 5.** Higher is faster on big channels but makes YouTube push back more often.

### Scheduling and integrations

Save a task with your channels and run it on an Apify schedule, then send each run to Google Sheets, a webhook, Make, Zapier or your vector database through Apify's integrations. From your own code:

```bash
curl -X POST "https://api.apify.com/v2/acts/oski~youtube-transcript-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"urls": ["https://www.youtube.com/watch?v=aircAruvnKk", "@veritasium"], "maxVideosPerSource": 20}'
```

The sync endpoint waits up to 5 minutes. For big runs, start the run with `/runs` and read the dataset when it finishes.

### If something looks wrong

The status line at the end of every run explains anything that came back without a transcript. The common messages:

| Message | What to do |
|---|---|
| Input problem: ... | Something in the input needs changing, and the message says what. The run stopped before anything was charged. |
| Channel ... was not found | Check the handle (for example `@mkbhd`) or paste the channel link from your browser. |
| Searched YouTube for "..." because it is not a link | You typed words into the links box. That's fine, but put phrases under Search phrases next time. |
| N videos had no captions | The creator did not add captions and YouTube made no auto captions. Nothing to fetch, not charged. |
| N videos were age-restricted | YouTube only shows these to signed-in adults. Not charged. |
| N videos were refused by YouTube | Rare: every IP we tried was turned away. Run those links again later. Not charged. |
| No en captions; this is the original ko transcript | The video has no captions in your language. Turn on Translate to ask YouTube for a translation. |

Still stuck? Open an issue on the actor page with the run link and it gets looked at quickly.

### FAQ

**Do I need a YouTube account or API key?**
No. It reads the same public captions YouTube shows under any video, with no login and no quota.

**Which videos have transcripts?**
Most videos with speech have automatic captions in their spoken language. Many also have human captions, often in several languages. Music-only videos and very new uploads sometimes have none.

**Is auto-caption text accurate?**
It is YouTube's speech recognition: good for search and summaries, with occasional misheard words and little punctuation. Choose "Human captions only" when accuracy matters more than coverage.

**Can I get a language the video was not captioned in?**
Turn on Translate. YouTube's automatic translation is used when it agrees; when it refuses, you still get the original transcript, flagged in the message field.

**Is it legal?**
The actor reads captions YouTube publishes to everyone. You are responsible for how you use them, including copyright and YouTube's terms. Transcripts belong to their creators.

**What if it breaks?**
Open an issue on the actor page and it gets fixed fast. Every row carries a status and a plain message, so a problem shows up in your data instead of as silently empty rows.

### Other scrapers from Oski

- [Telegram Channel Scraper](https://apify.com/oski/telegram-channel-scraper): posts, views and media from public Telegram channels.
- [Google Ads Transparency Scraper](https://apify.com/oski/google-ads-transparency-scraper) and [Facebook Ad Library Scraper](https://apify.com/oski/facebook-ad-library-scraper): the ads a creator or brand is running.
- [Website Contact Finder](https://apify.com/oski/website-contact-finder): business emails and phones from any website.
- [LinkedIn Jobs Scraper](https://apify.com/oski/linkedin-jobs-scraper): job listings with salaries and full descriptions.

# Actor input Schema

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

Paste anything from YouTube, one per line: video links, Shorts, youtu.be links, video IDs, channel links or @handles, playlist links, or a search results page. A channel or playlist gives the transcript of every video in it, up to the limit below. Several links on one line, separated by commas or spaces, also work.

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

Get transcripts for the top videos YouTube shows for each phrase, up to the limit below.

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

How many videos to take from each channel, playlist or search (newest first for channels). Single video links always count as one. 1,000 transcripts cost about $3.

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

Language codes in order of preference, for example en, es, de, pt-BR. The first one the video has is used, human captions before auto captions. If none match, you get the transcript in the video's own language.

## `translate` (type: `boolean`):

If a video has no captions in your first language, ask YouTube for its automatic translation instead of the original language. YouTube sometimes refuses translations; those videos still come back in the original language, flagged in the message field.

## `captionType` (type: `string`):

Human captions are written by the creator and are usually more accurate. Auto captions are YouTube's speech recognition and exist on most videos.

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

Which tabs to read when you paste a channel.

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

Add a segments list with start time, duration and text for every caption line, as well as the full text.

## `includeSrt` (type: `boolean`):

Add the transcript as a ready-to-use .srt subtitle file in the srt field.

## `includeDescription` (type: `boolean`):

Add the video's description text next to the transcript.

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

Optional hard cap across all sources in the run. 0 means no cap beyond the per-source limit.

## `concurrency` (type: `integer`):

How many videos to fetch in parallel. 5 is fast and gentle; lower it if you see many refused videos.

## `residentialFallback` (type: `boolean`):

YouTube sometimes asks datacenter IPs to prove they are not a bot. When that happens the video is retried on a residential IP, at no extra cost to you.

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

The default Apify datacenter proxy works for channels, playlists and searches; refused transcripts fall back to residential automatically.

## Actor input object example

```json
{
  "urls": [
    "https://www.youtube.com/watch?v=aircAruvnKk",
    "https://youtu.be/dQw4w9WgXcQ",
    "@mkbhd",
    "https://www.youtube.com/playlist?list=PLZHQObOWTQDPD3MizzM2xVFitgF8hE_ab"
  ],
  "searchQueries": [
    "sourdough starter",
    "home workout for beginners"
  ],
  "maxVideosPerSource": 5,
  "languages": [
    "en",
    "es"
  ],
  "translate": false,
  "captionType": "any",
  "channelTabs": [
    "videos"
  ],
  "includeTimestamps": true,
  "includeSrt": false,
  "includeDescription": true,
  "concurrency": 5,
  "residentialFallback": true,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `overview` (type: `string`):

One row per video: title, channel, language, caption type, word count, status and the full transcript text.

## `allFields` (type: `string`):

Every field for each video, including timestamped segments, SRT subtitles when chosen and the 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 = {
    "urls": [
        "https://www.youtube.com/watch?v=aircAruvnKk",
        "https://www.youtube.com/shorts/VKlulHwMxgU",
        "https://www.youtube.com/@3blue1brown"
    ],
    "maxVideosPerSource": 5,
    "languages": [
        "en"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("oski/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=aircAruvnKk",
        "https://www.youtube.com/shorts/VKlulHwMxgU",
        "https://www.youtube.com/@3blue1brown",
    ],
    "maxVideosPerSource": 5,
    "languages": ["en"],
}

# Run the Actor and wait for it to finish
run = client.actor("oski/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=aircAruvnKk",
    "https://www.youtube.com/shorts/VKlulHwMxgU",
    "https://www.youtube.com/@3blue1brown"
  ],
  "maxVideosPerSource": 5,
  "languages": [
    "en"
  ]
}' |
apify call oski/youtube-transcript-scraper --silent --output-dataset

```

## MCP server setup

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