# YouTube Transcript Scraper | Subtitles, Timestamps & Metadata (`tactful_anvil/youtube-transcript-scraper`) Actor

Transcripts for any list of YouTube videos or the top videos of a keyword: plain text + timestamped segments, language (human or auto), all available languages, plus title, channel, views, length. $6 per 1,000; no captions = not charged. No API key, no login. For creators, marketers & AI/RAG.

- **URL**: https://apify.com/tactful\_anvil/youtube-transcript-scraper.md
- **Developed by:** [Mr Zack](https://apify.com/tactful_anvil) (community)
- **Categories:**
- **Stats:** 2 total users, 1 monthly users, 83.3% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $6.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 Transcript Scraper — Subtitles, Timestamps & Video Metadata

**Transcripts for any list of YouTube videos — or for the top videos of a keyword — in one run, $6 per 1,000.** Paste URLs, Shorts links, youtu.be links or IDs and get the full transcript as plain text plus timestamped segments (start, duration, text), the language used, whether it is human or auto-generated, every other language available, and the video's title, channel, length, views, keywords and thumbnail. Type a search query instead and the Actor finds the videos for you (`searchQuery` + `maxVideos`).

Videos without captions, private/removed videos, duplicates and search-listing rows are **never charged**. No YouTube API key, no login, no cookies, no browser.

### Who uses this

- **Content creators & agencies** repurposing videos into blog posts, newsletters, shorts scripts and SEO pages.
- **Marketers & researchers** mining competitor channels, reviews and interviews at scale.
- **AI / RAG builders** feeding LLMs with clean, segmented transcripts (works directly from the Apify MCP server).
- **Educators, journalists, analysts** who need quotable, timestamped text instead of re-watching hours of video.

### Input

| Field | Default | Notes |
|---|---|---|
| `videoUrls` | 2 demo videos | One per line: watch / shorts / embed / youtu.be URLs or bare IDs. |
| `searchQuery` | — | Transcribe the top videos for a keyword instead of (or in addition to) URLs. |
| `channelUrls` | — | **New in 0.0.3.** Whole channels: `@handle`, `/channel/UC…`, `/c/…`, `/user/…` — newest videos first. |
| `playlistUrls` | — | **New in 0.0.3.** Every video of a playlist (URL with `list=` or a bare `PL…`/`UU…` id). |
| `maxVideos` | `20` | Cap per keyword search / channel / playlist (max 500). |
| `languages` | `["en"]` | Preference order; prefix match (`en` → `en-GB`). Use `["all"]` to get **every** caption track. |
| `oneRowPerLanguage` | `false` | **New in 0.0.3.** One transcript row for *each* listed language that exists (e.g. `["en","es","id"]` → up to 3 rows per video). Automatically on with `["all"]`. |
| `outputFormats` | `[]` | **New in 0.0.3.** Add ready-to-save subtitle files as text fields: `srt` and/or `vtt`. |
| `preferManual` | `true` | Human captions over auto-generated when both exist. |
| `fallbackToAnyLanguage` | `true` | If no preferred language, take the first available track. |
| `includeSegments` | `true` | `segments[]` with `start`, `duration` (seconds), `text`. |
| `includeSearchRows` | `false` | Also output the ranked search listing (free rows, `type: search-result`). |

### Output — one row per video

| Field | Example |
|---|---|
| `videoId`, `url`, `title`, `channel`, `channelId`, `channelUrl` | `kJQP7kiw5Fk`, `Luis Fonsi - Despacito ft. Daddy Yankee`, `LuisFonsiVEVO` |
| `lengthSeconds`, `viewCount`, `publishDate`, `category`, `keywords[]`, `thumbnailUrl` | `282`, `9116018813`, … |
| `status` | `ok` | `no-captions` | `failed` (only `ok` rows are charged) |
| `language`, `languageName`, `isAutoGenerated` | `en`, `English - en`, `false` |
| `availableLanguages[]` | `["en","en-US","ja","es","es-ES","ko (auto)"]` |
| `transcript` | full plain text |
| `segments[]` | `{ "start": 0.433, "duration": 27.1, "text": "…" }` |
| `wordCount`, `charCount`, `segmentCount`, `source`, `scrapedAt` | `653`, `2990`, `90`, `direct` |
| `srt`, `vtt` (only with `outputFormats`) | `1\n00:00:00,433 --> 00:00:27,533\n…` / `WEBVTT\n\n00:00:00.433 --> …` |

### Example output (real row, 6 Sep 2026)

```json
{
  "videoId": "Ks-_Mh1QhMc",
  "url": "https://www.youtube.com/watch?v=Ks-_Mh1QhMc",
  "title": "Your Body Language May Shape Who You Are | Amy Cuddy | TED",
  "channel": "TED", "lengthSeconds": 1263, "viewCount": 29037137,
  "status": "ok", "language": "en", "isAutoGenerated": false,
  "availableLanguages": ["sq", "ar", "hy", "az", "bg", "my", "…53 total"],
  "transcript": "So I want to start by offering you a free no-tech life hack…",
  "segments": [{ "start": 12.9, "duration": 3.1, "text": "So I want to start by offering you" }],
  "wordCount": 3210, "segmentCount": 428
}
```

Default input (2 videos) costs $0.013 and finishes in ~3 seconds; 100 videos ≈ $0.60 in under a minute.

### Pricing

| Event | Price |
|---|---|
| Transcript | **$0.006** ($6 / 1,000 videos) |
| Actor start | $0.001 |

One row = one transcript = one charge, so `languages: ["all"]` on a video with 26 caption tracks bills 26 transcripts — use `oneRowPerLanguage` with an explicit list when you only need a few. Platform usage included. Not charged: videos without captions, unavailable/private videos, duplicates, videos where none of your languages exist (with `fallbackToAnyLanguage: false`), and search-result rows.

### Reliability

YouTube throttles datacenter IPs in waves. This Actor uses YouTube's own mobile player endpoint (the one the Android app uses) and, when YouTube still blocks a request, transparently retries through a residential fallback path — so runs keep succeeding instead of returning empty datasets. If every request is blocked and the fallback is down, the run fails loudly with nothing charged beyond the start fee.

### Schedule & integrate

Track a channel or topic: set `searchQuery`, schedule the Actor daily (Console → **Schedules**) and push new transcripts to Google Sheets, Notion, Slack or your vector DB via **Integrations**.

### API & AI agents (MCP)

```bash
curl -X POST "https://api.apify.com/v2/acts/tactful_anvil~youtube-transcript-scraper/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"videoUrls":["https://www.youtube.com/watch?v=Ks-_Mh1QhMc"],"languages":["en"],"includeSegments":false}'
```

Add it to Claude, Cursor or ChatGPT through the **Apify MCP server** (`https://mcp.apify.com`) and ask "summarize the last 10 videos about airbnb investing" — the agent gets clean transcripts back. Python: `ApifyClient(token).actor("tactful_anvil/youtube-transcript-scraper").call(run_input={...})`.

### Related Actors

- [Kick Trending Clips Scraper](https://apify.com/tactful_anvil/kick-trending-clips-scraper) — what is going viral on Kick right now.
- [Whop Content Rewards Scraper](https://apify.com/tactful_anvil/whop-content-rewards-scraper) — paid clipping campaigns that need exactly this kind of source material.
- [Google Play Reviews Scraper](https://apify.com/tactful_anvil/google-play-reviews-scraper) — more voice-of-customer text for research.

### Found this useful? Bookmark & review

A bookmark or a short review helps other creators find this Actor — and tells me what to add next (channel mode, SRT/VTT export, translation tracks?). Issues tab is open; I answer within 24 h.

### Changelog

- **0.0.4 (8 Sep 2026)** — billing hardening: a charging hiccup can no longer abort a run that already returned transcripts (rows are delivered either way).
- **0.0.3 (8 Sep 2026)** — **channel mode** (`channelUrls`, newest `maxVideos` videos per channel) and **playlist mode** (`playlistUrls`); **one row per language** (`oneRowPerLanguage`, or `languages: ["all"]` for every track — 26 languages on a MrBeast video in one run); **SRT / VTT** output fields (`outputFormats`); Google "sorry"/429 pages are now detected as blocks (never charged, relay retry) instead of being parsed as a 1-line transcript. No existing field changed.
- **0.1 (6 Sep 2026)** — launch: URL/ID + search mode, language preference with manual-first, timestamped segments, video metadata, residential fallback.

# Actor input Schema

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

One per line: youtube.com/watch?v=…, youtu.be/…, /shorts/…, /embed/… or the bare 11-character ID.

## `searchQuery` (type: `string`):

Transcribe the top videos for a keyword (e.g. "airbnb investing"). Combine with Max videos. Leave empty when passing URLs.

## `channelUrls` (type: `array`):

Transcribe the newest videos of whole channels: @handle, youtube.com/@handle, /channel/UC…, /c/… or /user/…. Uses Max videos per channel (newest first).

## `playlistUrls` (type: `array`):

Transcribe every video of a playlist (URL with list=… or a bare PL…/UU… id). Uses Max videos per playlist.

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

How many videos to transcribe per keyword search, channel or playlist (max 500, newest first for channels). Ignored for explicit URLs.

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

Language codes in order of preference (en, en-US, es, id, …). A prefix match (en → en-GB) counts.

## `oneRowPerLanguage` (type: `boolean`):

Output a separate transcript row for EVERY listed language that exists on the video (instead of the first match). Set Preferred languages to \["all"] to get every available track. Each row is charged as one transcript.

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

When both exist for a language, take the manual track.

## `fallbackToAnyLanguage` (type: `boolean`):

If none of the preferred languages exist, return the first available track (manual first). Off = mark the video as no track (not charged).

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

Adds segments\[] with start, duration (seconds) and text. Plain transcript text is always included.

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

Also return ready-to-use subtitle files as text fields: srt and/or vtt (built from the timestamped segments).

## `includeSearchRows` (type: `boolean`):

In search mode also output the ranked search listing (type: search-result). Never charged.

## Actor input object example

```json
{
  "videoUrls": [
    "https://www.youtube.com/watch?v=kJQP7kiw5Fk",
    "https://www.youtube.com/watch?v=Ks-_Mh1QhMc"
  ],
  "channelUrls": [],
  "playlistUrls": [],
  "maxVideos": 20,
  "languages": [
    "en"
  ],
  "oneRowPerLanguage": false,
  "preferManual": true,
  "fallbackToAnyLanguage": true,
  "includeSegments": true,
  "outputFormats": [],
  "includeSearchRows": false
}
```

# Actor output Schema

## `transcripts` (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=kJQP7kiw5Fk",
        "https://www.youtube.com/watch?v=Ks-_Mh1QhMc"
    ],
    "channelUrls": [],
    "playlistUrls": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("tactful_anvil/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 = {
    "videoUrls": [
        "https://www.youtube.com/watch?v=kJQP7kiw5Fk",
        "https://www.youtube.com/watch?v=Ks-_Mh1QhMc",
    ],
    "channelUrls": [],
    "playlistUrls": [],
}

# Run the Actor and wait for it to finish
run = client.actor("tactful_anvil/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 '{
  "videoUrls": [
    "https://www.youtube.com/watch?v=kJQP7kiw5Fk",
    "https://www.youtube.com/watch?v=Ks-_Mh1QhMc"
  ],
  "channelUrls": [],
  "playlistUrls": []
}' |
apify call tactful_anvil/youtube-transcript-scraper --silent --output-dataset

```

## MCP server setup

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