YouTube Transcript Scraper - Subtitles & Captions, $0.001/Video
Pricing
from $0.97 / 1,000 transcripts
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
Maintained by Apify PartnerActor stats
0
Bookmarked
63
Total users
38
Monthly active users
14 hours ago
Last modified
Categories
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=…oryoutu.be/…links, one per entry - Output: one JSON row per video:
transcriptlines withtext,startTime(like"0:03") anddur(seconds), plus theselectedcaption track - Price: $0.001 per transcript ($1 per 1,000)
- Language: optional
langcode (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" }]}
Supported YouTube link formats
https://www.youtube.com/watch?v=VIDEO_ID, includingm.youtube.comandmusic.youtube.comhttps://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
| Field | Type | Description |
|---|---|---|
startUrls | Array, required | YouTube video links, one per entry. See Supported YouTube link formats. A run without startUrls fails at start. |
lang | String | Optional 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. |
maxItems | Integer | Stops saving once this many rows are in the dataset. Leave empty to process every URL. Free-plan runs are capped at 10 rows. |
customMapFunction | String | Optional JavaScript function that reshapes each row. See Custom map function. |
outputConnector | String | Optional MCP connector to deliver the results to. See Send results to Notion, Todoist and more. |
deliverTarget | String | Where the connector writes: a page or database URL or ID (Notion example: https://www.notion.so/My-Posts-24f8a1b2c3d44e5f8a9b0c1d2e3f4a5b). Some services need none. |
deliverMode | String | perPost writes one record per item, summary one roll-up per run. Defaults to perPost. |
deliverMaxItems | Integer | Optional 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.
| Field | Description |
|---|---|
inputSource | The 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. |
id | The 11-character YouTube video ID. |
transcript | Lines 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"). |
selected | The caption track that was returned: title, written in the requested language (e.g. "English (auto-generated)", "Español"), and languageCode. |
availableLanguages | A 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
langwhen you need one language across a batch, then drop rows whoseselected.languageCodediffers. - Test a new batch with a small
maxItemsbefore scaling up. - Keep the full output until your schema is settled; reshape with
customMapFunctionlater.
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 item | Price | Description |
|---|---|---|
| Video Transcript | $0.001 | Charged per successful transcript extraction (transcript-query) |
| Dataset Item | $0.00 | Charged 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
startUrlsentry with a valid transcript costs $0.001 for thetranscript-querycharge. - 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-itemevent 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-itemcharge 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,startTimeanddur, 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 aspt-BR;selectedshows which track came back. - Bulk runs: pass hundreds of links in one run;
maxItemscaps 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:
customMapFunctionrenames 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.
inputSourceis 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.
- Authorize a connector (Notion, Todoist, or any service whose MCP server offers non-destructive write tools) once in Console → Settings → API & Integrations → MCP connectors.
- Pick it in the Send results to Notion, Todoist, … field.
- 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:
perPostwrites one record per video;summarywrites a single roll-up for the run. - Max records to deliver (optional): a cap on how many records
perPostdelivers.
- Destination target: the page or database URL or ID (Notion example:
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
startTimeto 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
langto 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
startTimeanddurto 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
startTimefor retrieval-augmented generation (RAG) with timestamp citations. - Set
langfor language-specific datasets, and drop rows whoseselected.languageCodefell back to another language. - Use
customMapFunctionto 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
langand checkselected.languageCodeto 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 code | Cause | Behavior |
|---|---|---|
| C001 | The transcript request to the backend failed (network or unexpected error). | The video is skipped; no row is pushed for it. |
| C002 | The backend returned an empty or unexpected response. | The video is skipped; no row is pushed for it. |
| C003 | The 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
startUrlsmust be ayoutube.com/watch?v=…oryoutu.be/…link. - The link's host must be YouTube; other domains are rejected.
startUrlsmust not be empty.- The videos must have captions; check on YouTube first.
Related YouTube Actors
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 API | YouTube Playlist Scraper | YouTube Channel Scraper |
|---|---|---|
| Videos, channels, playlists, search and trending metadata from one actor | Every video in a playlist, with position and metadata, ready for this scraper's startUrls | Every 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.