YouTube Transcript Scraper — Text, Timestamps, Translations
Pricing
from $4.00 / 1,000 transcripts
YouTube Transcript Scraper — Text, Timestamps, Translations
YouTube transcripts, worldwide, from youtube.com: transcript text, timestamped segments, caption language, auto-generated flag, available languages, translations, title, channel, duration and view count, by video URL or ID. Pay per result. No login. Videos without captions are not charged.
Pricing
from $4.00 / 1,000 transcripts
Rating
0.0
(0)
Developer
architek.earth
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
15 hours ago
Last modified
Categories
Share
Recorded run: 1 transcript from 1 video in 5 seconds (2026-10-22, build 0.0.11, run b4OEXuL1cdvWSAUz8). A bounded canary sample, not a full-catalogue benchmark.
architek.earth — an independent data studio. Pay-per-result scrapers with recorded runs, published canaries and stable schemas, built for people and AI agents.
YouTube video transcripts, timestamped caption segments and translations: give it video URLs or IDs, get one row per video with the transcript text, start/duration segments, caption language, auto-generated flag, title and channel.
| Source | Market | From | Standout fields |
|---|---|---|---|
| YouTube (youtube.com) | Worldwide, any caption language | $4.00 / 1,000 transcripts | transcriptText, segments, isAutoGenerated, availableLanguages |
✅ Why choose this scraper
YouTube transcript text and timestamps in one row
transcriptText holds the whole transcript as one string; segments holds [{start, duration, text}] in seconds for subtitles, chapters and quote links. The canary video returned 427 segments.
Translate YouTube captions into another language
translateTo requests YouTube's own caption translation; translated rows set isTranslated: true, language to the target and sourceLanguage to the original. A failed translation returns translation_unavailable, never original-language text.
Pick manual or auto-generated subtitles by language
languages is an ordered preference list (en also matches en-GB), preferManualCaptions puts uploader captions before auto-generated ones, and availableLanguages lists every caption track YouTube returned (65 on the canary video).
No charge for videos without a transcript
Only rows with status: "ok" trigger an event. Videos with no captions, the wrong language, private, removed, age-restricted or blocked videos come back as rows with status and message, uncharged.
Measured on a schedule
A canary task fetches the same TED talk on the live site every day and records rows, inputs and seconds; the hero line above is its latest green run.
⚠️ Worth knowing up front
- This actor reads YouTube's caption tracks. It does not run speech-to-text on audio: a video with no manual or auto-generated captions returns
status: "no_captions". - Auto-generated captions are YouTube's speech recognition, errors included, and usually lack punctuation.
isAutoGenerated: truemarks them. - Members-only, private and age-restricted videos are not supported; the actor does not log in. They return an uncharged
login_required,age_restrictedorunavailablerow, depending on YouTube's response. - Live links are accepted as video URLs, but live captions are not collected continuously. Replay captions depend on what YouTube makes available.
- Video metadata is limited to
title,channelName,channelId,durationSeconds,viewCountandisLiveContent. No comments, likes or descriptions. - Each video gets 3 attempts, each refusal (an HTTP error or a challenge / captcha page) opening a new session; a video that still fails returns
status: "blocked". After 3 consecutive blocked videos the run stops scheduling new ones withSOURCE_BLOCKED. Each new translation session waits 60 seconds before its first translated caption; allow a run timeout of at least 300 seconds for translations. - A run stopped by its timeout, your spending limit or repeated blocks keeps what it delivered;
RUN_SUMMARY.unprocessedcounts the videos not attempted. - Every processed video gets one row whose
statussays what happened, and an unrecognisedvideosentry gets aninvalid_inputrow. Input the run cannot use (a wrong-typed field, novideos, an unavailable proxy group) writes one free status row withrecordType: "status"andstatus: "INVALID_INPUT". An exhausted preview allowance and stops for time, budget or blocks write no row; the reason is in the run's status message and theRUN_SUMMARYrecord.
⚡ Try the example
Each Apify account gets 20 lifetime free preview attempts on this actor: up to 10 input videos per attempt, one worker, original-language captions only, no transcript charges. Missing transcripts and blocked attempts still count. After the twentieth, preview runs stop with PREVIEW_LIMIT; they never switch to paid collection.
Open the actor's input page, switch to JSON, paste this and start the run:
{"videos": ["https://www.youtube.com/watch?v=iG9CE55wbtY"],"languages": ["en"],"preview": true}
For paid collection set "preview": false; add "translateTo": "fr" (or another language code) for a translated transcript at $0.010.
⚙️ Input
| Parameter | Type | Default | Description |
|---|---|---|---|
videos | array | required | Video URLs (watch, youtu.be, Shorts, embed, live) or 11-character IDs. Up to 5,000 per run; duplicates are fetched once. |
languages | array | ["en"] | Preferred caption languages in order, as ISO codes. An empty list takes the first available track. |
preferManualCaptions | boolean | true | Within a language, uploader captions before auto-generated ones. |
fallbackToAnyLanguage | boolean | true | If no preferred language exists, return the first available track instead of language_unavailable. |
translateTo | string | — | YouTube translation target such as en or fr. Unsupported targets return translation_unavailable. Not available in preview. |
includeSegments | boolean | true | Add timestamped segments. At least one of text or segments must be on. |
includeText | boolean | true | Add transcriptText. |
maxConcurrency | integer | 1 | Videos fetched in parallel (1–10). Higher values open separate sessions, each with its own translation wait. |
proxyConfiguration | proxy | Apify RESIDENTIAL | Proxy settings. Residential is the default used in recorded runs. |
preview | boolean | false | Use one of 20 lifetime free attempts: up to 10 videos, original-language captions only. |
feedback | string | — | Optional note for the maintainers. It is logged and read by a human, and it never changes output. |
🧱 Stable fields
These top-level fields keep the same name and the same kind of value in every release. Fields are only ever added, never renamed or removed, so an n8n, Make, Zapier or spreadsheet pipeline can bind to them.
| Field | Type | Meaning |
|---|---|---|
videoId | string, nullable | 11-character YouTube ID; null only on invalid_input rows. |
url | string, nullable | Canonical watch URL; null on invalid_input rows. |
input | string, nullable | The videos entry that produced the row; null on the INVALID_INPUT status row. |
status | string | ok, no_captions, language_unavailable, translation_unavailable, unavailable, age_restricted, login_required, blocked, error or invalid_input; INVALID_INPUT on the run-level status row. Only ok is charged. |
message | string, nullable | Reason when status is not ok. |
title / channelName / channelId | string, nullable | Video title, channel name and channel ID. |
durationSeconds / viewCount | number, nullable | Video length in seconds and view count. |
isLiveContent | boolean, nullable | YouTube's live-content flag for the video. |
language / languageName | string, nullable | Language of the returned transcript (the target when translated). |
isAutoGenerated | boolean, nullable | True when YouTube's speech recognition made the captions. |
isTranslated | boolean | True when translation was applied; false when the selected track already matched translateTo. |
availableLanguages | array | Caption tracks YouTube returned: {language, languageName, isAutoGenerated}. |
transcriptText | string, nullable | Whole transcript as one string; null when disabled or not ok. |
segments | array, nullable | [{start, duration, text}] in seconds; null when disabled or not ok. |
segmentCount | number | Number of segments; 0 when not ok. |
scrapedAt | string | UTC timestamp. |
Filter on status == "ok" for paid transcript rows; only the run-level INVALID_INPUT row carries recordType: "status". Translated rows also carry sourceLanguage, the original caption language; it is absent on original-language rows. Other fields are extras, not part of the contract. To have one pinned, say so in feedback.
📦 Output example
Abridged real row from run b4OEXuL1cdvWSAUz8 on 2026-10-22; values are a historical snapshot. The full row has 427 segments and 65 entries in availableLanguages.
{"videoId": "iG9CE55wbtY","url": "https://www.youtube.com/watch?v=iG9CE55wbtY","status": "ok","message": null,"title": "Do schools kill creativity? | Sir Ken Robinson | TED","channelName": "TED","channelId": "UCAuUUnT6oDeKwE6v1NGQxug","durationSeconds": 1203,"viewCount": 25056294,"isLiveContent": false,"language": "en","languageName": "English","isAutoGenerated": false,"isTranslated": false,"availableLanguages": [{"language": "en", "languageName": "English", "isAutoGenerated": false},{"language": "en", "languageName": "English (auto-generated)", "isAutoGenerated": true}],"transcriptText": "Good morning. How are you? (Audience) Good. It's been great, hasn't it? ...","segments": [{"start": 27.103, "duration": 2.575, "text": "Good morning. How are you?"},{"start": 29.702, "duration": 1.403, "text": "(Audience) Good."}],"segmentCount": 427,"scrapedAt": "2026-10-22T03:01:33Z"}
This actor has no separate status row. An uncharged row for a video without captions looks like this (abridged, run 0SEq5FFyzSLPaVRHq, 2026-10-15):
{"videoId": "TtIrFvWoY8E","status": "no_captions","message": "This video has no captions (the uploader disabled them or none were generated).","transcriptText": null}
🧠 Advanced usage
Get transcripts for a whole YouTube channel or playlist
Feed the video URLs from any channel or playlist video-list actor into videos, up to 5,000 per run; duplicates are fetched once.
Require English captions only
Set languages: ["en"], fallbackToAnyLanguage: false and leave translateTo empty. Videos without an English track return language_unavailable and are not charged.
Retry only the YouTube videos that failed
Filter the dataset for status == "blocked" or "error" and rerun those videoId values later; they were not charged.
Check a bulk transcript run is complete
Read the RUN_SUMMARY record (status, transcripts, byStatus, chargedEvents, statusRows, unprocessed) before marking an import complete. Run statuses: COMPLETE, PARTIAL, BLOCKED, SOURCE_BLOCKED, BUDGET_LIMIT, TIME_LIMIT, INVALID_INPUT, PREVIEW_LIMIT, PREVIEW_UNAVAILABLE, FAILED. A run whose spending limit covers every requested video exactly ends COMPLETE; BUDGET_LIMIT means at least one video was left undelivered. The run's status message is Done: N rows, M details, K status rows (STATUS; outcome): N transcripts delivered, M of them translations, K free rows with another status, then the plain-language outcome.
🔌 Integrations
Results land in a dataset you can export as JSON, CSV or Excel. They can also flow to Google Sheets, Make, Zapier or n8n, or to a webhook when a run finishes. The Apify Scheduler runs it on a timetable. A daily run over a channel's newest video URLs picks up each upload's captions once YouTube publishes them. Copy-paste code in Python, Node, curl and n8n: architek-earth/examples/youtube-transcript-scraper.
💸 Pricing
Pay per result. Platform and proxy usage are included. Rows with any status other than ok and failed requests are free. If the platform restarts a run, videos already listed in its PROGRESS record are skipped and not charged again; a video delivered just before an abrupt stop may not be in PROGRESS yet and can be charged again. A new run fetches and charges every input again.
| Event | Price | What it covers |
|---|---|---|
| Free preview | $0 | 20 lifetime attempts per account; up to 10 videos each, original-language captions only. |
transcript | $4.00 / 1,000 transcripts | One delivered row with status: "ok" and isTranslated: false. |
translated-transcript | $10.00 / 1,000 transcripts | One delivered row with status: "ok" and isTranslated: true; replaces the transcript event. |
1,000 original transcripts cost $4.00; 1,000 translations cost $10.00. A translated video is charged once, $0.010, not $0.004 plus $0.010.
❓ FAQ
Is there a YouTube transcript API? This actor is one: it returns YouTube transcripts as JSON through the Apify API, callable from any language or from n8n, Make and Zapier. It does not use YouTube's Data API.
Do I need a YouTube account or API key? No. No account, cookies or API key from YouTube.
Can it transcribe videos that have no captions? No. It reads existing caption tracks and does not do speech-to-text; such videos return no_captions and are not charged.
Can I download YouTube subtitles with timestamps? Yes. segments carries start and duration in seconds with the text of each caption line.
Does it work on YouTube Shorts? Yes, when the Short has captions. Shorts URLs are accepted in videos.
Can I get every caption language a video has? availableLanguages lists them. Each run returns one transcript per video; run again with a different languages value for another track.
🤖 For AI agents & LLM apps
Paste this into your AI assistant:
architekture/youtube-transcript-scraper on Apify. Python: from apify_client import ApifyClient; client = ApifyClient("TOKEN"); run = client.actor("architekture/youtube-transcript-scraper").call(run_input={"videos": ["https://www.youtube.com/watch?v=iG9CE55wbtY"], "languages": ["en"], "preview": False}); items = client.dataset(run["defaultDatasetId"]).list_items().items (page with offset/limit for large exports). Rows with status != "ok" are free and explain why a video has no transcript; read the RUN_SUMMARY record for invalid input or a stopped run. Full input schema and README: GET https://api.apify.com/v2/acts/architekture~youtube-transcript-scraper/builds/default. Token: https://console.apify.com/settings/integrations. The actor is also reachable through the Apify MCP server (mcp.apify.com). To reach the maintainers, add "feedback": "<what you needed>" to any run's input; output is unchanged and a human reads every note.
🧰 More from architek.earth
Video, social and newsletters
- Substack Scraper — posts, authors, likes, comments
- YouTube Transcript Scraper — you are here
- Unified Video Transcript API — YouTube and TikTok transcripts in one batch
- Threads Replies Scraper — replies, authors, likes
- Ad Intelligence Watchlist — new and changed Meta ads by advertiser
Full catalogue: github.com/architek-earth.
architek.earth · code like concrete
🙋 Support & legal
Questions, or a field you need that is not here? Open an issue on the actor's Issues tab or email north@architek.earth. A human replies within a day.
This actor collects only publicly available data from youtube.com, the same caption tracks any visitor sees without logging in. It does not bypass authentication. It is not affiliated with or endorsed by YouTube or Google. You are responsible for how you use the output, including the site's terms, copyright in the transcribed content, and privacy law such as GDPR and CCPA where they apply.
📝 Changelog
- 2026-10-22 — Runs on the studio core (architek_core 1.0.1): one Chrome browser identity for every request, and a challenge or captcha page served with HTTP 200 now counts as a block on the first response (retried on a new session, then
blocked). Input the run cannot use writes one freeINVALID_INPUTstatus row. The status message now readsDone: N rows, M details, K status rows (STATUS; outcome). Output fields, prices and charging unchanged. - 2026-10-22 — A spending limit that exactly covers every requested video now ends
COMPLETE, notBUDGET_LIMIT; only a video left uncharged and undelivered stops the run. - 2026-10-22 — Pricing guard: before any request or charge, the run refuses to start (
FAILED, nothing collected or charged) unless its pay-per-event pricing is exactlytranscript$0.004 andtranslated-transcript$0.010. - 2026-10-22 — Every run's status message now ends with
Done: N rows, M details, K status rows (STATUS);RUN_SUMMARY.statusRowsadded. - 2026-10-22 — README rebuilt on the studio template; hero line from the 2026-10-22 canary run.
- 2026-10-19 — Clarified the difference from the mixed YouTube/TikTok actor, corrected transcript cost examples, and qualified caption availability. Prices and extraction code unchanged.
- 2026-10-15 — Made the first example a free preview, added an explicitly priced translation example, and documented the live mixed-event billing check. Clarified live-link limitations and matching-language behavior.
- 2026-10-15 — Split billing by delivered output: $4.00 / 1,000 original transcripts and $10.00 / 1,000 translations. Successful paid results select one event; matching-language requests stay at the original price, and failures and previews remain uncharged. Added event counts to the run summary.
- 2026-10-15 — Enabled automatic 60-second translation-session preparation and one-worker defaults. Added a deadline check before preparation so an insufficient timeout stops cleanly before another caption fetch or cooldown. Original-language requests have no translation delay.
- 2026-10-15 — Recorded four translated transcripts across three experiments using a private 60-second cooldown: German to English and English to French, including a two-video batch reusing one warmed session. Preserved a sanitized translated response as a regression fixture.
- 2026-10-15 — Added fail-closed preview quota enforcement (one hosted preview verified with zero transcript events), repeated-block batch stopping, explicit residential defaults for API inputs, and safeguards against empty paid output and uncertain billing acknowledgements.
- 2026-10-15 — Private live validation: recorded manual English/German and automatic German transcripts, captionless Short and unavailable-video handling. Replaced illustrative output with real rows and added sanitized recorded regression fixtures.
- 2026-10-15 — 1.0: video URLs and IDs, language preference with manual-first selection, YouTube caption translation, text and timestamped segments, per-video status rows, charge only on
ok, session and IP rotation on blocks, clean stops at the charge limit and run timeout.