YouTube Transcript Scraper - Bulk Subtitles to Text, No Browser
Pricing
$5.00 / 1,000 transcript delivereds
YouTube Transcript Scraper - Bulk Subtitles to Text, No Browser
Clean transcript text with timestamped segments, language, auto-generated flag, title and channel for any list of YouTube videos, Shorts or bare IDs. No headless browser; proxy configuration for rate-limited IPs. Charged only for videos that return a transcript.
YouTube Transcript Scraper - subtitles to text with timestamps, from Python or the API
Paste a list of YouTube videos and get one row per video back: the full transcript as clean text, the timestamped caption lines, the language you got, whether it was auto-generated, and the video's title and channel, from watch URLs, Shorts, youtu.be links or bare video IDs. Call it from Python, JavaScript, cURL or an AI agent, or export a CSV. No YouTube Data API key and no headless browser.
- Charged per transcript, nothing else. Captions disabled, a private or removed video, or a request
YouTube keeps blocking: you get a row with a
status, and it costs nothing. - Language fallback that makes sense. Your languages in your order, manually created captions before auto-generated ones, and optional translation through YouTube's own translation.
- Bounded retries. YouTube blocks datacenter IPs, so the Actor rotates residential proxy sessions up to
maxRetriestimes, then reports the video asblocked. - Reliability, measured on our own runs. 128 of 128 runs in the last 30 days succeeded (our account's run history, read 2026-10-02). These are our own test and monitoring runs, not customer runs.
What data you get
video_id,url- the videostatus-ok,no_transcript,captions_disabled,unavailable,blockedorerrortext- the whole transcript as one stringsegments[]- every caption line withstart,duration(seconds) andtext(optional)language,source_language,is_generated- what you got and where it came fromavailable_languages[]- every transcript YouTube offers for the video (language,code,generated)word_count,segment_count,duration_seconds- size of the transcripttitle,channel,channel_url,thumbnail- public video metadata (optional)error- the reason on rows that are notok;translation_noteif translation was not possible
Use cases
- Feed videos to an LLM or RAG pipeline - clean text for summaries, embeddings or question answering.
- Notes from lectures, talks and podcasts - search a long video instead of rewatching it.
- Quote finding with timestamps -
segmentsgive the second each line was said. - Topic and keyword research - what a set of videos talks about, in their own words.
- Translation work - the original captions or YouTube's translation as a starting text.
How to use it (quick start)
- Open the Actor in Apify Console (you need an Apify account).
- Paste your video URLs or IDs into Video URLs or IDs, one per line, and set your preferred languages.
- Click Start. When the run finishes, open the Dataset tab and export as JSON, CSV or Excel.
To call it from your own code, skip to Use it from code.
Input
| Field | What it does | Default |
|---|---|---|
videoUrls | Watch, Shorts, youtu.be, embed or live URLs, or 11-character IDs (required) | one example video |
languages | Language codes in order of preference, e.g. en, en-US, de, es | ["en"] |
allowAutoGenerated | Fall back to automatic captions, then to any available transcript | true |
translateTo | Translate with YouTube's translation; empty keeps the original | "" |
includeSegments | Add the timestamped segments array | true |
includeMetadata | Add title, channel, channel URL and thumbnail | true |
concurrency | Videos processed in parallel (1-20) | 5 |
maxRetries | Fresh proxy sessions to try per video when blocked (1-10) | 4 |
proxyConfiguration | Proxy; YouTube blocks datacenter IPs | Apify residential |
One video, English:
{ "videoUrls": ["https://www.youtube.com/watch?v=dQw4w9WgXcQ"] }
Any URL form, German first, then English (all three are the same video, so this returns one row):
{"videoUrls": ["https://www.youtube.com/watch?v=dQw4w9WgXcQ","https://youtu.be/dQw4w9WgXcQ","dQw4w9WgXcQ"],"languages": ["de", "en"]}
Only human-made captions, translated to Spanish:
{"videoUrls": ["https://www.youtube.com/watch?v=dQw4w9WgXcQ"],"languages": ["en"],"allowAutoGenerated": false,"translateTo": "es"}
Output
A real row from a run with the default input (run S8SrhkA8w19WyC7gj, 2026-10-02 UTC; text and
segments shortened, available_languages trimmed from 6 entries to 2):
{"video_id": "dQw4w9WgXcQ","url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ","available_languages": [{ "language": "English", "code": "en", "generated": false },{ "language": "English (auto-generated)", "code": "en", "generated": true }],"status": "ok","language": "en","source_language": "en","is_generated": false,"segment_count": 61,"duration_seconds": 211.3,"word_count": 487,"text": "[♪♪♪] ♪ We're no strangers to love ♪ ♪ You know the rules and so do I ♪ ...","segments": [{ "start": 1.36, "duration": 1.68, "text": "[♪♪♪]" },{ "start": 18.64, "duration": 3.24, "text": "♪ We're no strangers to love ♪" }],"title": "Rick Astley - Never Gonna Give You Up (Official Video) (4K Remaster)","channel": "Rick Astley","channel_url": "https://www.youtube.com/@RickAstleyYT","thumbnail": "https://i.ytimg.com/vi/dQw4w9WgXcQ/hqdefault.jpg"}
| Field | Meaning |
|---|---|
status | ok means a transcript was returned; any other value says why not |
text | all caption lines joined with spaces |
language / source_language | language returned / language of the source track (differ after translation) |
duration_seconds | end of the last caption line, which can be shorter than the video |
Use it with AI agents (MCP)
Apify's hosted MCP server exposes this Actor as a tool to an MCP client. Add it with the URL below and the
header Authorization: Bearer <YOUR_APIFY_TOKEN>:
{"mcpServers": {"apify": {"url": "https://mcp.apify.com?tools=actors,docs,leekung125/youtube-transcript-scraper","headers": { "Authorization": "Bearer YOUR_APIFY_TOKEN" }}}}
Clients that support it can also sign in through the browser instead of pasting a token. Example prompt:
Get the English transcript of https://www.youtube.com/watch?v=dQw4w9WgXcQ and summarize it in five bullet points.
Use it from code (Python, JavaScript, cURL)
Python (pip install apify-client):
from apify_client import ApifyClientclient = ApifyClient("YOUR_APIFY_TOKEN")run = client.actor("leekung125/youtube-transcript-scraper").call(run_input={"videoUrls": ["https://www.youtube.com/watch?v=dQw4w9WgXcQ"],"languages": ["en"],})for row in client.dataset(run.default_dataset_id).list_items().items:print(row["status"], row.get("title"))
JavaScript (npm i apify-client):
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });const run = await client.actor('leekung125/youtube-transcript-scraper').call({videoUrls: ['https://www.youtube.com/watch?v=dQw4w9WgXcQ'],languages: ['en'],});const { items } = await client.dataset(run.defaultDatasetId).listItems();console.log(items);
cURL (runs the Actor and returns the rows in one request):
curl -X POST "https://api.apify.com/v2/acts/leekung125~youtube-transcript-scraper/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \-H "Content-Type: application/json" \-d '{"videoUrls": ["https://www.youtube.com/watch?v=dQw4w9WgXcQ"], "languages": ["en"]}'
Schedules, webhooks and integrations (Make, Zapier, n8n, Google Sheets) work with this Actor as with any Apify Actor.
Pricing
$0.005 per transcript delivered - that is $5.00 per 1,000 transcripts. The event is charged once per video
whose row has status: "ok". You pay only this event price; there is no separate charge to you for compute or
other platform usage on this Actor.
| Transcripts delivered | Cost |
|---|---|
| 1,000 | 1,000 x $0.005 = $5.00 |
| 10,000 | 10,000 x $0.005 = $50.00 |
| 100,000 | 100,000 x $0.005 = $500.00 |
Never charged: rows with no_transcript, captions_disabled, unavailable, blocked or error, and
retries. A video listed twice is fetched once. If the run reaches its maximum charge limit, the Actor stops
starting new videos.
Limits and honesty notes
- Transcripts are what YouTube provides: manually uploaded captions when they exist, otherwise automatic
captions, whose accuracy depends on the audio.
is_generatedtells you which you got. - Output is JSON rows (exportable to CSV or Excel), not SRT or VTT files.
- Live streams without captions, members-only and age-restricted videos may have no transcript.
- With
allowAutoGenerated: trueand none of your languages available, you get the first transcript YouTube lists, in its own language; checklanguage. Set it tofalsefor human-made captions in your languages only. - If a caption track cannot be translated, you get the original language and a
translation_note. - Title and channel come from YouTube's public oEmbed endpoint, best effort; if that call fails the row lacks
them.
blockedrows carry no metadata. - A video YouTube blocks on every retry comes back
blocked, uncharged. Run it later or raisemaxRetries(max 10). - Entries that are not a YouTube URL or 11-character ID are skipped. If no entry is valid, the run fails with a message saying so.
- Public data only. No login; it reads captions and public oEmbed metadata.
FAQ
Do I need a YouTube API key? No YouTube API key. You need an Apify account, and an Apify token to call it from code.
Is it legal to scrape YouTube transcripts? It reads only public captions and metadata, with no login. You are responsible for lawful use and for following YouTube's terms, including copyright in the transcripts. Do not resell personal data.
Can I get SRT or VTT subtitle files?
Not directly. segments holds start, duration and text for every caption line, which is all an SRT or
VTT file needs if you write one yourself.
Can I export to CSV or Excel? Yes, from the run's Dataset tab (CSV, Excel, JSON and more), or through the API.
Can I run it on a schedule? Yes, with Apify Schedules, like any Actor.
Can I use it from an AI agent or MCP? Yes, through Apify's hosted MCP server. See Use it with AI agents (MCP).
Why did I get fewer rows than I asked for? Duplicate links to the same video are merged into one row, entries that are not a YouTube URL or ID are skipped, and if the run reaches its maximum charge limit the Actor stops starting new videos.
What happens when a video has no transcript or YouTube blocks the request?
You still get a row (no_transcript, captions_disabled, unavailable, or blocked after maxRetries
proxy sessions) with the reason in error. None of these are charged.
What is charged?
Only rows with status: "ok", at $0.005 each. See Pricing.
Related Actors
| Actor | What it does |
|---|---|
| YouTube Channel Transcripts | Every video of a channel or playlist, each with its transcript |
| YouTube Search Scraper | A keyword in, ranked YouTube video rows out |
| Google Play Reviews Scraper | Reviews of Android apps from Google Play |
| App Store Reviews Scraper | Reviews of apps from the Apple App Store, by app and country |
| Google Trends Scraper | Google Trends interest over time, by region, related queries and topics |
Support
Open an issue on the Issues tab with the run ID and your input; we read every issue.