YouTube Transcript Scraper — Text, Timestamps, Translations avatar

YouTube Transcript Scraper — Text, Timestamps, Translations

Pricing

from $4.00 / 1,000 transcripts

Go to Apify Store
YouTube Transcript Scraper — Text, Timestamps, Translations

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

architek.earth

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

15 hours ago

Last modified

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.

SourceMarketFromStandout fields
YouTube (youtube.com)Worldwide, any caption language$4.00 / 1,000 transcriptstranscriptText, 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: true marks them.
  • Members-only, private and age-restricted videos are not supported; the actor does not log in. They return an uncharged login_required, age_restricted or unavailable row, 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, viewCount and isLiveContent. 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 with SOURCE_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.unprocessed counts the videos not attempted.
  • Every processed video gets one row whose status says what happened, and an unrecognised videos entry gets an invalid_input row. Input the run cannot use (a wrong-typed field, no videos, an unavailable proxy group) writes one free status row with recordType: "status" and status: "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 the RUN_SUMMARY record.

⚡ 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

ParameterTypeDefaultDescription
videosarrayrequiredVideo URLs (watch, youtu.be, Shorts, embed, live) or 11-character IDs. Up to 5,000 per run; duplicates are fetched once.
languagesarray["en"]Preferred caption languages in order, as ISO codes. An empty list takes the first available track.
preferManualCaptionsbooleantrueWithin a language, uploader captions before auto-generated ones.
fallbackToAnyLanguagebooleantrueIf no preferred language exists, return the first available track instead of language_unavailable.
translateTostring—YouTube translation target such as en or fr. Unsupported targets return translation_unavailable. Not available in preview.
includeSegmentsbooleantrueAdd timestamped segments. At least one of text or segments must be on.
includeTextbooleantrueAdd transcriptText.
maxConcurrencyinteger1Videos fetched in parallel (1–10). Higher values open separate sessions, each with its own translation wait.
proxyConfigurationproxyApify RESIDENTIALProxy settings. Residential is the default used in recorded runs.
previewbooleanfalseUse one of 20 lifetime free attempts: up to 10 videos, original-language captions only.
feedbackstring—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.

FieldTypeMeaning
videoIdstring, nullable11-character YouTube ID; null only on invalid_input rows.
urlstring, nullableCanonical watch URL; null on invalid_input rows.
inputstring, nullableThe videos entry that produced the row; null on the INVALID_INPUT status row.
statusstringok, 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.
messagestring, nullableReason when status is not ok.
title / channelName / channelIdstring, nullableVideo title, channel name and channel ID.
durationSeconds / viewCountnumber, nullableVideo length in seconds and view count.
isLiveContentboolean, nullableYouTube's live-content flag for the video.
language / languageNamestring, nullableLanguage of the returned transcript (the target when translated).
isAutoGeneratedboolean, nullableTrue when YouTube's speech recognition made the captions.
isTranslatedbooleanTrue when translation was applied; false when the selected track already matched translateTo.
availableLanguagesarrayCaption tracks YouTube returned: {language, languageName, isAutoGenerated}.
transcriptTextstring, nullableWhole transcript as one string; null when disabled or not ok.
segmentsarray, nullable[{start, duration, text}] in seconds; null when disabled or not ok.
segmentCountnumberNumber of segments; 0 when not ok.
scrapedAtstringUTC 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.

EventPriceWhat it covers
Free preview$020 lifetime attempts per account; up to 10 videos each, original-language captions only.
transcript$4.00 / 1,000 transcriptsOne delivered row with status: "ok" and isTranslated: false.
translated-transcript$10.00 / 1,000 transcriptsOne 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

Full catalogue: github.com/architek-earth.

architek.earth · code like concrete

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 free INVALID_INPUT status row. The status message now reads Done: 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, not BUDGET_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 exactly transcript $0.004 and translated-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.statusRows added.
  • 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.