YouTube Transcript Scraper - Subtitles & Captions, $0.001/Video avatar

YouTube Transcript Scraper - Subtitles & Captions, $0.001/Video

Pricing

from $0.97 / 1,000 transcripts

Go to Apify Store
YouTube Transcript Scraper - Subtitles & Captions, $0.001/Video

YouTube Transcript Scraper - Subtitles & Captions, $0.001/Video

Extract YouTube transcripts, subtitles, and captions (uploaded or auto-generated) as JSON with per-line timestamps from youtube.com/watch and youtu.be links. $0.001 per transcript. Pick a caption language, run in bulk, reshape output. No API key or login. For AI, RAG, SEO, and research.

Pricing

from $0.97 / 1,000 transcripts

Rating

5.0

(1)

Developer

API Dojo

API Dojo

Maintained by Apify Partner

Actor stats

0

Bookmarked

63

Total users

38

Monthly active users

14 hours ago

Last modified

Share

YouTube Transcript Scraper & API – Transcripts, Subtitles and Captions with Timestamps

YouTube Transcript Scraper returns the captions of public YouTube videos as structured JSON: every line's text with its start time and duration, plus the caption track that was returned. Give it youtube.com/watch or youtu.be links and get results through the Apify Console or API, with no YouTube API key or login, at $0.001 per transcript ($1 per 1,000). It reads the caption tracks YouTube already has, uploaded or auto-generated; it doesn't transcribe audio, so videos with captions turned off return no transcript.

At a glance

  • Input: youtube.com/watch?v=… or youtu.be/… links, one per entry
  • Output: one JSON row per video: transcript lines with text, startTime (like "0:03") and dur (seconds), plus the selected caption track
  • Price: $0.001 per transcript ($1 per 1,000)
  • Language: optional lang code (en, es, de, tr, pt-BR, …); auto-generated captions count
  • Free Apify plan: 5 runs a month, up to 10 transcripts per run
  • Not supported: Shorts, live, embed, playlist and channel links; videos without captions; speech-to-text

Quick start

Open the Actor in Apify Console and click Start; the input is prefilled. This input fetches one English transcript:

{
"startUrls": ["https://www.youtube.com/watch?v=_AbFXuGDRTs"],
"lang": "en"
}

To call it from code, send the same input to the Apify API and get the rows back in one request:

curl -X POST "https://api.apify.com/v2/acts/apidojo~youtube-transcript-scraper/run-sync-get-dataset-items" \
-H "Authorization: Bearer $APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"startUrls":["https://www.youtube.com/watch?v=_AbFXuGDRTs"],"lang":"en"}'

Real output for that video, trimmed to the first 2 of its 551 lines:

{
"inputSource": "https://www.youtube.com/watch?v=_AbFXuGDRTs",
"type": "transcript",
"id": "_AbFXuGDRTs",
"transcript": [
{ "text": "Hidden in this mountain is a $1 billion", "dur": "5.679", "startTime": "0:00" },
{ "text": "nuclear bunker. We are 2,000 ft", "dur": "4.8", "startTime": "0:03" }
],
"selected": { "title": "English (auto-generated)", "languageCode": "en" },
"availableLanguages": [{ "title": "Vietnamese", "languageCode": "vi" }]
}
  • https://www.youtube.com/watch?v=VIDEO_ID, including m.youtube.com and music.youtube.com
  • https://youtu.be/VIDEO_ID

Links can be plain strings or objects like { "url": "…" }. Each link is processed separately, so a duplicate link returns a duplicate row.

Not supported: Shorts (/shorts/…), live (/live/…), embed (/embed/…), playlist and channel links. Links from other sites are rejected.

Starting from a playlist or a channel? Get its video links first with YouTube Playlist Scraper or YouTube Channel Scraper, then pass them to startUrls.

Only videos with captions return a transcript. Videos with captions turned off are skipped (error code C003) and the rest of the batch continues.

Input parameters

FieldTypeDescription
startUrlsArray, requiredYouTube video links, one per entry. See Supported YouTube link formats. A run without startUrls fails at start.
langStringOptional caption language code, such as en, es, de, tr or pt-BR. Leave empty for the video's default caption track. If the video has no track in that language, a different track is returned, so check selected.languageCode.
maxItemsIntegerStops saving once this many rows are in the dataset. Leave empty to process every URL. Free-plan runs are capped at 10 rows.
customMapFunctionStringOptional JavaScript function that reshapes each row. See Custom map function.
outputConnectorStringOptional MCP connector to deliver the results to. See Send results to Notion, Todoist and more.
deliverTargetStringWhere the connector writes: a page or database URL or ID (Notion example: https://www.notion.so/My-Posts-24f8a1b2c3d44e5f8a9b0c1d2e3f4a5b). Some services need none.
deliverModeStringperPost writes one record per item, summary one roll-up per run. Defaults to perPost.
deliverMaxItemsIntegerOptional cap on how many records perPost delivers. Ignored in summary.

Output

Each video becomes one JSON row in the dataset. Export it as JSON, CSV, Excel or XML from the Console, or read it through the API.

FieldDescription
inputSourceThe link exactly as you submitted it. Always present, even with a custom map function.
type"transcript" on transcript rows. Notice rows don't have it.
idThe 11-character YouTube video ID.
transcriptLines in playback order. Each has text, startTime (clock-style string such as "0:03" or "17:06", not seconds) and dur (seconds as a decimal string, such as "5.679").
selectedThe caption track that was returned: title, written in the requested language (e.g. "English (auto-generated)", "Español"), and languageCode.
availableLanguagesA single { title, languageCode } entry from the transcript backend. It doesn't list the video's caption tracks, so don't use it to find languages; request one with lang and check selected.languageCode.

Line text can include line breaks, zero-width spaces, speaker-change markers (>>) and tags such as [Music]. Auto-generated captions may show [ __ ] where YouTube hides a word. Strip these if you need clean prose.

To keep only transcripts, filter on type === "transcript".

More input examples

Request French captions for a batch:

{
"startUrls": [
"https://www.youtube.com/watch?v=_AbFXuGDRTs",
"https://youtu.be/iG9CE55wbtY"
],
"lang": "fr",
"maxItems": 50
}

Get one plain-text string per video instead of timed lines:

{
"startUrls": ["https://www.youtube.com/watch?v=_AbFXuGDRTs"],
"customMapFunction": "(object) => ({ videoId: object.id, language: object.selected.languageCode, fullText: object.transcript.map((line) => line.text).join(' ') })"
}

Tips:

  • Set lang when you need one language across a batch, then drop rows whose selected.languageCode differs.
  • Test a new batch with a small maxItems before scaling up.
  • Keep the full output until your schema is settled; reshape with customMapFunction later.

Pricing

Pricing follows a transparent, pay-per-event model. You're only charged for transcripts that are actually extracted. That's $1 per 1,000 transcripts.

Pricing itemPriceDescription
Video Transcript$0.001Charged per successful transcript extraction (transcript-query)
Dataset Item$0.00Charged for each item pushed to the dataset, but this actor doesn't charge this event, so it costs nothing regardless of plan

Understanding your costs

  • Single video: One startUrls entry with a valid transcript costs $0.001 for the transcript-query charge.
  • Batch run (10 videos): If all 10 videos return a transcript, you're charged 10 × $0.001 = $0.01. Videos that are skipped (no transcript, invalid URL, or a failed request) are not charged.
  • No extra storage fee: This actor doesn't charge the dataset-item event at all, so there's no additional cost on top of the $0.001 transcript price.

Why it works so well

  • Pay per event: only successfully extracted transcripts are charged.
  • Scale from a single video lookup to large batch or bulk extraction jobs without unpredictable costs.
  • No dataset-item charge on top of the transcript price, since this actor doesn't bill for that event.

Free Apify plan

If you run on Apify's Free plan, you can still use this YouTube Transcript Scraper in Demo Mode. Free users can run the actor up to 5 times per month, with each run capped at a maximum of 10 items — just enough to test it out. To use this actor without limitations, subscribe to a paid plan on Apify. Subscribe here.

Features

  • Timed transcript lines: every line has text, startTime and dur, so you can rebuild exact timing, chapter markers or subtitle files.
  • Uploaded and auto-generated captions: reads whichever caption tracks the video already has.
  • Language selection: request a language with lang, including regional codes such as pt-BR; selected shows which track came back.
  • Bulk runs: pass hundreds of links in one run; maxItems caps how many rows are saved.
  • Fault-tolerant: each video is processed on its own. Videos without captions and failed requests are skipped while the rest of the batch continues, and rate limits are retried automatically. 4,015 of 4,018 runs succeeded in the last 30 days (Apify public run stats, September 2026).
  • Custom output shape: customMapFunction renames or reshapes the fields of every row.
  • No API key or login: works from the video link alone. Run it from the Console, the Apify API, integrations or on a schedule.

Custom map function

Use this function to reshape every row. It receives one transcript object and returns the object to save, so you can rename fields, change formatting or keep only the attributes you need.

  • The return value has to be an object.
  • It reshapes rows; it can't drop them.
  • inputSource is always added back to the result.
  • If the function throws, the unmodified row is saved.

Example:

(object) => {
return {
videoId: object.id,
language: object.selected.languageCode,
fullText: object.transcript.map((line) => line.text).join(' '),
};
}

Result:

{
"inputSource": "https://www.youtube.com/watch?v=_AbFXuGDRTs",
"videoId": "_AbFXuGDRTs",
"language": "en",
"fullText": "Hidden in this mountain is a $1 billion nuclear bunker. We are 2,000 ft …"
}

Send results to Notion, Todoist and more (MCP connectors)

The YouTube Transcript Scraper can deliver its results straight into the tools you already use, through Apify MCP Connectors.

  1. Authorize a connector (Notion, Todoist, or any service whose MCP server offers non-destructive write tools) once in Console → Settings → API & Integrations → MCP connectors.
  2. Pick it in the Send results to Notion, Todoist, … field.
  3. Fill in where and how to write:
    • Destination target: the page or database URL or ID (Notion example: https://www.notion.so/My-Posts-24f8a1b2c3d44e5f8a9b0c1d2e3f4a5b). Share it with your connector first through Notion's ••• → Connections menu. Some services need no target.
    • Write mode: perPost writes one record per video; summary writes a single roll-up for the run.
    • Max records to deliver (optional): a cap on how many records perPost delivers.

Notion: point the target at a Notion page and the Actor creates a database with one typed column per field (URL, number, date, text). An existing database gets its matching columns filled by name. The connector can't add columns to it, so an empty database receives only titles and the rest of each record goes to the row's page body. The selected object becomes selected.title and selected.languageCode columns, and the transcript lines become one JSON text column. Notion caps a text cell at 2,000 characters, so a long transcript is cut off in Notion; the dataset always has the full transcript. Fields without a column go to the row's page body, so nothing is lost.

Your credentials stay with you: the Actor writes only through your own connector and only uses non-destructive write tools, so it can't delete or overwrite anything. Delivery runs after the scrape and is best-effort. If it fails, the run still finishes normally with all results in the dataset, and the run log says what went wrong. If the run is close to its timeout, delivery stops early so the run still succeeds, and the log says how many records were delivered.

Use cases

Content repurposing and marketing

Turn video content into blog posts, show notes and social copy without re-watching or transcribing by hand.

  • Draft blog posts or newsletters from the full transcript of an existing video.
  • Use startTime to create accurate chapter markers or show notes.
  • Batch-process a channel's back catalog into a searchable content library.

Example: turn a podcast's YouTube upload into a written recap and pull quotes for social media. If the recap also needs view counts or channel details, YouTube Scraper API can add that metadata for the same video.

SEO and content marketing

Spoken content becomes text you can analyze and reuse for search.

  • Pull keyword-rich transcript text to inform on-page copy.
  • Find the subtopics that come up most across the top videos in a niche.
  • Build FAQ or article content from spoken explanations in tutorial videos.
  • Request the same video in several languages with lang to plan multilingual content.

Example: scrape transcripts from the top-ranking videos on a topic to find subtopics and phrasing worth covering in a companion article.

Research and academic analysis

Structured, timed transcript text supports discourse and content analysis at scale.

  • Collect transcripts across many videos for text-based discourse analysis.
  • Use startTime and dur to tie quotes to exact moments in the source video.
  • Build longitudinal datasets that track how a topic is discussed over time.

Example: scrape 100 educational videos on the same topic to compare how creators explain one concept. For a course-length playlist, YouTube Playlist Scraper can list every video URL in order, ready for startUrls.

AI, RAG and NLP pipelines

Clean, timed transcript text is ready-made input for summarization, retrieval, classification or fine-tuning.

  • Collect transcripts at scale to build training or evaluation corpora.
  • Chunk lines by startTime for retrieval-augmented generation (RAG) with timestamp citations.
  • Set lang for language-specific datasets, and drop rows whose selected.languageCode fell back to another language.
  • Use customMapFunction to shape rows into the schema your pipeline expects.

Example: scrape transcripts from a curated list of domain videos to build a fine-tuning set for a domain-specific summarization model.

Accessibility and localization

Existing captions are a starting point for accessibility review and translation work.

  • Pull the existing transcript as the base for an accessibility audit.
  • Request a language with lang and check selected.languageCode to see whether the video already has captions in it.
  • Use the timed lines to resync or reuse captions on other platforms.

Example: check a set of training videos for existing captions in each target language to plan which still need translation. If the videos span a whole channel, YouTube Channel Scraper can list every video so nothing gets missed.

Frequently asked questions

Can I scrape YouTube transcripts without an API key?

Yes. The scraper works from a video's link and needs no YouTube Data API key or login.

Can I use this as a YouTube Transcript API?

Yes. Every run can be started through the Apify API, including the run-sync-get-dataset-items endpoint shown in Quick start, so you can call it from your own app, script or automation.

How do I download YouTube subtitles or captions as text?

Pass the video links in startUrls and run the Actor. Each video's captions come back as timed lines you can export as JSON, CSV or Excel, or join into one string with customMapFunction.

Does it work with auto-generated captions?

Yes. Auto-generated tracks are returned like uploaded ones; selected.title shows which kind you got, for example "English (auto-generated)".

Can it transcribe videos that have no captions?

No. It reads caption tracks that YouTube already has and doesn't run speech-to-text. Videos with captions turned off are skipped (C003).

Does it support YouTube Shorts?

No. Only youtube.com/watch?v=… and youtu.be/… links are supported.

Can I get SRT files?

Not directly. The output is JSON with text, startTime and dur per line, which you can export as JSON, CSV or Excel and convert to SRT in your own code.

Does this scraper support bulk or batch transcript extraction?

Yes. Pass multiple links in startUrls to run it as a bulk YouTube transcript scraper, and use maxItems to cap how many rows are saved.

Is this different from open-source "youtube-transcript" libraries?

It covers similar ground to open-source youtube-transcript packages, but runs as a managed Apify actor with built-in retries, rate-limit handling, batch support, and a hosted API, so there's no need to install or maintain a library yourself.

How much does it cost to scrape a YouTube transcript?

You pay $0.001 per successfully extracted transcript (transcript-query). There's no separate dataset-item charge, since this actor doesn't bill for that event, on any plan.

What happens if I submit a non-YouTube URL?

It's rejected. Only hostnames containing youtube or youtu are accepted.

Can I request a transcript in a specific language?

Yes. Set lang to a language code such as en, tr or pt-BR. If the video has no track in that language, a different track is returned, so check selected.languageCode.

Can I see which caption languages a video has?

Not reliably. The availableLanguages field holds a single entry and doesn't list the video's tracks. Request the language you need with lang and check selected.languageCode.

What does the transcript timing data look like?

Each line has startTime, a clock-style string such as "0:03" or "17:06", and dur, the line's duration in seconds as a string such as "5.679".

How many videos can I process per run?

As many as you pass in startUrls. Set maxItems to cap the rows saved. Free-plan runs are capped at 10 rows.

What happens if the backend rate limit is hit?

A C019 notice row is pushed to the dataset and the request is retried automatically after a short delay.

Why did my run fail immediately with no videos processed?

Usually startUrls is missing, or the run failed to initialize with the subscription and policy service (C100). Check the Log tab for the code.

Can I automate transcript scraping on a schedule?

Yes. Use Apify's built-in scheduler, for example to pull transcripts as new videos appear on a channel you monitor.

Troubleshooting

A video is missing from the results

The actor logs the reason and continues with the rest of the batch.

Error codeCauseBehavior
C001The transcript request to the backend failed (network or unexpected error).The video is skipped; no row is pushed for it.
C002The backend returned an empty or unexpected response.The video is skipped; no row is pushed for it.
C003The video has no transcript (captions turned off or unavailable).The video is skipped; no row is pushed for it.

The dataset contains C019 rows

The backend rate limit was hit. The request is retried automatically after a short delay, so no action is needed; filter the notice rows out with type === "transcript".

The run stopped or failed

C098 (unhandled promise rejection) and C099 (uncaught exception) stop the run; C100 means it couldn't initialize with the subscription and policy service. Retry the run, and if it happens again, contact support with the run's Log tab output.

The run returned no results

  • Each link in startUrls must be a youtube.com/watch?v=… or youtu.be/… link.
  • The link's host must be YouTube; other domains are rejected.
  • startUrls must not be empty.
  • The videos must have captions; check on YouTube first.

A transcript captures what's said in a video. For view counts, channel details or the rest of a playlist, pair this actor with these tools from the same developer:

YouTube Scraper APIYouTube Playlist ScraperYouTube Channel Scraper
Videos, channels, playlists, search and trending metadata from one actorEvery video in a playlist, with position and metadata, ready for this scraper's startUrlsEvery video published by a given channel

Contact

If you need support, check the run's Log tab in Apify Console for the error code first, then reach out through the actor's Issues tab on Apify with the run link attached.