# YouTube Transcript & Subtitles Scraper API (`johnvc/youtubetranscripts`) Actor

Scrape YouTube transcripts, subtitles, and captions in bulk, the cheapest pay-per-video YouTube transcript API on Apify. Callable from any MCP client (Claude, Cursor, ChatGPT). Supports YouTube videos, Shorts, and every URL format.

- **URL**: https://apify.com/johnvc/youtubetranscripts.md
- **Developed by:** [John](https://apify.com/johnvc) (community)
- **Categories:** Videos, AI, Developer tools
- **Stats:** 2,889 total users, 1,777 monthly users, 100.0% runs succeeded, 25 bookmarks
- **User rating**: 4.54 out of 5 stars

## Pricing

from $0.01 / 1,000 video processeds

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?

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 & Subtitles Scraper: Pay-Per-Video MCP API

Scrape **YouTube transcripts, subtitles, and captions** in bulk at near-free pay-per-video rates. Built for **MCP clients** (Claude, Cursor, VS Code, ChatGPT), AI agents, content pipelines, and SEO research. Works with standard videos, **YouTube Shorts**, and every URL format.

> 💡 Pricing: **pay-per-video at near-free rates** - the cheapest pay-per-video YouTube transcript API on Apify. Failed videos are never charged. No subscription, no proxy fees. Current per-video rates are always on this page's pricing card.

***

> **Example code (Python):** <https://github.com/johnisanerd/Apify-Youtube-Transcripts-API>

### ⚡ What you get back

For every video, the dataset receives:

- `non_timestamped` - full transcript text, ready for LLMs.
- `timestamped` - every snippet with `text`, `start`, and `duration` in seconds.
- `language`, `language_code`, `is_generated`, `is_translatable`, `translation_languages`.
- `total_seconds` - video duration.
- `video_id`, `url`, `success`, `timestamp`.
- With the `channel` input: one row per channel video (`result_type: "channel_video"`) with title, duration, view count, and thumbnail - newest first.
- On failure: structured `error`, `error_message`, `error_type` (no crash; the run continues).

***

### 🎯 Use cases

- **LLM training data** - bulk-collect transcripts for fine-tuning or RAG.
- **AI agents that watch YouTube** - let Claude or Cursor pull a transcript on demand via MCP.
- **Content repurposing** - turn videos into blog posts, summaries, or social clips.
- **SEO research** - mine transcripts for keywords, topics, and competitor analysis.
- **Podcast & video search** - index thousands of videos by their actual spoken content.
- **Whole-channel transcription** - point `channel` at any creator to list every video, or transcribe the whole channel in one run.

***

### 🤖 Use with MCP (Claude, Cursor, VS Code, ChatGPT)

This Actor is callable as a tool from any [Model Context Protocol](https://modelcontextprotocol.io) client. The [Apify MCP server](https://docs.apify.com/platform/integrations/mcp) exposes every Actor on the store: **no installation, no separate package**.

#### Step 1: Get an Apify API token

Grab a token from your [Apify account → Integrations](https://console.apify.com/account/integrations?fpr=9n7kx3).

#### Step 2: Connect your MCP client

**Claude Desktop / Claude Code**: edit `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com/",
      "headers": { "Authorization": "Bearer YOUR_APIFY_TOKEN" }
    }
  }
}
```

**Cursor**: add `.cursor/mcp.json` in your project (or `~/.cursor/mcp.json` globally):

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com/",
      "headers": { "Authorization": "Bearer YOUR_APIFY_TOKEN" }
    }
  }
}
```

**VS Code**: add `.vscode/mcp.json`:

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com/",
      "headers": { "Authorization": "Bearer YOUR_APIFY_TOKEN" }
    }
  }
}
```

**ChatGPT / web-only clients**: use the hosted [Apify Tester MCP Client](https://apify.com/jiri.spilka/tester-mcp-client?fpr=9n7kx3) in the browser.

#### Step 3: Ask your agent

Once connected, prompt naturally:

> *"Use Apify to get the transcript of https://www.youtube.com/watch?v=1UL8iZJD38U"*

> *"Pull YouTube transcripts for these three URLs and summarize each: …"*

> *"Find the YouTube transcript scraper on Apify and run it for this Shorts URL."*

Your client will discover this Actor via the `search-actors` tool, register it as a callable function, and feed the transcript back into the conversation.

Full reference: [Apify MCP integration docs](https://docs.apify.com/platform/integrations/mcp) · [MCP server source](https://github.com/apify/apify-mcp-server).

***

### 💸 Pay per run with crypto (x402)

The YouTube Transcripts API supports agentic payments via the [x402 protocol](https://docs.apify.com/platform/integrations/x402).
AI agents and MCP clients can pay for runs in USDC (on Base) with no Apify account or API token needed:
point your agent at the [Apify MCP server](https://mcp.apify.com/?tools=actors,docs,johnvc/YoutubeTranscripts) and it can
discover, pay for, and run this Actor autonomously. Read the
[Apify x402 announcement](https://apify.com/change-log/pay-for-apify-actors-with-x402?fpr=9n7kx3) for details.

### 🪢 Use in n8n

Prefer a visual workflow? A dedicated n8n community node wraps this transcript API so you can drop it into any pipeline with no code.

1. In n8n, open **Settings → Community Nodes → Install** and enter the package name `n8n-nodes-youtube-transcripts-api`.
2. Add your Apify API token as an **Apify API** credential (grab the token from your [Apify account → Integrations](https://console.apify.com/account/integrations?fpr=9n7kx3)).
3. Drop the **YouTube Transcripts** node into a workflow, pass one URL or an array of URLs, and wire the transcript output into your next step: Google Sheets, a database, or an LLM node.

The node is published on npm with build provenance: [`n8n-nodes-youtube-transcripts-api`](https://www.npmjs.com/package/n8n-nodes-youtube-transcripts-api). It calls this same Actor, so pricing and output fields match the API and MCP paths above.

***

### 🧰 Use it as a Claude or agent skill

Prefer to drive this API from an AI agent? These open agent skills (the [agentskills.io](https://agentskills.io/specification) standard) each wrap this Actor around a ready-made workflow and install in Claude Code, Cursor, and other skills-compatible agents:

- YouTube transcript API: fetch any video's captions as JSON, SRT, or VTT. [Repo](https://github.com/johnisanerd/claude-skill-youtube-transcript-api) - `npx skills add johnisanerd/claude-skill-youtube-transcript-api`
- YouTube transcripts as LLM training data: build a documented text corpus from a URL list. [Repo](https://github.com/johnisanerd/claude-skill-youtube-transcripts-llm-training-data) - `npx skills add johnisanerd/claude-skill-youtube-transcripts-llm-training-data`

***

### 🔧 Input

| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| `youtube_url` | string **or** array of strings | ✅ unless `channel` is set | - | One URL or many (**max 250 per run**). Works with standard videos, Shorts, `youtu.be`, embed, and mobile URLs. Channel URLs are detected automatically and treated as the `channel` input. Oversized batches are truncated to the first 250 with a dataset notice - split the rest into another run. |
| `languages` | array of strings | - | `["en"]` | Ordered list of ISO 639-1 language codes. First match wins. |
| `translate_to` | string | - | - | If set, translate the picked transcript into this language code. Source must be translatable (see `list_only`). |
| `transcript_type` | enum `any` / `manual` / `generated` | - | `any` | Filter by manually-created vs. auto-generated captions. |
| `output_formats` | array of strings | - | `[]` | Extras: `srt`, `vtt`, `text`. The structured fields are always included; these add formatted-subtitle strings. |
| `preserve_formatting` | boolean | - | `false` | Keep YouTube's inline `<i>` / `<b>` tags in transcript text. |
| `list_only` | boolean | - | `false` | Discovery mode: do not fetch transcripts; return the list of available transcripts per video. Free of the per-video charge. |
| `include_metadata` | boolean | - | `true` | Enrich each result with video metadata: title, description, channel, view count, like count, video duration, upload date, thumbnail URL, tags, categories. Adds ~1-3 seconds per video. Set to `false` to skip and run faster. |
| `channel` | string **or** array of strings | - | - | One or more channels to list: an `@handle`, a bare handle, a channel ID (`UC...`), or any channel URL (`/channel/`, `/c/`, `/user/`, `/@handle`). Returns one row per video (newest first, up to `max_videos`) with `video_id`, `url`, `title`, duration, view count, and thumbnail, marked `result_type: "channel_video"`. Listing is free of the per-video transcript charge. |
| `max_videos` | integer | - | `100` | How many videos to list per channel, newest first. Max 1000. |
| `channel_transcripts` | boolean | - | `false` | Also fetch a transcript for every listed channel video (deduplicated against `youtube_url` entries, max 250 transcripts per run). Each successful transcript is charged exactly like one passed via `youtube_url`. |

#### Single video

```json
{ "youtube_url": "https://www.youtube.com/watch?v=p8gV_7zFN44" }
```

#### Batch (processed in parallel)

```json
{
  "youtube_url": [
    "https://www.youtube.com/watch?v=5kcaHAuGxmY",
    "https://www.youtube.com/watch?v=p8gV_7zFN44",
    "https://www.youtube.com/shorts/s4UkCaf_scs"
  ]
}
```

#### Pick a language

```json
{
  "youtube_url": "https://www.youtube.com/watch?v=p8gV_7zFN44",
  "languages": ["es", "en"]
}
```

Tries Spanish first, falls back to English.

#### Translate to another language

```json
{
  "youtube_url": "https://www.youtube.com/watch?v=p8gV_7zFN44",
  "languages": ["en"],
  "translate_to": "fr"
}
```

Picks the English transcript and translates it to French. If the source isn't translatable, you'll get the original English transcript back without an error.

#### Get SRT + VTT alongside JSON

```json
{
  "youtube_url": "https://www.youtube.com/watch?v=p8gV_7zFN44",
  "output_formats": ["srt", "vtt", "text"]
}
```

Adds `srt`, `vtt`, and `text` fields to each dataset item in addition to the structured `timestamped` and `non_timestamped` fields.

#### Discover what transcripts exist (no charge)

```json
{
  "youtube_url": "https://www.youtube.com/watch?v=p8gV_7zFN44",
  "list_only": true
}
```

Returns `available_transcripts` per video without fetching any captions. Useful to discover which languages and which translations are available before committing to a transcript-fetch run.

#### List every video on a channel

```json
{
  "channel": "@mkbhd",
  "max_videos": 200
}
```

Accepts an `@handle`, a bare handle, a channel ID, or any channel URL - `{ "channel": "https://www.youtube.com/channel/UCBJycsmduvYEL83R_U4JriQ" }` works the same way. Each video becomes one dataset row (newest first) with `video_id`, `url`, `title`, `video_duration_seconds`, `view_count`, `thumbnail_url`, and `result_type: "channel_video"`. Pasting a channel URL into `youtube_url` does the same thing. Listing is not charged per video.

#### Transcribe an entire channel

```json
{
  "channel": "@mkbhd",
  "max_videos": 50,
  "channel_transcripts": true
}
```

Lists the newest 50 videos, then fetches a transcript for each one through the normal pipeline (language selection, translation, metadata, and output formats all apply). Transcripts are capped at 250 videos per run across all channels plus explicit URLs; each successful transcript is charged as one `videoprocessed` event.

> 💡 **Batch limit: 250 URLs per run.** Larger inputs are truncated to the first 250 URLs; a `truncated: true` summary row is written to the dataset with `urls_submitted` / `urls_skipped`, and the Console status message explains what happened. Split bigger jobs into multiple runs. Failed or skipped videos are still recorded (`success: false`, `error_message`) so you can retry only the missing ones.

***

### 📤 Example output

With `include_metadata: true` (the default), each dataset item carries both the transcript and the video metadata:

```json
{
  "url": "https://www.youtube.com/watch?v=p8gV_7zFN44",
  "video_id": "p8gV_7zFN44",
  "language": "English",
  "language_code": "en",
  "is_generated": false,
  "is_translatable": true,
  "translation_languages": ["es", "fr", "de"],
  "total_seconds": 4782.52,
  "duration_human": "1h 19m 42s",
  "source_type": "Manual",
  "snippet_count": 1238,
  "timestamped": [
    { "text": "Hello and welcome to this video", "start": 0.08, "duration": 3.5 }
  ],
  "non_timestamped": "Hello and welcome to this video...",
  "title": "Some video title",
  "channel_name": "Some Channel",
  "channel_id": "UCxxxxxxxxxxxxxxxxxxxxxx",
  "channel_url": "https://www.youtube.com/channel/UCxxxxxxxxxxxxxxxxxxxxxx",
  "view_count": 12345678,
  "like_count": 234567,
  "video_duration_seconds": 4783,
  "upload_date": "2024-05-12",
  "thumbnail_url": "https://i.ytimg.com/vi/p8gV_7zFN44/hqdefault.jpg",
  "tags": ["education", "tutorial"],
  "categories": ["Education"],
  "availability": "public",
  "was_live": false,
  "timestamp": "2026-06-30T10:30:00",
  "success": true
}
```

Set `include_metadata: false` to skip the title/channel/view-count/thumbnail fields and run ~1-3 seconds faster per video.

***

### 💰 Pricing

Pay-per-event, engineered to be the lowest-cost YouTube transcript API on Apify:

| Event | Charged for |
|---|---|
| `videoprocessed` | each video successfully transcribed |
| `apify-actor-start` | one event per GB of memory at run start (Apify-managed) |
| `apify-default-dataset-item` | per item stored in the dataset (Apify-managed) |

The exact per-event rates for your plan are always shown on this page's pricing card (they vary slightly by plan tier). Unlike actors that charge per second of video, this one charges **per video**: a 5-hour lecture costs the same as a 60-second Short. Channel listing rows (`result_type: "channel_video"`) are never charged as `videoprocessed`. No subscription, no proxy fees. Failed videos and videos with no transcript are **not charged**.

***

### 🚀 Quickstart

1. Open this Actor on the [Apify store](https://apify.com/johnvc/YoutubeTranscripts?fpr=9n7kx3).
2. Paste a YouTube URL into `youtube_url` (or an array of URLs).
3. Click **Start**. Results appear in the dataset within seconds.

Prefer the API? Every Actor on Apify has an [API endpoint](https://docs.apify.com/platform/integrations) and can be called from any language, scheduled, or wired into webhooks.

***

### ❓ FAQ

**What if the video has no transcript?**
The Actor records an error entry in the dataset (`success: false`, `error_message: …`) and moves on to the next URL. You are **only charged for successful transcriptions** of the requested videos.

**Does it work for YouTube Shorts?**
Yes. `https://www.youtube.com/shorts/VIDEO_ID` works out of the box, as do `youtu.be/`, `/embed/`, and `m.youtube.com` formats.

**Can I call this from an AI agent?**
Yes, that is the primary design goal. See the **Use with MCP** section above for Claude, Cursor, VS Code, and ChatGPT setup.

**Can I get transcripts for an entire channel?**
Yes. Pass the channel's @handle, ID, or URL in the `channel` input to list its videos newest first (up to `max_videos`), and set `channel_transcripts: true` to also fetch a transcript for each listed video in the same run (up to 250 transcripts per run). Listing alone is free of the per-video transcript charge.

**What about translation?**
Built in: set `translate_to` to a language code (e.g. "es", "fr", "de") and the Actor returns the transcript translated through YouTube's own translation pipeline. Each result also lists `is_translatable` and `translation_languages`, the translation targets YouTube exposes for that transcript (usually around 18 common languages). If your requested target is not available, the original transcript is returned unchanged - check the `translated_to` field to confirm a translation happened. For languages YouTube does not offer, pair the plain-text output with your favorite LLM.

**Why is this so much cheaper than other YouTube transcript scrapers?**
This is a loss-leader: built for adoption, not margin. The actor uses YouTube's own caption tracks via a free open-source library, so the marginal cost is near zero.

***

### 🔗 Links

- [Actor on Apify Store](https://apify.com/johnvc/YoutubeTranscripts?fpr=9n7kx3)
- [n8n community node: n8n-nodes-youtube-transcripts-api](https://www.npmjs.com/package/n8n-nodes-youtube-transcripts-api)
- [Apify MCP integration docs](https://docs.apify.com/platform/integrations/mcp)
- [Model Context Protocol](https://modelcontextprotocol.io)
- [Apify platform docs](https://docs.apify.com/)

***

### Featured Tasks

Ready-to-run examples that show this API solving a specific problem. Each opens its own setup so you can
run it on your account in one click.

- [Get YouTube transcripts in Claude Code via MCP](https://apify.com/johnvc/YoutubeTranscripts/examples/get-youtube-transcripts-in-claude-code-via-mcp?fpr=9n7kx3) - pull any video's transcript straight into [Claude Code](https://claude.ai/referral/uIlpa7nPLg) (free trial available) through MCP, with timestamped and plain text output.
- [Use YouTube transcripts in Claude Cowork via MCP](https://apify.com/johnvc/YoutubeTranscripts/examples/use-youtube-transcripts-in-claude-cowork-via-mcp?fpr=9n7kx3) - fetch transcripts inside [Claude Cowork](https://claude.ai/referral/uIlpa7nPLg) (free trial available) as an MCP plugin for research and document workflows.
- [Download a batch of YouTube transcripts in bulk](https://apify.com/johnvc/YoutubeTranscripts/examples/download-a-batch-of-youtube-transcripts-in-bulk?fpr=9n7kx3) - transcribe a whole list of videos in one run, one dataset row per video with language metadata.

#### For developers

- [Run the YouTube transcript API without IP blocks](https://apify.com/johnvc/YoutubeTranscripts/examples/run-youtube-transcript-api-without-ip-block?fpr=9n7kx3) - fetch transcripts at scale without IpBlocked or RequestBlocked errors.
- [Get YouTube transcripts without API rate limits](https://apify.com/johnvc/YoutubeTranscripts/examples/youtube-transcript-api-no-rate-limit?fpr=9n7kx3) - pull transcripts in bulk without 429 rate-limit errors.
- [Translate YouTube subtitles via API](https://apify.com/johnvc/YoutubeTranscripts/examples/translate-youtube-subtitles-via-api?fpr=9n7kx3) - render any video's captions into another language with the translate\_to input.
- [YouTube transcript API in Python without IP blocks](https://apify.com/johnvc/YoutubeTranscripts/examples/youtube-transcript-api-in-python-no-ip-block?fpr=9n7kx3) and [for Node.js and JavaScript](https://apify.com/johnvc/YoutubeTranscripts/examples/youtube-transcript-api-for-node-js?fpr=9n7kx3).
- [Get a YouTube Shorts transcript by URL](https://apify.com/johnvc/YoutubeTranscripts/examples/get-youtube-shorts-transcript-by-url?fpr=9n7kx3) - paste any Shorts URL and get its transcript.
- [Hosted YouTube transcript MCP server](https://apify.com/johnvc/YoutubeTranscripts/examples/hosted-youtube-transcript-mcp-server?fpr=9n7kx3) - a no-setup MCP server that returns transcripts as a callable tool.

#### Transcripts by language

Translate any video's captions on demand. Representative pages: [Spanish](https://apify.com/johnvc/YoutubeTranscripts/examples/youtube-transcripts-in-spanish?fpr=9n7kx3), [French](https://apify.com/johnvc/YoutubeTranscripts/examples/youtube-transcripts-in-french?fpr=9n7kx3), [German](https://apify.com/johnvc/YoutubeTranscripts/examples/youtube-transcripts-in-german?fpr=9n7kx3), [Japanese](https://apify.com/johnvc/YoutubeTranscripts/examples/youtube-transcripts-in-japanese?fpr=9n7kx3), [Korean](https://apify.com/johnvc/YoutubeTranscripts/examples/youtube-transcripts-in-korean?fpr=9n7kx3), [Russian](https://apify.com/johnvc/YoutubeTranscripts/examples/youtube-transcripts-in-russian?fpr=9n7kx3), [Hindi](https://apify.com/johnvc/YoutubeTranscripts/examples/youtube-transcripts-in-hindi?fpr=9n7kx3), [Portuguese](https://apify.com/johnvc/YoutubeTranscripts/examples/youtube-transcripts-in-portuguese?fpr=9n7kx3), [Arabic](https://apify.com/johnvc/YoutubeTranscripts/examples/youtube-transcripts-in-arabic?fpr=9n7kx3), and more. See the full set on the Examples tab.

In-language landing pages: [YouTube字幕下载 API (Chinese)](https://apify.com/johnvc/YoutubeTranscripts/examples/youtube-subtitle-download-api-chinese?fpr=9n7kx3), [YouTube字幕翻译 API (Chinese)](https://apify.com/johnvc/YoutubeTranscripts/examples/youtube-subtitle-translate-api-chinese?fpr=9n7kx3), [Скачать субтитры YouTube (Russian)](https://apify.com/johnvc/YoutubeTranscripts/examples/download-youtube-subtitles-via-api-russian?fpr=9n7kx3), [Перевод субтитров YouTube (Russian)](https://apify.com/johnvc/YoutubeTranscripts/examples/translate-youtube-subtitles-via-api-russian?fpr=9n7kx3).

#### Use in your MCP client

Connect the actor as a callable MCP tool in [ChatGPT](https://apify.com/johnvc/YoutubeTranscripts/examples/youtube-transcripts-in-chatgpt-via-mcp?fpr=9n7kx3), [Cursor](https://apify.com/johnvc/YoutubeTranscripts/examples/youtube-transcripts-in-cursor-via-mcp?fpr=9n7kx3), [Visual Studio Code](https://apify.com/johnvc/YoutubeTranscripts/examples/youtube-transcripts-in-visual-studio-code-via-mcp?fpr=9n7kx3), [Windsurf](https://apify.com/johnvc/YoutubeTranscripts/examples/youtube-transcripts-in-windsurf-via-mcp?fpr=9n7kx3), or [Cline](https://apify.com/johnvc/YoutubeTranscripts/examples/youtube-transcripts-in-cline-via-mcp?fpr=9n7kx3).

***

### 📡 Where the transcripts come from

Every row in the dataset is built from the caption track that [YouTube](https://www.youtube.com) already publishes on the public video page. Nothing here is run through speech-to-text, so the wording you get back is the same wording a viewer sees in the transcript panel, delivered as `timestamped` snippets with `start` and `duration`, plus one clean `non_timestamped` block for feeding straight into an LLM. Short-form videos are covered the same way: paste a [YouTube Shorts](https://www.youtube.com/shorts) URL and it resolves to the same `video_id` as any other URL format. This Actor is an independent tool. It is not affiliated with, endorsed by, or operated by YouTube or Google, and it is not YouTube's own API.

#### Does YouTube have a public transcript API?

Not one you can point at arbitrary videos for bulk transcript text. [YouTube's](https://www.youtube.com) own Data API will list caption tracks, but actually downloading a caption file through it requires OAuth as the channel owner, so it only works on videos you already control. This Actor reads caption tracks that are public on the video page, which is why it works on videos you do not own, and it returns `language`, `language_code`, `is_generated`, `is_translatable`, and `translation_languages` next to the text so you know exactly which track you got.

#### How is this different from grabbing captions by hand?

By hand you open the video, expand the transcript panel, copy, paste, then do it again. Fine for one video, painful by the twentieth. Send an array of URLs instead and each one lands as its own dataset row with `video_id`, `total_seconds`, the transcript, and, when `include_metadata` is on, the title, channel, view count, and upload date. If you are not sure which languages a batch actually has, run it once with `list_only: true` first: that returns `available_transcripts` per video and is not charged as a `videoprocessed` event.

***

### 🌐 About Alpha OSINT

This Actor is part of [Alpha OSINT](https://www.alphaosint.com), toolset of financial and operations data sources and APIs.
See the [YouTube Transcripts API source page](https://www.alphaosint.com/sources/youtube-transcripts-api/) for related tools and use cases.
For support or requests for this actor, please start a ticket [directly on our support page](https://apify.com/johnvc/YoutubeTranscripts/issues/open?fpr=9n7kx3).

Last Updated: 2026.09.09

# Actor input Schema

## `youtube_url` (type: `string,array`):

Required unless 'channel' is provided. Provide one YouTube URL as a string, or multiple as an array (max 250 URLs per run). Works with standard videos, Shorts, youtu.be short links, embed URLs, and m.youtube.com mobile URLs. Channel URLs (youtube.com/@handle, /channel/, /c/, /user/) are detected automatically and treated as the 'channel' input. Each video URL is processed in parallel. If more than 250 URLs are submitted, only the first 250 are processed and a truncation notice is written to the dataset - split larger batches into multiple runs. Invalid URLs are recorded as errors in the dataset but do not stop the run. Charged per video successfully transcribed.

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

Ordered list of ISO 639-1 language codes. The first available transcript matching one of these languages is returned. Example: \["en", "es", "fr"] tries English first, then Spanish, then French. Defaults to \["en"] when omitted.

## `translate_to` (type: `string`):

Optional. If set to an ISO 639-1 code (e.g. "es", "fr", "de"), the picked transcript is translated to this language. Requires the source transcript to be translatable - run with list\_only=true first to see translation\_languages for each available transcript. If translation is unavailable, the original transcript is returned (no error).

## `transcript_type` (type: `string`):

Filter which transcript variant to consider when both manually-created and auto-generated captions exist for a language. "any" picks whichever matches the language priority first; "manual" only considers human-made captions; "generated" only considers auto-generated.

## `output_formats` (type: `array`):

Optional list of extra output formats to include in the dataset alongside the default structured fields. Allowed values: "srt" (SubRip subtitles), "vtt" (WebVTT subtitles), "text" (plain text, newline-separated). Unknown values are ignored with a warning. The structured fields (timestamped, non\_timestamped, etc.) are always included.

## `preserve_formatting` (type: `boolean`):

If true, keep YouTube's inline HTML formatting tags (<i>, <b>) in transcript text. Default false (strips them for clean text).

## `list_only` (type: `boolean`):

If true, do NOT fetch any transcript. Instead, for each URL return the list of all available transcripts with their language code, generated/manual status, is\_translatable flag, and translation\_languages. Useful for discovering what languages exist before picking one. Not charged as a videoprocessed event.

## `include_metadata` (type: `boolean`):

If true, also fetch video metadata via yt-dlp: title, description, channel\_name, channel\_id, channel\_url, view\_count, like\_count, video\_duration\_seconds, upload\_date, thumbnail\_url, tags, categories, availability, was\_live. Adds about 1-3 seconds per video. If metadata extraction fails, the transcript is still returned and metadata\_error is populated. Defaults to true.

## `channel` (type: `string,array`):

Optional. One YouTube channel as a string, or multiple as an array. Accepts an @handle ("@mkbhd"), a bare handle ("mkbhd"), a channel ID ("UCBJycsmduvYEL83R\_U4JriQ"), or any channel URL (/channel/, /c/, /user/, /@handle; a /videos, /shorts, or /streams tab URL keeps that tab). Each channel produces one dataset row per video, newest first, up to max\_videos: video\_id, url, title, duration, view count, and thumbnail. Rows are marked result\_type="channel\_video". Listing is not charged as a videoprocessed event.

## `max_videos` (type: `integer`):

Maximum number of videos to list per channel, newest first. Default 100. Values above 1000 are capped at 1000 (and values below 1 are raised to 1) with a log warning rather than failing the run. When channel\_transcripts is true, at most 250 videos per run (across all channels plus explicit URLs) proceed to transcript extraction.

## `channel_transcripts` (type: `boolean`):

If true, every video listed from the channel(s) is also queued for transcript extraction (newest first, deduplicated against youtube\_url entries, subject to the 250 videos per run limit). Each successful transcript is charged as a videoprocessed event, exactly like videos passed via youtube\_url. Default false: channels are only listed.

## Actor input object example

```json
{
  "youtube_url": "https://www.youtube.com/watch?v=1UL8iZJD38U",
  "languages": [
    "en"
  ],
  "transcript_type": "any",
  "output_formats": [],
  "preserve_formatting": false,
  "list_only": false,
  "include_metadata": true,
  "max_videos": 100,
  "channel_transcripts": false
}
```

# Actor output Schema

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

Complete transcript data for all processed YouTube videos. Each item includes video metadata (URL, video ID, language info), timestamped transcript snippets with start times and durations, full non-timestamped transcript text, and processing status. Failed videos include error information.

# 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 = {
    "youtube_url": "https://www.youtube.com/watch?v=1UL8iZJD38U",
    "languages": [
        "en"
    ],
    "output_formats": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("johnvc/youtubetranscripts").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 = {
    "youtube_url": "https://www.youtube.com/watch?v=1UL8iZJD38U",
    "languages": ["en"],
    "output_formats": [],
}

# Run the Actor and wait for it to finish
run = client.actor("johnvc/youtubetranscripts").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 '{
  "youtube_url": "https://www.youtube.com/watch?v=1UL8iZJD38U",
  "languages": [
    "en"
  ],
  "output_formats": []
}' |
apify call johnvc/youtubetranscripts --silent --output-dataset

```

## MCP server setup

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

```

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/zPumutvB61fpEsglh/builds/Di6JWHSjaOvhL9jh2/openapi.json
