# YouTube Channel Transcript Scraper - Whole Channel, One Input (`neverempty/youtube-channel-transcripts`) Actor

Give it a YouTube channel handle and get transcripts for its recent videos - no need to collect video IDs first. Every row says which language you got and whether the captions were human-written or auto-generated, and videos with no captions come back with the reason instead of a blank row.

- **URL**: https://apify.com/neverempty/youtube-channel-transcripts.md
- **Developed by:** [NeverEmpty](https://apify.com/neverempty) (community)
- **Categories:** Videos, AI, MCP servers
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$8.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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## YouTube Channel Transcript Scraper

Give it a channel. Get transcripts for that channel's recent videos.

Most transcript scrapers want a list of video IDs, which means you need a second tool — or a scrape of your own — before you can start. This one takes `@veritasium`, a channel URL, or a `UC...` channel ID, finds the videos itself, and returns one row per video.

### What you get

One row per video:

| Field | |
|---|---|
| `channelInput` / `channelId` / `channelTitle` | which channel this row came from |
| `videoId` / `url` / `title` / `publishedAt` | the video |
| `text` | the full transcript as one string |
| `segments` | per-line text with `start` and `duration` (switch off with `includeTimestamps: false`) |
| `language` | the language you actually received |
| `isAutoGenerated` | `true` if these are machine-generated captions |
| `availableLanguages` | every caption language the video offers |
| `segmentCount` | number of caption lines |
| `ok` | `false` on rows that could not be transcribed, with `reason` |

### Read `isAutoGenerated` before you trust the text

I measured the 15 most recent videos from each of 12 channels — 180 videos — on 2026-08-29.

| | |
|---|---|
| Returned a caption track | 163 (90.6%) |
| ...of which the track was auto-generated | **124 (76.1%)** |
| Human captions available | 43 (23.9%) |
| No caption track at all | 15 (8.3%) |
| Video not playable | 2 (1.1%) |

Whether a channel has human-written captions is close to binary. **Six of the twelve channels had none at all** across their last 15 uploads, while Marques Brownlee (12) and NASA (11) between them account for over half of the 43.

Machine captions and human captions arrive through the same field, with the same success status. `isAutoGenerated` is the only thing that tells them apart, so it is on every row — along with `language` and `availableLanguages`, so you can see what you actually received rather than assuming you got what you asked for.

### What it does **not** do

- **It cannot reach a channel's back catalogue.** YouTube's public channel feed returns at most **15 videos**, so 15 per channel is the ceiling. Ask for more and it will tell you in the log rather than quietly returning 15.
- **It does not translate.** If a video only offers Bulgarian captions, you get Bulgarian, labelled as Bulgarian. Machine-translating a machine transcription without saying so is how a pipeline ends up confidently wrong.
- **It does not return blank rows.** A private video, a members-only video, one with no captions, or one that was blocked comes back with `ok: false` and a `reason` in plain English.

### Billing

You are charged per transcript that actually contains text. Rows that explain a failure are free. If you set a maximum total charge on the run and it is reached, the run stops and says so in the log and in the run status, rather than silently returning a partial result that looks complete.

### Input

```json
{
  "channels": ["@veritasium", "https://www.youtube.com/@mkbhd", "UCHnyfMqiRRG1u-2MsSQLbXA"],
  "maxVideosPerChannel": 15,
  "languages": ["en"],
  "includeTimestamps": true,
  "proxyMode": "auto",
  "maxRetries": 3
}
```

| Input | |
|---|---|
| `channels` | Handles (`@name`), channel URLs, or `UC...` channel IDs. Required. |
| `maxVideosPerChannel` | 1–15. YouTube's public feed caps at 15 per channel. |
| `languages` | Preferred language codes in order. Human captions win over auto-generated ones. |
| `includeTimestamps` | Include per-line `segments` with `start` and `duration`. |
| `proxyMode` | `auto` tries datacenter proxies first, because their traffic is free, and falls back to residential only when YouTube blocks. Set `residential` directly if you run at volume. |
| `maxRetries` | Attempts per video on a different IP before writing a row that says why it failed. |

# Actor input Schema

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

Channel handles (@veritasium), channel URLs, or UC... channel IDs. Give the channel, not the videos - this Actor finds the videos for you.

## `maxVideosPerChannel` (type: `integer`):

How many of the channel's most recent videos to transcribe. YouTube's public feed returns at most 15 videos per channel, so 15 is the ceiling - this Actor cannot reach a channel's back catalogue and will not pretend to.

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

Language codes in order of preference, e.g. en, ja, es. Human-written captions are preferred over auto-generated ones. If none of your languages exist, you get the track that does exist - and the row tells you which language and whether it was auto-generated.

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

Include the per-line segments with start time and duration in addition to the full text.

## `proxyMode` (type: `string`):

auto = try datacenter first (cheaper), fall back to residential when YouTube blocks. Use residential if you scrape at volume.

## `maxRetries` (type: `integer`):

How many times to retry a single video on a different IP before giving up and writing a row that says why.

## Actor input object example

```json
{
  "channels": [
    "@veritasium"
  ],
  "maxVideosPerChannel": 15,
  "languages": [
    "en"
  ],
  "includeTimestamps": true,
  "proxyMode": "auto",
  "maxRetries": 3
}
```

# Actor output Schema

## `results` (type: `string`):

All rows produced by this run.

# 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 = {
    "channels": [
        "@veritasium"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("neverempty/youtube-channel-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 = { "channels": ["@veritasium"] }

# Run the Actor and wait for it to finish
run = client.actor("neverempty/youtube-channel-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 '{
  "channels": [
    "@veritasium"
  ]
}' |
apify call neverempty/youtube-channel-transcripts --silent --output-dataset

```

## MCP server setup

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