# YouTube Transcript Scraper: Channels, Playlists & Shorts (`nextgensda/youtube-transcript-scraper`) Actor

Timestamped transcripts and video metadata from YouTube videos, Shorts, whole channels, playlists and searches. Any caption language, SRT/VTT, publish dates. No login or browser; monitor channels for new uploads with only-new runs.

- **URL**: https://apify.com/nextgensda/youtube-transcript-scraper.md
- **Developed by:** [Tauqeer Abbas](https://apify.com/nextgensda) (community)
- **Categories:** Videos, AI, Lead generation
- **Features:** Limited permissions
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.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: Channels, Playlists & Shorts

![YouTube Transcript Scraper: timestamped transcripts from videos, Shorts, whole channels, playlists and searches, $1.00 per 1,000 transcripts](https://api.apify.com/v2/key-value-stores/P2URgmEFDmPVNWfdR/records/banner-2026-11-12.png?signature=1ONFUHgVZoJviLixWiRHj)

Timestamped YouTube transcripts at **$1.00 per 1,000**: single videos, Shorts,
whole channels, playlists and keyword searches in one run, with the video's
title, channel, views, duration and exact publish date in the same row.

**No login, no API key, no browser. 256 MB. About 100 transcripts a minute.**

### Why this actor

|  | This actor | Typical transcript scrapers |
|---|---|---|
| **Price per 1,000 transcripts** | **$1.00** | $2.00 – $5.00 |
| Videos without captions | **$0.30 / 1,000** (metadata only), or skipped for free | charged or dropped |
| Channels, playlists, Shorts and search in one run | **yes** | often video URLs only |
| Exact publish date and time | **yes** | often relative ("3 weeks ago") or missing |
| SRT and WebVTT subtitles | **yes** | sometimes |
| Monitor channels for new uploads | **built in** (`onlyNew`) | do it yourself |
| Memory | **256 MB** | 1 – 4 GB |
| Login or API key | no | sometimes |

A video found by several inputs (a channel and a search, say) is delivered and
charged once.

### Built for AI datasets and research

- **LLM training and fine-tuning**: spoken-language text with timestamps,
  language and whether captions are the creator's own or auto-generated.
- **RAG and agents**: index a channel's back catalogue, then keep it fresh with
  a daily only-new run.
- **Summaries and chaptering**: segments with start/end times map straight to
  clips and chapters.
- **Content and market research**: what a niche's top videos for a keyword
  actually say, with views and dates.
- **Subtitles**: SRT or WebVTT ready to download.

Export as JSON, CSV, Excel or XML, schedule runs, or deliver through webhooks
and the Apify API.

### What you get

One row per video:

| Field | |
|---|---|
| `videoId`, `url`, `isShort` | |
| `title`, `description`, `keywords`, `thumbnail` | |
| `channelName`, `channelId`, `channelUrl` | |
| `durationSeconds`, `viewCount` | |
| `publishDate` | ISO 8601 with time and zone, e.g. `2026-10-29T13:00:20-07:00` |
| `publishedText` | as the channel or search page shows it ("2 weeks ago") |
| `isLiveContent` | a live stream or its replay |
| `captionLanguages` | every caption track: language, name, auto-generated or not |
| `transcriptLanguage`, `transcriptAutoGenerated` | the track that was read |
| `hasTranscript` | false when the video has no captions |
| `transcript` | `[{start, end, text}]`, seconds |
| `transcriptText` | the whole transcript as one string |
| `srt`, `vtt` | when asked for |
| `source`, `sourceUrl`, `scrapedAt` | which input found the video |

Example segment: `{"start": 1.2, "end": 3.36, "text": "All right, so here we are, in front of the elephants"}`

### Quick start

Transcripts of a channel's 50 newest videos:

```json
{
  "channels": ["@GoogleDevelopers"],
  "maxVideosPerSource": 50
}
```

Search results with SRT subtitles:

```json
{
  "searches": ["sourdough starter"],
  "maxVideosPerSource": 30,
  "outputFormats": ["text", "srt"]
}
```

New uploads of several channels, every day (schedule it):

```json
{
  "channels": ["@GoogleDevelopers", "@Google"],
  "channelTab": "all",
  "onlyNew": true,
  "monitorName": "google-channels"
}
```

### Input

| Input | Default | Notes |
|---|---|---|
| `startUrls` | — | videos, Shorts, `youtu.be` links, channels (`/@handle`, `/videos`, `/shorts`, `/streams`), playlists, search pages |
| `channels` | — | `@handles`, channel URLs or `UC…` ids |
| `searches` | — | keywords (videos, in YouTube's order) |
| `channelTab` | `videos` | `videos`, `shorts`, `streams` or `all` (videos + Shorts) |
| `maxVideosPerSource` | `50` | per channel tab, playlist or search |
| `maxItems` | `1000` | videos in total |
| `transcripts` | `true` | off: metadata only |
| `language` | `en` | e.g. `de`, `es`, `pt-BR`; `en-GB` also matches `en`; empty = the spoken language |
| `fallbackToOriginalLanguage` | `true` | no captions in your language: take the spoken-language transcript |
| `preferManualCaptions` | `true` | the creator's captions before auto-generated ones |
| `includeSegments` | `true` | the timestamped list |
| `outputFormats` | `text` | add `srt` and/or `vtt` |
| `includePublishDate` | `true` | exact date and time |
| `skipVideosWithoutTranscript` | `false` | leave caption-less videos out (not charged) |
| `onlyNew`, `monitorName` | off | skip videos earlier runs delivered (remembered a year) |
| `concurrency` | `4` | videos read at once |
| `proxyConfiguration` | Apify Proxy | datacenter by default; switches to residential by itself if needed |

### Pricing

Pay per event: you pay only for the rows you receive.

| Event | Price |
|---|---|
| `transcript`: a video with its transcript | **$1.00 / 1,000** |
| `video`: a video without one (no captions, or transcripts off) | **$0.30 / 1,000** |
| Actor start | $0.001 per run |

Videos YouTube doesn't show without signing in (age-restricted, private,
removed) get a row with `error` and are **not charged**. Set a **maximum cost
per run** and the actor stops when it is reached.

### Notes and limits

- About 1 in 8 videos has no captions at all (music, some live streams, short
  clips). They come with metadata only (`hasTranscript: false`), or not at all
  with `skipVideosWithoutTranscript`.
- Transcripts come from YouTube's captions: the creator's own, or YouTube's
  automatic speech recognition. Machine translation into other languages is not
  offered.
- With a `language` the video doesn't have, you get its spoken-language
  transcript and `transcriptLanguage` says which (turn off
  `fallbackToOriginalLanguage` to get none instead).
- Age-restricted, private and members-only videos can't be read without an
  account.
- Channel tabs are read newest first. With `onlyNew`, a channel stops at the
  first run of videos it already delivered, so daily runs are quick and cheap.
- Searches follow YouTube's relevance order and end after about 300–350
  videos per keyword (measured); use several related keywords for more.

# Actor input Schema

## `startUrls` (type: `array`):

Any YouTube URL: a video (<code>watch?v=</code>, <code>youtu.be/</code>, <code>/shorts/</code>, <code>/live/</code>), a channel (<code>/@handle</code>, optionally <code>/videos</code>, <code>/shorts</code> or <code>/streams</code>), a playlist (<code>/playlist?list=</code>) or a search results page (<code>/results?search_query=</code>). Bare video ids work too.

## `channels` (type: `array`):

Channel handles (<code>@GoogleDevelopers</code>), channel URLs or <code>UC…</code> ids. Their newest videos come first.

## `searches` (type: `array`):

Search YouTube for each keyword (videos only, in YouTube's relevance order).

## `channelTab` (type: `string`):

Which videos of a channel to read, unless its URL names a tab.

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

How many videos to read from each channel tab, playlist or search.

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

The run stops after this many video rows.

## `transcripts` (type: `boolean`):

Off: video metadata only (title, channel, views, duration, description, caption languages), at the lower per-video price.

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

A language code like <code>en</code>, <code>de</code>, <code>es</code> or <code>pt-BR</code>. A regional code also matches its base language (<code>en-GB</code> takes <code>en</code>). Empty: the video's spoken language.

## `fallbackToOriginalLanguage` (type: `boolean`):

When a video has no captions in your language, return its spoken-language transcript (the row says which language it is). Off: such videos come without a transcript.

## `preferManualCaptions` (type: `boolean`):

Take the creator's own captions over YouTube's auto-generated ones when both exist in the language.

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

The transcript as a list of <code>{start, end, text}</code> (seconds).

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

Extra fields with the whole transcript: plain text (<code>transcriptText</code>), <code>srt</code>, <code>vtt</code>.

## `includePublishDate` (type: `boolean`):

The video's publish date and time (one small extra request per video).

## `skipVideosWithoutTranscript` (type: `boolean`):

Leave out videos that have no captions at all (about 1 in 8). They aren't charged.

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

Skip videos this monitor delivered in earlier runs (remembered a year). Skipped videos aren't charged, and a channel stops being read at the first run of videos it already has. Schedule it to get every new upload of a channel.

## `monitorName` (type: `string`):

Keep separate monitors apart (e.g. one per client). Letters, digits and dashes.

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

Videos read at once, each through its own proxy IP.

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

Apify's datacenter proxies work and cost least: YouTube refuses some of their IPs, and the actor moves to a new one each time. If too many in a row are refused, the run switches to US residential by itself.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://www.youtube.com/watch?v=jNQXAC9IVRw"
    }
  ],
  "channels": [
    "@GoogleDevelopers"
  ],
  "channelTab": "videos",
  "maxVideosPerSource": 10,
  "maxItems": 1000,
  "transcripts": true,
  "language": "en",
  "fallbackToOriginalLanguage": true,
  "preferManualCaptions": true,
  "includeSegments": true,
  "outputFormats": [
    "text"
  ],
  "includePublishDate": true,
  "skipVideosWithoutTranscript": false,
  "onlyNew": false,
  "monitorName": "default",
  "concurrency": 4,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `rows` (type: `string`):

Title, channel, transcript language and text, duration, views, publish date and link.

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

Every field: timestamped segments, SRT/VTT, description, keywords, thumbnail, caption languages.

# 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 = {
    "startUrls": [
        {
            "url": "https://www.youtube.com/watch?v=jNQXAC9IVRw"
        }
    ],
    "channels": [
        "@GoogleDevelopers"
    ],
    "maxVideosPerSource": 10,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("nextgensda/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 = {
    "startUrls": [{ "url": "https://www.youtube.com/watch?v=jNQXAC9IVRw" }],
    "channels": ["@GoogleDevelopers"],
    "maxVideosPerSource": 10,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("nextgensda/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 '{
  "startUrls": [
    {
      "url": "https://www.youtube.com/watch?v=jNQXAC9IVRw"
    }
  ],
  "channels": [
    "@GoogleDevelopers"
  ],
  "maxVideosPerSource": 10,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call nextgensda/youtube-transcript-scraper --silent --output-dataset

```

## MCP server setup

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