YouTube Transcript, Subtitles & Comments Scraper
Pricing
from $1.60 / 1,000 video transcript delivereds
YouTube Transcript, Subtitles & Comments Scraper
YouTube transcript scraper & API: get subtitles & captions as plain text, SRT, VTT, timestamped JSON or LLM-ready chunks from videos, Shorts, channels, playlists and search. Any language or translated, plus chapters, metadata and comments. $2 per 1,000 transcripts; videos without captions are free.
Pricing
from $1.60 / 1,000 video transcript delivereds
Rating
0.0
(0)
Developer
Aaron Tuttle
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
11 days ago
Last modified
Categories
Share
YouTube Transcript Scraper & API
Get YouTube transcripts (subtitles / closed captions) as plain text, clean paragraphs, SRT, WebVTT, timestamped JSON or LLM-ready chunks, from videos, Shorts, channels, playlists and YouTube search. Any caption language, an ordered list of preferred languages, or YouTube's own translation. Chapters, video metadata and optional comments with replies are included.
$2 per 1,000 transcripts, from $1.60 on higher plans. Videos without captions are free, searching and listing channels is free, and there's no extra platform fee: compute and proxies are included.
YouTube blocks anonymous transcript requests aggressively, which is why transcript tools often hang, return "Sign in to confirm you're not a bot" or come back empty. This scraper retries blocked requests on fresh sessions and IPs, has several independent ways to download a transcript, and only charges for transcripts it actually delivers.
Quick start
- Paste one or more YouTube URLs (or type search terms under Search YouTube).
- Optionally pick transcript languages, a chunk size for AI pipelines, or turn on comments.
- Click Start and download the results as JSON, CSV or Excel, or read them through the API.
The smallest input that works:
{ "startUrls": ["https://www.youtube.com/watch?v=dQw4w9WgXcQ"] }
What you can paste
| Input | Examples | What happens |
|---|---|---|
| Video | https://www.youtube.com/watch?v=…, https://youtu.be/…, /shorts/…, /live/…, /embed/…, or an 11-character ID | One item per video |
| Channel | @handle, https://www.youtube.com/@handle, /channel/UC…, /c/…, /user/… | Newest videos first, up to Max videos per channel/playlist; optionally Shorts and finished live streams too |
| Playlist | https://www.youtube.com/playlist?list=PL… | Videos in playlist order |
| Search | Terms under Search YouTube (searchQueries), or a youtube.com/results?search_query=… URL | Video results, up to Max videos per search, with YouTube's sort and upload-date filters |
Expanding channels, playlists and searches is free; you pay per transcript delivered. Duplicates across sources are processed once.
Search options: searchSort (relevance or popularity), searchUploadDate (any, today, week, month, year) and searchSubtitlesOnly (YouTube's "Subtitles/CC" filter). YouTube no longer offers sorting search results by upload date, so use the upload-date filter or Published after for recent videos. Without Include Shorts, searches ask YouTube for regular videos only. Videos that are live right now or scheduled have no transcript yet and are left out.
Output formats
Items (default): one JSON record per video.
| Field | What it is |
|---|---|
transcript.text | The whole transcript in one string |
transcript.paragraphText | The same text split into readable paragraphs (at chapters, pauses and sentence ends) |
transcript.chunks | With Chunk length (chunkSeconds) or Chunk size (chunkWords): [{ index, startSec, endSec, text, wordCount, url, chapter }], where url opens the video at that moment (&t=…s). Chunks never cross a chapter |
transcript.segments | Timed captions: [{ start, duration, text }] |
transcript.srt, transcript.vtt | Ready-to-save subtitle files |
transcript.language, isAutoGenerated, isTranslated, sourceLanguage, availableLanguages | Which captions you got and which exist |
chapters | Title, start and end of each chapter (YouTube's markers, else timestamps in the description) |
| Metadata | title, channel, channelId, publishedAt, durationSec, viewCount, likeCount, description, tags, category, thumbnails, live / unlisted flags |
comments | With Comments on (see below) |
hasTranscript, reason, warnings | Why a video has no transcript, and notes such as a language fallback |
Turn on Lean items (leanOutput) to leave out srt, vtt and segments so records stay small for LLM context windows; text, paragraphs, chunks, chapters and metadata stay.
Example (shortened):
{"type": "video","videoId": "dQw4w9WgXcQ","url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ","title": "Rick Astley - Never Gonna Give You Up (Official Video)","channel": "Rick Astley","publishedAt": "2009-10-25T06:57:33.000Z","durationSec": 213,"hasTranscript": true,"transcript": {"language": "en","isAutoGenerated": false,"isTranslated": false,"segmentCount": 61,"text": "We're no strangers to love You know the rules and so do I ...","paragraphText": "We're no strangers to love You know the rules and so do I ...\n\n...","chunks": [{ "index": 0, "startSec": 18.64, "endSec": 43.2, "text": "We're no strangers to love ...", "wordCount": 52, "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ&t=18s", "chapter": null }],"srt": "1\n00:00:18,640 --> 00:00:21,480\nWe're no strangers to love\n\n...","segments": [{ "start": 18.64, "duration": 2.84, "text": "We're no strangers to love" }]},"chapters": [],"comments": [],"warnings": []}
A video without a transcript is delivered like this and not charged:
{ "type": "video", "videoId": "…", "hasTranscript": false, "reason": "no-captions", "error": "This video has no captions (neither uploaded nor auto-generated).", "transcript": null }
reason is one of no-captions, live-no-captions, language-unavailable, private, unavailable, age-restricted, members-only, geo-blocked, premium-only, live-offline. A video that YouTube kept blocking after all retries is an item with type: "error" (also not charged).
Rows (outputFormat: "rows"): flat rows for CSV, Excel and Google Sheets, with the same columns on every row. recordType is video, transcript_segment (or transcript_chunk when chunking is on), comment or error; transcript rows carry startSec, endSec, text, chapter and a timestampUrl link to the moment.
The dataset has ready-made views: Overview, Transcript segments, AI chunks, Clean text (paragraphs), Comments and Flat rows.
How much does it cost to scrape YouTube transcripts?
You pay per result; compute and proxy costs are included. Your Apify plan decides your discount tier, and higher tiers get lower prices automatically:
| Event | Free / Bronze | Silver | Gold and above |
|---|---|---|---|
| Transcript delivered (per 1,000) | $2.00 | $1.80 | $1.60 |
| Comment delivered (per 1,000) | $0.50 | $0.45 | $0.40 |
| Run start (once per run at the default 256 MB memory) | $0.00005 | $0.00005 | $0.00005 |
| Example (Free / Bronze prices) | Approx. cost |
|---|---|
| 1 video transcript | $0.002 |
| 100 transcripts (e.g. a channel's latest 100 videos) | $0.20 |
| 1,000 transcripts | $2.00 ($1.60 on Gold and above) |
| 1 transcript + 100 comments | $0.052 |
| 50 transcripts + 100 comments each | $2.60 |
Not charged: videos without captions, private, removed, age-restricted, members-only or still-live videos, transcripts skipped by your language setting, videos left out by Published after or Skip already scraped, videos YouTube kept blocking, and the searching and listing itself. A charge is one delivered transcript, whatever formats, chunks or translation you asked for. To cap a run, set Apify's Maximum cost per run; the scraper stops cleanly at the limit.
Free Apify plan: a free $5 credit ≈ 2,500 transcripts at $2 per 1,000. Free-plan runs process up to 100 videos and 50 comments per video; the run finishes normally and the log says what was skipped. Paid plans have no per-run cap.
YouTube comments
Turn on Comments (includeComments) to get top or newest comments (commentSort), optionally with replies (includeReplies), up to Max comments per video (replies count toward it). Each comment has commentId, author, authorChannelId, text, likes, replyCount, publishedTime (YouTube's text, e.g. "2 weeks ago"), an approximate publishedAt, isPinned, isHearted, isCreator and, for replies, parentCommentId. You can collect comments without transcripts by turning Transcript off. Comments are charged per comment delivered; if comments are turned off for a video, commentsDisabled is true and nothing is charged for them.
Languages & translation
- Transcript languages (
languages): an ordered list such as["en", "es"]. The first language the video has is used, uploaded captions before auto-generated ones, exact code (pt-BR) before the base language (pt). The older singlelanguagefield still works. - If none of these languages exists (
whenLanguageMissing):original(default): the video's own captions, with a note inwarnings;translate: YouTube's translation of the original captions into your first language;skip: no transcript (reason: "language-unavailable"), not charged.
- Always translate to (
translateTo): translate every transcript into one language with YouTube's translation (the same as the website's "auto-translate").transcript.sourceLanguagetells you what it was translated from. - Leave the languages empty to get each video's original language.
transcript.availableLanguageslists every caption track a video has.
Use with AI agents (MCP, n8n, Make, Zapier)
MCP (Claude, Cursor, VS Code and other MCP clients): add Apify's MCP server with just this Actor as a tool. Hosted server (OAuth or an Authorization: Bearer <APIFY_TOKEN> header):
{"mcpServers": {"youtube-transcripts": {"url": "https://mcp.apify.com?tools=aaronoptimus/youtube-transcripts-scraper"}}}
Local server (stdio):
{"mcpServers": {"youtube-transcripts": {"command": "npx","args": ["-y", "@apify/actors-mcp-server", "--tools", "aaronoptimus/youtube-transcripts-scraper"],"env": { "APIFY_TOKEN": "YOUR_APIFY_TOKEN" }}}}
Tip for agents: ask for "leanOutput": true and "chunkWords": 300 to get compact, citeable chunks instead of full subtitle files.
n8n, Make, Zapier: use Apify's integration for your tool, choose this Actor, pass the same JSON input, then read the run's dataset items. You can also schedule runs and send results to Google Sheets, Slack or webhooks with Apify integrations.
API / code (JavaScript):
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: 'MY_APIFY_TOKEN' });const run = await client.actor('AaronOptimus/youtube-transcripts-scraper').call({ searchQueries: ['how to start a podcast'], maxVideosPerSearch: 10, chunkWords: 300, leanOutput: true });const { items } = await client.dataset(run.defaultDatasetId).listItems();for (const v of items) console.log(v.title, v.transcript?.chunks?.length);
Python:
from apify_client import ApifyClientclient = ApifyClient("MY_APIFY_TOKEN")run = client.actor("AaronOptimus/youtube-transcripts-scraper").call(run_input={"startUrls": ["https://www.youtube.com/watch?v=dQw4w9WgXcQ"], "languages": ["en", "es"]})items = client.dataset(run["defaultDatasetId"]).list_items().itemsprint(items[0]["transcript"]["text"])
Scheduled channel monitoring
Schedule a run (Apify → Schedules) with an input like this to get only new uploads and pay only for them:
{"startUrls": ["https://www.youtube.com/@TED"],"publishedAfter": "7 days","includeShorts": false,"includeLiveStreams": true,"skipAlreadyScraped": true,"skipAlreadyScrapedStore": "ted-monitor"}
- Published after (
publishedAfter): a date (2026-09-01) or an age ("7 days","24 hours","2 weeks"). Applies to channels, playlists and searches; videos you paste directly are always processed. Paging stops once a channel's videos are older, and each video's own publish date is checked before its transcript is downloaded. - Include Shorts / Include live streams: add a channel's Shorts tab and its finished live streams (Live tab). Max videos per channel/playlist applies to each tab.
- Skip videos already scraped (
skipAlreadyScraped, off by default): IDs of delivered transcripts are kept in a named key-value store in your Apify account (skipAlreadyScrapedStore, defaultyoutube-transcripts-seen) and skipped in later runs. Videos that had no transcript yet are tried again next time, since YouTube adds auto-generated captions some time after upload. Use a different store name per schedule to keep separate lists, or delete the store to start over.
Is there an official YouTube transcript API?
YouTube's official Data API v3 can list a video's caption tracks, but downloading caption text with it requires OAuth and is meant for videos you own or are allowed to edit, so it doesn't give you transcripts of arbitrary public videos. That is why transcript tools read the same captions the YouTube website and apps show. This Actor gives you that as an API: one HTTP call (or an MCP tool call) with video, channel, playlist or search input, and JSON, CSV or Excel back.
youtube-transcript-api blocked on AWS/VPS?
The popular open-source Python library youtube-transcript-api works well on a laptop, but YouTube blocks most requests from cloud servers (AWS, GCP, Azure, VPS providers), so it fails with "request blocked" / IP-blocked errors once deployed. Running it behind your own rotating proxies is the usual workaround. This Actor does that part for you: it runs on Apify Proxy with automatic session and IP rotation, switches to residential proxy for a video only when the default proxy keeps being refused, and returns a clear error item (not charged) for videos it still can't get. No proxy setup or extra proxy bill on your side.
FAQ
Why do other transcript tools hang or return nothing? YouTube serves transcripts only to sessions it trusts and answers the rest with sign-in walls, HTTP 429s or empty caption files. Tools that try once either stall or return empty results. This scraper retries on fresh sessions and IPs with short backoff, tries several transcript sources (the caption-track download, the Android app's caption tracks and, when available, the watch page's transcript panel), and gives each video a time limit so a run never hangs.
Which videos have no transcript? Videos whose creator didn't upload captions and that YouTube hasn't auto-captioned (common for music, very new uploads and some languages), ongoing live streams and premieres, and videos YouTube won't serve anonymously (private, removed, age-restricted, members-only). They're delivered with hasTranscript: false and a reason, and not charged.
Are auto-generated transcripts accurate? They're YouTube's own speech recognition, the same text as in the transcript panel. transcript.isAutoGenerated tells you which kind you got; uploaded captions are used whenever they exist in your language.
Do Shorts have transcripts? Many do (usually auto-generated). Paste Shorts URLs directly, or turn on Include Shorts for channels and searches.
How do I get the most recent videos on a topic? Search with searchUploadDate: "week" (or today / month), or combine search terms with Published after.
What does a "transcript" charge include? Everything for that video: text, paragraphs, chunks, SRT, VTT, segments, chapters and metadata, in any language or translated. Comments are charged separately, per comment.
What happens if YouTube blocks a request? The scraper retries on a fresh session and IP and, if needed, switches to residential proxy for that video. If a video still fails within its time limit, it's listed with type: "error" and not charged. If only the comments fail, you still get the transcript plus a note in warnings.
Is it legal? The scraper only reads publicly available data that any visitor can see without logging in: captions, metadata and comments. It doesn't log in, bypass age or membership restrictions, solve CAPTCHAs or download videos. Comments contain user names, which may be personal data in your jurisdiction; you're responsible for using the data in line with YouTube's terms and your local laws (GDPR, CCPA…).
Limitations
- Only captions that exist on YouTube (uploaded or auto-generated) are returned; there is no speech-to-text for videos without captions.
- Private, members-only and age-restricted videos can't be read anonymously; ongoing live streams get a transcript only after they end.
- YouTube's search returns a limited number of results per query; very broad terms may end before Max videos per search.
- Channel and search dates shown by YouTube are relative ("2 weeks ago"), so Published after uses a lenient first pass and then each video's own publish date (day precision for older videos).
publishedAtof comments is approximate (YouTube only shows relative times).- Free Apify plan: up to 100 videos per run and 50 comments per video.
Found a problem or need a feature? Open an issue on the Issues tab. See the Changelog for updates.