YouTube Transcript API — Captions to Text, SRT & VTT
Pricing
from $2.40 / 1,000 transcript returneds
YouTube Transcript API — Captions to Text, SRT & VTT
Get the transcript of any YouTube video as timed segments, plain text, SRT or VTT. Picks your preferred language, falls back to auto-generated captions and can machine-translate. Returns title, channel, duration and every available language. Residential proxy included, no API key, no login.
Pricing
from $2.40 / 1,000 transcript returneds
Rating
0.0
(0)
Developer
Insight Solutions
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
12 hours ago
Last modified
Categories
Share
Get the transcript of any YouTube video, as data. Give this Actor a list of YouTube links — or bare video IDs — and get back the spoken words as timed segments, plain text, SubRip (.srt) or WebVTT (.vtt), alongside the video's title, channel, duration, publish date and every caption language it offers.
No API key. No login. No cookies to paste. $4 per 1,000 transcripts, residential proxy included, videos without captions are free, and a run that returns no transcripts costs nothing at all.
Try it in 30 seconds
{"videoUrls": ["https://www.youtube.com/watch?v=iG9CE55wbtY","EJXTZ5jpSmk"],"languages": ["en"],"format": "segments"}
One watch URL, one bare video ID — both work, and so do youtu.be share links, Shorts, embeds and live URLs.
What comes back
{"ok": true,"videoId": "iG9CE55wbtY","url": "https://www.youtube.com/watch?v=iG9CE55wbtY","title": "Do schools kill creativity? | Sir Ken Robinson | TED","channelName": "TED","channelId": "UCAuUUnT6oDeKwE6v1NGQxug","durationSec": 1164,"publishedAt": "2007-01-06T00:00:00.000Z","language": "en", // what you actually got"languageName": "English","isAutoGenerated": false, // human-written caption file, not speech recognition"isTranslated": false, // not machine-translated from another language"languageFallback": false, // true only when none of your languages could be delivered"availableLanguages": [ // every language this video offers, translations included{ "languageCode": "en", "languageName": "English", "isAutoGenerated": false, "isTranslatable": true, "isTranslation": false },{ "languageCode": "es", "languageName": "Spanish", "isAutoGenerated": false, "isTranslatable": true, "isTranslation": false }],"segments": [ // timings in seconds{ "start": 12.32, "duration": 4.08, "text": "Good morning. How are you?" },{ "start": 16.4, "duration": 3.52, "text": "It's been great, hasn't it?" }],"text": "Good morning. How are you? It's been great, hasn't it? …","wordCount": 2543,"charCount": 14208,"retrievedVia": "innertube-android","scrapedAt": "2026-09-09T11:04:22.311Z","source": "youtube.com","sourceUrl": "https://www.youtube.com/api/timedtext?v=iG9CE55wbtY&lang=en&fmt=json3"}
Ask for "format": "srt" or "format": "vtt" and the row gains an srt or vtt field holding a complete, valid subtitle file — numbered cues and HH:MM:SS,mmm for SubRip, a WEBVTT header and HH:MM:SS.mmm for WebVTT, with overlapping cues clamped so a player never doubles a line. Ask for "format": "text" and you get the prose without the segment array.
Use cases
- Summarise or search long-form video — pull
textstraight into an LLM prompt, withwordCountthere so you can size the prompt before you send it. - Build a searchable video archive — one row per video, timings intact, so a search hit can link to
youtube.com/watch?v=ID&t=42s. - Repurpose a channel's back catalogue into articles, show notes or newsletters.
- Ship subtitles —
srtandvttcome out ready to attach to a re-upload or a player. - Feed a research or RAG pipeline — segments carry timestamps, so a citation can point at the second the claim was made.
- Translate a talk — ask for a language the video does not have and YouTube's own machine translation fills it in, flagged as
isTranslated.
How it works, and why it keeps working
YouTube has no public captions API. Everything that reads transcripts reads the same private endpoint the YouTube apps use — and over 2025 and 2026 that endpoint has been progressively fenced off. Single-path scrapers break every few months. This Actor runs a chain of five paths and takes the first that answers:
| Rung | What it is | Why it is on the list |
|---|---|---|
| 1 | InnerTube player, Android client | Loosest validation of the four clients, and the fastest path |
| 2 | InnerTube player, Android VR client | The client that is not subject to the proof-of-origin token requirement — the answer when rung 1 returns a token-gated caption URL |
| 3 | InnerTube player, iOS client | A third policy bucket; occasionally the only one serving a given video |
| 4 | InnerTube player, Web client | The browser context. Most likely to be token-gated, so it goes last |
| 5 | Watch page ytInitialPlayerResponse | The same document scraped from HTML. Slow, but it is an ordinary page load and survives some API-level refusals |
Under all five: Apify residential proxy, one pinned session per parallel worker. When YouTube refuses an exit IP — HTTP 429, HTTP 403, or a player response saying "sign in to confirm you're not a bot" — that session is retired and the rung is retried once from a different residential address. Retrying on an address that was just refused only deepens the block, so it is never done.
The retrievedVia field on every row says which rung produced it. If you are monitoring this Actor, that column is the early warning: a shift from innertube-android to the later rungs means the front of the chain is degrading, well before anything actually fails.
How it compares
- Five fallback paths, not one. Most transcript scrapers implement a single client context and break the week YouTube changes it.
- Residential proxy is in the price. YouTube blocks datacenter IPs wholesale, so a transcript scraper without residential egress does not work from a cloud host at all. The proxy is configured by default and the cost is inside the $4/1,000 — you are not billed for proxy traffic on top.
- Failures are free and legible. A video with no captions, a private video or a blocked fetch produces a diagnostic row with an
errorTypeyou can branch on — and no charge. A run that returns nothing at all finishes FAILED with the reason in its status message, never a green run containing an apology. - Language handling that says what it did. You get
language,isAutoGenerated,isTranslatedand the fullavailableLanguageslist, so a fallback is never silent.
Input reference
| Field | Type | Default | What it does |
|---|---|---|---|
videoUrls (required) | array of strings | prefilled with two captioned videos | Watch URLs, youtu.be links, Shorts, embed or live URLs, or bare 11-character video IDs. Duplicates are fetched, and billed, once |
languages | array of strings | ["en"] | Language codes in order of preference. See the fallback order below |
format | segments | text | srt | vtt | segments | The primary representation. srt and vtt are only added to the row when you ask for them |
includeTimestamps | boolean | true | Keep the segments array. Turn off for a much smaller row |
includeMetadata | boolean | true | Fill in title, channel, duration and publish date. Costs no extra request |
maxConcurrency | integer | 4 | Videos in parallel. Each worker keeps its own proxy session |
videoTimeoutSecs | integer | 30 | The whole budget for one video, across every rung, retry and the caption download. Any single request is additionally capped at 15 s |
maxRunSecs | integer | 240 | Whole-run wall-clock budget. When it runs out the Actor keeps what it has and files a free diagnostic row for each video it never reached |
proxyConfiguration | object | Apify residential | Leave it alone. Clearing it sends requests from the run's datacenter address, which YouTube will block |
How languages resolves, in order — and the row always says which step won:
- a human-written track in one of your languages (exact code first, then the base language:
enacceptsen-GB); - an auto-generated track in one of your languages;
- a machine translation into one of your languages — the translation is always attempted when the track is translatable, even if YouTube's response did not list your language as a target;
- any human-written track the video has;
- any auto-generated track.
Output reference
Every row carries the same keys. ok: true is a transcript; ok: false is a free diagnostic row.
| Field | What it is |
|---|---|
videoId, url, input | The video, its canonical watch URL, and the entry you supplied |
title, channelName, channelId, durationSec, publishedAt | Video metadata, when includeMetadata is on |
language, languageName | The language you actually got |
isAutoGenerated | true when the text came from speech recognition rather than a human-written caption file |
isTranslated | true when YouTube machine-translated an existing track for you |
languageFallback | true when none of your languages could be delivered, so language is something else |
availableLanguages[] | Every language the video offers, machine-translation targets included |
segments[] | { start, duration, text } in seconds |
text | Every segment joined with single spaces |
srt / vtt | A complete subtitle file — present only when that format was requested |
wordCount, charCount | Size of text |
ok, error, errorType | Whether this row is a transcript, and if not, why not |
retrievedVia | Which rung of the fallback chain produced it |
scrapedAt, source, sourceUrl | When, from where. The caption URL's expiring signature is stripped before it is written |
errorType on a diagnostic row is one of:
| Value | Meaning | Charged? |
|---|---|---|
no-captions | The video has no caption track at all | No |
private | Private, members-only or age-restricted, so a signed-in account would be needed. This Actor never signs in | No |
unavailable | Deleted, region-locked or otherwise unplayable | No |
blocked | YouTube refused our requests, including when the caption file is gated behind a proof-of-origin token | No |
invalid-id | The entry was not a YouTube video — a channel, a playlist, or something else | No |
timeout | The run's maxRunSecs budget ran out before this video was reached | No |
Pricing
$4.00 per 1,000 transcripts. Pay-per-event, with the residential proxy already inside that number — there is no separate proxy line on your bill for this Actor.
| Event | What triggers it | FREE | Starter | Scale | Business |
|---|---|---|---|---|---|
| Transcript returned (primary) | One transcript written to your dataset | $0.004 | $0.004 | $0.0032 | $0.0024 |
| Run started | Once per run, after the first transcript | $0.001 | $0.001 | $0.001 | $0.001 |
Worked example. 250 videos, of which 240 have captions:
- 240 transcripts × $0.004 = $0.96
- 1 run start = $0.001
- 10 videos without captions = $0.00
- Total: $0.961
What you are never charged for: a video with no captions, a private or deleted video, an entry that was not a video, a video the run never reached before maxRunSecs, or a fetch YouTube blocked. If a whole run comes back empty it finishes FAILED and bills nothing at all, start fee included.
Set ACTOR_MAX_TOTAL_CHARGE_USD on a run and the Actor stops fetching once the ceiling is in sight, rather than handing you rows it cannot bill or billing you for rows it cannot hand over.
Limits, and the one that might bite
Videos without captions. Plenty of YouTube has no caption track at all — short clips, music videos, some live streams, and anything an uploader has explicitly opted out of. There is nothing to fetch; you get a free no-captions row saying so.
Age-restricted, private and members-only videos. These need a signed-in account. This Actor does not log in, does not accept cookies and does not take a session token, and it never will — that is a deliberate line, not a missing feature. They return a free private row.
Proof-of-origin ("PO") tokens — the real risk. Through 2025 and 2026 YouTube has been rolling out an experiment that marks a caption URL with exp=xpe, and the caption endpoint then answers HTTP 200 with an empty body unless the request carries a token minted by YouTube's own player JavaScript. There is no way to produce that token without running YouTube's JavaScript in a browser.
What this Actor does about it:
- it detects the marker on the caption URL before spending a request on it;
- it moves to the next client in the chain, because the clients are gated independently — an Android VR caption URL is routinely clean when the Android or Web one is not. This is the single reason rung 2 exists;
- if every rung is gated, it writes a free
blockeddiagnostic row whoseerrorsays exactly that, rather than an empty transcript or a silent success.
Today the chain gets past this for the overwhelming majority of videos. If YouTube ever extends the token requirement to every client context, transcripts would stop being retrievable by any means short of a full browser — for this Actor and for every other tool of its kind. We would say so on this page rather than quietly returning worse data, and the run would still cost you nothing.
Rate and reliability. Requests go out through residential addresses with per-worker sessions and one rotation per block. Four videos in parallel is the default because it is where throughput and block rate balance; raising maxConcurrency speeds a long list up and makes blocks more likely.
Use it from an AI agent, or from code
One JSON object in, one flat array out — the shape agent runtimes want. The Actor runs with limited permissions, uses pay-per-event pricing and never enters Standby, so it works over the Apify MCP server and with x402 agentic payments. The Integrations tab pushes results to Slack, a webhook, Zapier, Make, Google Sheets, Snowflake or BigQuery.
curl -X POST "https://api.apify.com/v2/acts/insight.solutions~youtube-transcript-api/run-sync-get-dataset-items?token=$APIFY_TOKEN" \-H "Content-Type: application/json" \-d '{"videoUrls":["iG9CE55wbtY"],"format":"text"}'
# pip install apify-clientfrom apify_client import ApifyClientclient = ApifyClient("<APIFY_TOKEN>")run = client.actor("insight.solutions/youtube-transcript-api").call(run_input={"videoUrls": ["https://www.youtube.com/watch?v=iG9CE55wbtY","https://youtu.be/EJXTZ5jpSmk",],"languages": ["en"],"format": "segments",})for row in client.dataset(run["defaultDatasetId"]).iterate_items():if row.get("ok"):print(row["title"], row["wordCount"], "words", sep=" | ")else:print("skipped:", row["input"], row["errorType"])
Set "format": "text" and "includeTimestamps": false when you only need the words — the payload shrinks by roughly half and costs the same.
FAQ
Does this work on any video? Any public video that has a caption track. Videos with captions turned off, and private, members-only or age-restricted videos, return a free diagnostic row instead.
Are auto-generated captions any good?
They are usable and they are clearly labelled — isAutoGenerated: true. Expect almost no punctuation, sentence boundaries you cannot trust, and mangled proper nouns. A human-written track is always preferred when the video has one.
Can I get a language the video does not have?
Often, yes. If any track is translatable, the Actor asks YouTube for your language and you get a machine translation, flagged isTranslated: true. If YouTube serves nothing for that language, you get the original track and the row says so (languageFallback: true). The quality is YouTube's machine translation, not ours.
Why do I sometimes get a different language than I asked for?
Because the video did not offer yours and could not translate into it. The row still tells you exactly what you got — language, plus the full availableLanguages list, so you can decide what to do next.
Do I need my own proxy? No. Apify residential proxy is configured by default and its cost is inside the per-transcript price.
Will it handle a playlist or a whole channel? Not yet — this Actor takes videos, one row each. Feed it a list of video URLs from wherever your list comes from.
What happens if one video fails?
The others still run. The failed one produces a free diagnostic row and the run finishes SUCCEEDED. If every video fails the run finishes FAILED and you are billed nothing at all.
How long can a run take?
maxRunSecs, 240 seconds by default, checked before each video. When it runs out the Actor stops fetching, keeps and bills for everything already collected, files a free timeout row for each video it never reached, and finishes.
Is the data fresh? Live. Every run reads YouTube at that moment; nothing is cached.
Legal and data-protection notes
- Public videos only. Every source is a public video page or the public caption endpoint behind it. The Actor never logs in, never accepts cookies or session tokens, never takes an API key belonging to anyone else, and never touches private, members-only or age-restricted content.
- Captions are the uploader's copyright. This Actor retrieves them; it does not license them. Republishing a transcript, or training on it, is your call and your responsibility, subject to YouTube's terms and to copyright law where you operate. Quoting, summarising, searching and indexing are the ordinary uses and are what this is built for.
- Expiring credentials are stripped. The caption URL YouTube signs is a short-lived bearer credential.
sourceUrlrecords where the transcript came from with that signature removed. - No personal data by design. A transcript is the spoken content of a public video. The Actor stores no viewer data, no account data and no cookies, and it writes only the video's own public metadata alongside the text.
- Not affiliated with YouTube, Google LLC, or with any channel or creator whose video you retrieve. All product names and trademarks belong to their respective owners and are used only to describe which public endpoints this Actor reads.
Our other Actors
Every Insight Solutions Actor is pay-per-result with no browser, no login and no API key, and every one of them returns free diagnostic rows instead of billing for failures. Prices are per 1,000 results.
Video, audio & social
- YouTube Comments API — comments and replies with likes, pinned and hearted flags, newest or top sort.
- YouTube Channel API — a channel's videos, Shorts and live streams, plus YouTube search.
- Podcast Search, Episodes & Charts API — Apple Podcasts search, charts and full episode feeds.
- Bluesky Scraper — profiles, posts, followers and follows from the public AT Protocol API.
- Telegram Channel Scraper — posts, views and channel stats from public Telegram channels.
- Substack Scraper — posts with full free text, comments and publication profiles.
News, documents & the web
- Google News Search, Topics & Real Article URLs — news search and topic feeds with the publisher's real URL decoded.
- Website to Markdown — Content Extractor for LLMs & RAG — any site as clean Markdown, text and heading-aware chunks.
- Internet Archive API — archive.org search, item metadata, files and reviews.
- Wayback Machine Toolkit — archived URL inventories, snapshots and text diffs between dates.
- Website Technology Detector — the tech stack behind any site, with the evidence for each detection.
- Domain Intelligence API — DNS, RDAP registration, TLS certificate and HTTP facts in one row per domain.
- SEO Page Audit — sitemap crawl with on-page checks, structured data and broken-link reports.
- Keyword Suggestions API — Google, YouTube, Bing, Amazon and eBay autocomplete with alphabet and question expansions.
- Website Contact Extractor — emails, phone numbers and social profiles from any list of websites.
Business, finance & jobs
- Congress & Insider Trades API — STOCK Act periodic transaction reports and SEC Form 4 insider trades in one schema.
- SEC EDGAR API — filings, XBRL financials and full-text search by ticker or CIK.
- Y Combinator Companies, Batches & Founders — the YC directory with founders and social links, filterable by batch, industry and hiring status.
- Career Site Jobs API — jobs straight from Greenhouse, Lever, Ashby, Workable and 10+ other ATS career sites.
- New Job Postings Monitor — new, closed and changed postings on the career sites you watch.
- Shopify Products API — any Shopify store's catalogue, variants, prices and stock signals.
Apps & games
- App Store & Google Play Reviews API — reviews from both stores with ratings, versions and developer replies.
- App Store Top Charts & App Search API — Apple top charts by country and genre, plus app search and details.
- Steam Reviews API — Steam reviews with playtime, helpfulness and game details.
- Steam Game Data API — prices, tags, review scores, live player counts and top charts.