YouTube Transcript & Subtitles Scraper API avatar

YouTube Transcript & Subtitles Scraper API

Pricing

from $0.01 / 1,000 video processeds

Go to Apify Store
YouTube Transcript & Subtitles Scraper API

YouTube Transcript & Subtitles Scraper API

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.

Pricing

from $0.01 / 1,000 video processeds

Rating

4.5

(9)

Developer

John

John

Maintained by Community

Actor stats

23

Bookmarked

2.3K

Total users

1.5K

Monthly active users

11 hours ago

Last modified

Share

🎥 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 client. The Apify MCP server 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.

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):

{
"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):

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

VS Code: add .vscode/mcp.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 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=dQw4w9WgXcQ"

"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 · MCP server source.


💸 Pay per run with crypto (x402)

The YouTube Transcripts API supports agentic payments via the x402 protocol. 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 and it can discover, pay for, and run this Actor autonomously. Read the Apify x402 announcement 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).
  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. 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 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 - 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 - npx skills add johnisanerd/claude-skill-youtube-transcripts-llm-training-data

🔧 Input

ParameterTypeRequiredDefaultDescription
youtube_urlstring 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.
languagesarray of strings-["en"]Ordered list of ISO 639-1 language codes. First match wins.
translate_tostring--If set, translate the picked transcript into this language code. Source must be translatable (see list_only).
transcript_typeenum any / manual / generated-anyFilter by manually-created vs. auto-generated captions.
output_formatsarray of strings-[]Extras: srt, vtt, text. The structured fields are always included; these add formatted-subtitle strings.
preserve_formattingboolean-falseKeep YouTube's inline <i> / <b> tags in transcript text.
list_onlyboolean-falseDiscovery mode: do not fetch transcripts; return the list of available transcripts per video. Free of the per-video charge.
include_metadataboolean-trueEnrich 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.
channelstring 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_videosinteger-100How many videos to list per channel, newest first. Max 1000.
channel_transcriptsboolean-falseAlso 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

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

Batch (processed in parallel)

{
"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

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

Tries Spanish first, falls back to English.

Translate to another language

{
"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

{
"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)

{
"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

{
"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

{
"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:

{
"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:

EventCharged for
videoprocessedeach video successfully transcribed
apify-actor-startone event per GB of memory at run start (Apify-managed)
apify-default-dataset-itemper 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.
  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 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.



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.

For developers

Transcripts by language

Translate any video's captions on demand. Representative pages: Spanish, French, German, Japanese, Korean, Russian, Hindi, Portuguese, Arabic, and more. See the full set on the Examples tab.

In-language landing pages: YouTube字幕下载 API (Chinese), YouTube字幕翻译 API (Chinese), Скачать субтитры YouTube (Russian), Перевод субтитров YouTube (Russian).

Use in your MCP client

Connect the actor as a callable MCP tool in ChatGPT, Cursor, Visual Studio Code, Windsurf, or Cline.


📡 Where the transcripts come from

Every row in the dataset is built from the caption track that YouTube 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 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 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, toolset of financial and operations data sources and APIs. See the YouTube Transcripts API source page for related tools and use cases. For support or requests for this actor, please start a ticket directly on our support page.

Last Updated: 2026.08.30