YouTube Transcript API: Any Video, Channel or Playlist
Pricing
from $3.00 / 1,000 transcripts
YouTube Transcript API: Any Video, Channel or Playlist
Download YouTube transcripts through an API: one video, a whole channel, a playlist or a search in one run. Clean text with timestamps and language, plus views, dates and comments. Built for AI, RAG and LLM pipelines, callable from MCP clients. No YouTube API key. Pay only for delivered transcripts.
Pricing
from $3.00 / 1,000 transcripts
Rating
0.0
(0)
Developer
Lorenzo
Maintained by CommunityActor stats
0
Bookmarked
15
Total users
3
Monthly active users
15 days ago
Last modified
Categories
Share
Download YouTube Transcripts — API for Any Video, Channel or Playlist
Get the full transcript of one YouTube video, or of every video on a channel or playlist, as clean JSON (or CSV / Excel). Timestamps, language, views and publish dates come back in the same run. No YouTube API key, no quota, nothing to install.
Use it when you need: transcripts for an AI / RAG / LLM pipeline · every video
of a channel in one run · only the new videos since last time
(publishedWithinDays) · transcripts plus comments for research.
Price: transcripts from $3 per 1,000, plus $0.50 per 1,000 videos. With
transcripts on, you pay only for transcripts actually delivered: a video with no
subtitles (NO_CAPTIONS), a blocked or rate-limited request, or a failed video
costs nothing, its video row included.
Input:
{ "inputs": ["https://www.youtube.com/@veritasium"],"includeTranscript": true, "transcriptLanguages": ["en"],"maxResultsPerSource": 50 }
Output — one row per video:
{ "video_id": "JsBZOcqZerk","title": "The Insane Real Engineering of the Nazi Enigma Machine","channel": "Veritasium", "published_at": "2026-09-21T10:49:57-07:00","views": 16814797, "duration_s": 2861, "transcript_language": "en","transcript_status": "OK","transcript": "This is the Enigma, the encryption machine used by the Nazis in World …" }
Callable from any MCP client. Claude, Cursor, VS Code and anything else that
speaks Model Context Protocol can find and run this through Apify's MCP server
at mcp.apify.com — search-actors to discover it, call-actor to run it. No
setup on your side and no separate integration.
Built for pipelines that run more than once
Most scrapers charge you the same on Monday as they did on Sunday. This one remembers what it already fetched.
Measured on 27 August 2026 — one channel, 500 videos, same input both times:
| first run | run it again | |
|---|---|---|
| rows delivered | 500 | 500 |
| videos charged | 489 | 11 |
| requests to YouTube | 525 | 30 |
| time | 174s | 46s |
Video rows already fetched inside the cache window come back from stored state and are not charged. Eleven were charged the second time because those eleven had failed the first — you are not billed for a row that came back empty. A transcript is charged when it is fetched, normally once; the one exception is under Does it re-fetch videos I already pulled?
And it is one Actor, not three. Video metadata, transcripts and comments
arrive from the same run, in the same dataset, on one bill. No joining three
outputs on video_id and no three integrations to keep alive.
What you get back
| video_type | title | views | published_at | duration_s |
|---|---|---|---|---|
video | Google Pixel 11/Pro/Fold Impressions: It Is What It Is | 3138780 | 2026-08-12T07:00:35-07:00 | 673 |
short | This ZOOM is Insane! | 3567393 | 2026-07-20T13:39:08-07:00 | 66 |
stream | 5 New Phone Updates + Giveaway Update! | 940608 | 2017-11-29T09:15:12-08:00 | 2501 |
Real rows from a real run. Exact counts, not 3.1M. Full timestamps, not
7 days ago. Both survive sorting.
How do I download the transcript of every video on a channel?
{ "inputs": ["https://www.youtube.com/@veritasium"],"includeTranscript": true, "transcriptLanguages": ["en", "es"] }
Each row gains the transcript as text, its language, whether it was auto-generated, and how many timed cues it has.
Timed cues are a separate switch: includeTranscriptSegments. Turn it on
and each row also carries transcript_segments — one entry per cue with
start_s, duration_s and text — for citing a moment in the video. It costs
nothing extra, because the cues arrive in the same request as the text. It is
off by default because every export format flattens them: a 1,000-cue video
becomes transcript_segments/0/text … /999/text, and a spreadsheet of a few
videos turns into thousands of columns. transcript_cue_count ships either way.
transcriptLanguages is a preference order and human-written subtitles beat
auto-generated ones. Subtitles off returns NO_CAPTIONS, free.
includeLikes adds a like count from one small extra request, not charged
separately.
What a row actually looks like
One video, with transcripts, timed segments and comments all switched on. Real
output from a real run — long fields trimmed with …, nothing else changed.
{"_row_type": "video","video_id": "o4SSoURPODY","video_type": "video","title": "Google Pixel 11/Pro/Fold Impressions: It Is What It Is","channel": "Marques Brownlee","channel_id": "UCBJycsmduvYEL83R_U4JriQ","views": 3148445,"likes": 84617,"published_at": "2026-08-12T07:00:35-07:00","category": "Science & Technology","duration_s": 673,"description": "Every year, a new Pixel, and new hopes and dreams... …","keywords": ["Pixel 11", "Pixel 11 Pro", "Pixel 11 Pro Fold", "…"],"thumbnail": "https://i.ytimg.com/vi_webp/o4SSoURPODY/maxresdefault.webp","subscriber_count": "21.1M subscribers","is_live": false,"is_private": false,"is_crawlable": true,"transcript": "[music] >> You know, I've been using Pixel phones for a long time. …","transcript_segments": [{ "start_s": 2.619, "duration_s": 2.741, "text": "[music]" }, "…"],"transcript_cue_count": 331,"transcript_language": "en","transcript_is_generated": true,"transcript_status": "OK","_source_url": "https://www.youtube.com/watch?v=o4SSoURPODY","_cached": false,"_error_kind": null}
A comment from the same run — its own row, not a nested field:
{"_row_type": "comment","video_id": "o4SSoURPODY","comment_id": "Ugz7VRjqhsV-60jGjwR4AaABAg","comment_text": "Adding a physical RGB LED to the back of the phone just to …","comment_author": "@Mosesplusofficial","comment_author_channel_id": "UCr3n8BfGuDIzhO8ALIxMe4w","comment_author_is_verified": false,"comment_published_text": "8 days ago","comment_like_count_text": "13K","comment_reply_count_text": "200","comment_is_reply": false}
Three things that make this different
🟢 Failures are typed, never blank
Subtitles off returns
NO_CAPTIONS. Age-restricted and members-only videos returnUNAVAILABLE: AGE_RESTRICTED/UNAVAILABLE: MEMBERS_ONLY. A transcript request that failed returnsUNAVAILABLEorTRACK_FETCH_FAILED; a video that could not be fetched returnsFETCH_FAILED. Different values, so you always know which happened.
🟢 Transcripts and comments in the same run
One actor, one pass. Transcript text, timed segments, comments and replies — alongside the metadata, not in a separate job.
🟢 With transcripts on, you pay only for delivered transcripts
A video with no subtitles, a blocked request or a failed fetch costs nothing, video row included. With transcripts off, a video row is charged when it has data. A deleted video is always free, and so is one your filter removed, or a video row served from cache.
How do I scrape all videos from a YouTube channel?
{ "inputs": ["https://www.youtube.com/@mkbhd"], "maxResultsPerSource": 500 }
@handle, a channel URL, or a /channel/UC… ID all work.
You get all three tabs. Long videos, Shorts and live streams sit in separate
tabs that do not overlap — one channel measured 30, 48 and 5. video_type says
which is which, and the tabs are interleaved so asking for 30 returns a mix.
channelTabs narrows it.
Channel rows also carry subscriber_count and channel_description, free —
same request that lists the videos.
What happens when something fails?
You get a typed reason in the row, never an empty cell.
_error_kind | means |
|---|---|
REJECTED_INPUT | not a recognised channel, playlist, video or search |
RESOLVE_FAILED | the listing could not be read |
NO_VIDEOS | it was read and contained none |
FETCH_FAILED | the video request failed |
NO_DATA | it loaded but carried no video details — private and removed videos say so in _error (PRIVATE: …, REMOVED: …), cost nothing, and are not asked again inside the cache window |
Blocked requests retry automatically on a fresh IP. In a 36-video run that turned 3 blocks into 0 failures.
How do I export a YouTube playlist to CSV?
{ "inputs": ["https://www.youtube.com/playlist?list=PLbpi6ZahtOH6Blw3RGYpWkSByi_T7Rygb"],"maxResultsPerSource": 100 }
Then hit Export — CSV, JSON, Excel, or an API endpoint. Playlists return 100 videos per request, so multiples of 100 go furthest.
How do I get YouTube comments with replies?
{ "inputs": ["https://www.youtube.com/@mkbhd"], "includeComments": true,"maxCommentsPerVideo": 100, "maxRepliesPerComment": 5 }
Comments arrive as their own rows, not extra columns — _row_type is
video or comment. Folding 100 comments into a video row would export as 600
unusable columns.
Replies are rows too (comment_is_reply: true), capped because each comment
with replies costs an extra request.
How do I search YouTube and export the results?
{ "inputs": ["drone review", "sourdough starter"] }
One quirk: a bare 11-character word is read as a video ID, because that is
exactly how long a YouTube ID is — helicopters is eleven characters. Write
search:helicopters to force a search.
Can I filter, or work in another language?
| input | effect |
|---|---|
publishedAfter / publishedBefore | YYYY-MM-DD, inclusive |
durationMinSeconds | minimum length in seconds (to exclude Shorts, use channelTabs: ["videos"] — Shorts can run up to 3 minutes) |
durationMaxSeconds | upper bound in seconds |
language | en, de, ja, es — text YouTube generates |
region | US, DE, GB — how results are ranked |
Filters run after each video is read, so they are exact — and filtered-out videos are never charged.
subscriber_count follows the language as YouTube wrote it: 21.1M subscribers,
21,1 Mio. Abonnenten, チャンネル登録者数 2110万人. Titles, descriptions and
category are unaffected.
What people use this for
📈 Track a competitor's channel every week
- Run with the competitor's channel and
maxResultsPerSource: 50,maxAgeHours: 0so view counts are fresh. - Schedule it weekly in the Apify Console.
- Join runs on
video_idand diffviewsto get per-video growth, andpublished_atto get posting cadence.
Set maxAgeHours: 0 here — the cache is a time window, and stale view counts
would flatten exactly the trend you are measuring.
🔔 Watch a channel and get only what is new
- Run with the channel and
publishedWithinDays: 7. - Schedule it weekly in the Apify Console. That is the whole setup.
publishedWithinDays is a rolling window, counted from the day each run
starts — so a saved task keeps returning what is new instead of the same seven
days forever. publishedAfter is an absolute date and is the wrong tool for a
schedule: set once, it returns the same videos every week until someone edits it.
If you set both, the explicit date wins and the run log says which applied.
This is the cheapest thing this Actor does. Video rows it has already fetched come back from stored state and are not charged, and anything outside the window is filtered and free. Measured on a 500-video channel: the first run charged 489 videos, the second charged 11.
Use publishedWithinDays a little wider than your schedule — 8 or 9 days for a
weekly run — so a late-publishing channel or a missed run does not leave a hole.
How this differs from a YouTube RSS feed. Every channel has a free RSS feed
at youtube.com/feeds/videos.xml?channel_id=…, and if all you need is a title
and a link, use it — it is free and instant. It gives you the most recent 15
videos and nothing else: no transcript, no view count, no duration, no
description, no comments, and no way to reach further back. This Actor returns
new videos with the full record, transcripts included, for any number of
channels at once.
📺 Watch several channels at once
Put every channel in inputs — one run, one schedule, one dataset:
{"inputs": ["https://www.youtube.com/@veritasium","https://www.youtube.com/@3blue1brown","https://www.youtube.com/@kurzgesagt"],"publishedWithinDays": 8,"includeTranscript": true,"transcriptLanguages": ["en"]}
Save it as a task and schedule it weekly. Each row carries _input, so you
can tell which channel it came from. Video rows already fetched come back from
stored state and are not charged again, so each run costs you mainly its new
videos.
🤖 Build an LLM corpus from a channel's transcripts
- Run with the channel,
includeTranscript: true,maxResultsPerSource: 500. - Export as JSON and use
transcriptfor plain text. AddincludeTranscriptSegments: truewhen you need timestamps to cite back to a moment in the video — it is free, but it makes the rows much wider. - Filter on
transcript_status == "OK"—NO_CAPTIONSmeans that video never had subtitles, and it cost you nothing.
transcript_is_generated tells you whether a human wrote it. Auto-generated
captions carry more errors, which matters if you are grounding an answer on
them.
💬 Find what an audience actually asks about
- Run with the channel,
includeComments: true,maxCommentsPerVideo: 100, andmaxRepliesPerComment: 5to catch the threads under the popular ones. - Filter rows to
_row_type == "comment". - Group by
video_id, sort bycomment_like_count_text, read the top of each.
Useful for support-content planning, sponsorship research, and finding the question a whole audience keeps asking that nobody has answered yet.
What fields do I get?
| field | example |
|---|---|
video_id · video_type | dQw4w9WgXcQ · video, short, stream |
title · channel · channel_id | Rick Astley - Never Gonna Give You Up · Rick Astley · UCuAXFkgsw1L7xaCfnd5JJOw |
views · likes | 1806183998 · 136841 |
published_at · duration_s | 2009-10-24T23:57:33-07:00 · 213 |
category · keywords · thumbnail · description | Music · array · highest-res URL · full text |
subscriber_count · channel_description | from channel inputs |
is_live · is_private · is_crawlable | booleans |
| every row | _row_type _input _source_url _site _cached _cached_at _error_kind _error |
| with transcripts | transcript transcript_cue_count transcript_language transcript_language_matched transcript_is_generated transcript_status |
with includeTranscriptSegments | transcript_segments — one entry per cue, start_s duration_s text |
| comment rows | comment_id comment_text comment_author comment_author_channel_id comment_author_is_verified comment_author_is_creator comment_published_text comment_like_count_text comment_reply_count_text comment_is_reply |
Three comment fields end in _text because YouTube publishes them rounded and
relative — 13K, 7 days ago — and they pass through as given. 13K is not
expanded to 13000: it could be 13,499, and inventing digits hands you a wrong
number that looks precise.
Not included: no sort order, no video or audio downloads, no total comment count per video.
What transcript_status means — and what it costs
| status | meaning | transcript charged? | video row charged? | asked again next run? |
|---|---|---|---|---|
OK | transcript delivered | yes — the only charged status | yes | no, while the cache holds it |
NO_CAPTIONS | the video has no subtitles at all | no | no | no |
EMPTY_TRACK | a subtitle track exists but is empty | no | no | no |
UNAVAILABLE: AGE_RESTRICTED · PRIVATE · MEMBERS_ONLY · REMOVED | YouTube will not serve it to anyone signed out | no | no | no — remembered, not retried |
UNAVAILABLE: … (anything else) | YouTube refused after several attempts on fresh IPs | no | no | yes |
TRACK_FETCH_FAILED: … | the track was found but could not be downloaded | no | no | yes |
NO_REQUESTED_LANGUAGE | only with strictTranscriptLanguage: none of your languages exists, so the transcript was withheld | no | no | no |
| empty | transcripts were off, or the video itself could not be fetched (see _error_kind) | no | transcripts off: yes · video failed: no | — |
If none of your transcriptLanguages exists, the video's first track is
returned as OK with transcript_language_matched: false. Turn on
strictTranscriptLanguage to withhold it instead.
Does it re-fetch videos I already pulled?
Any video fetched in the last 24 hours comes back from stored state — and the
video row is not charged. Marked _cached: true with _cached_at.
A transcript is charged when it is fetched — normally once. A cached transcript is served free. The exception: the cache keeps only the most recent ~1.5 MB of transcript text, so an older transcript may have been dropped from it. Ask for that one again and it is fetched again, and charged again.
This is a time window, not change detection. It checks how long ago a video
was read, not whether it changed — so set maxAgeHours to 0 when sorting on
views or measuring growth. The window is per actor: yesterday's run counts for
today's.
Cached rows still follow this run's settings. Turn transcripts on and a cached video with no stored transcript gets one fetched and charged; turn them off and the row carries no transcript fields at all, whatever an earlier run stored. The cache never adds a field you did not ask for, and never withholds one you did.
What does it cost?
| you get | you pay |
|---|---|
| a video row | $0.50 / 1,000 — with transcripts on, only when its transcript is delivered |
| a transcript | $3.00–$5.00 / 1,000 — $5 on the Apify Free plan, $4 Starter, $3.50 Scale, $3 Business |
| a comment or reply | $0.20 / 1,000 |
| a failed or filtered row, a cached video row, or (with transcripts on) a video whose transcript was not delivered | nothing |
| a transcript served from cache | nothing — unless it had been dropped from the cache and is fetched again |
Transcripts and comments are billed separately — each is a separate request to YouTube, not an extra field.
Send everything in one run, not one run each — it starts once and finishes
sooner. Requests run 8 at a time, one per second each; concurrency adjusts that
between 1 and 16.
Use it from code
Set APIFY_TOKEN to your token from Console → Settings → API & Integrations.
Every example asks for three transcripts from one channel and keeps the rows
where transcript_status is OK.
Python
# pip install apify-clientimport osfrom apify_client import ApifyClientclient = ApifyClient(os.environ["APIFY_TOKEN"])run = client.actor("aquixlabs/youtube-channel-scraper").call(run_input={"inputs": ["https://www.youtube.com/@veritasium"],"maxResultsPerSource": 3,"includeTranscript": True,"transcriptLanguages": ["en"],})for row in client.dataset(run["defaultDatasetId"]).iterate_items():if row.get("transcript_status") == "OK":print(row["video_id"], row["title"], len(row["transcript"]), "chars")
JavaScript / Node
// npm install apify-clientimport { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: process.env.APIFY_TOKEN });const run = await client.actor('aquixlabs/youtube-channel-scraper').call({inputs: ['https://www.youtube.com/@veritasium'],maxResultsPerSource: 3,includeTranscript: true,transcriptLanguages: ['en'],});const { items } = await client.dataset(run.defaultDatasetId).listItems();for (const row of items.filter((r) => r.transcript_status === 'OK')) {console.log(row.video_id, row.title, row.transcript.length, 'chars');}
curl (one call, rows straight back)
curl -s -X POST \"https://api.apify.com/v2/acts/aquixlabs~youtube-channel-scraper/run-sync-get-dataset-items" \-H "Authorization: Bearer $APIFY_TOKEN" \-H "Content-Type: application/json" \-d '{"inputs": ["https://www.youtube.com/watch?v=dQw4w9WgXcQ"],"includeTranscript": true, "transcriptLanguages": ["en"]}'
The synchronous endpoint waits for the run to finish, so keep it to a few videos. For a whole channel, use a client library or start a run and fetch the dataset when it finishes.
LangChain (one Document per transcript)
# pip install langchain-apify langchain-coreimport osfrom langchain_apify import ApifyWrapperfrom langchain_core.documents import Documentos.environ.setdefault("APIFY_API_TOKEN", os.environ["APIFY_TOKEN"])loader = ApifyWrapper().call_actor(actor_id="aquixlabs/youtube-channel-scraper",run_input={"inputs": ["https://www.youtube.com/@veritasium"],"maxResultsPerSource": 3, "includeTranscript": True,"transcriptLanguages": ["en"]},dataset_mapping_function=lambda row: Document(page_content=row.get("transcript") or "",metadata={"video_id": row.get("video_id"), "title": row.get("title"),"url": row.get("_source_url"), "published_at": row.get("published_at")}),)docs = [d for d in loader.load() if d.page_content]
From an AI assistant (MCP)
Add Apify's MCP server to your client with this Actor preloaded:
https://mcp.apify.com/?tools=aquixlabs/youtube-channel-scraper
The tool appears as aquixlabs--youtube-channel-scraper. Ask for "the
transcripts of the last 3 videos on @veritasium" and the assistant fills in the
input itself.
For AI agents: pay per run with x402
This Actor is eligible for Apify's agentic payments. In Apify's words: "With the x402 protocol, AI agents can run Apify Actors and pay with USDC on the Base blockchain - no Apify account needed." The agent pays once (minimum $1) for a prepaid, spend-capped Apify API token from Apify AGI, then calls this Actor with that token like any other. Unused balance is non-refundable and the token expires 14 days after purchase. Setup, wallet and current terms: Apify's x402 docs.
- MCP tool:
aquixlabs--youtube-channel-scraper(load it withhttps://mcp.apify.com/?tools=aquixlabs/youtube-channel-scraper) - One request, with the prepaid token as
$TOKEN:
curl -s -X POST \"https://api.apify.com/v2/acts/aquixlabs~youtube-channel-scraper/run-sync-get-dataset-items" \-H "Authorization: Bearer $TOKEN" \-H "Content-Type: application/json" \-d '{"inputs": ["https://www.youtube.com/watch?v=dQw4w9WgXcQ"],"includeTranscript": true, "transcriptLanguages": ["en"]}'
- Price per event: a video row $0.0005 (with transcripts on, only when its transcript is delivered) · a transcript $0.003–$0.005 (by Apify plan, see What does it cost?) · a comment or reply $0.0002. Failed and filtered rows, and videos without a delivered transcript, cost nothing. Each run draws from the token's balance until it is used up.
Will it quietly break?
Every run emits a schema fingerprint — which fields resolved, which came back empty, the row count — compared against a stored baseline. A field that starts returning empty is caught as drift, not discovered months later.
Scrapers rarely break loudly. They thin out.
Frequently asked questions
How do I get a transcript from a YouTube video?
Paste the video URL into inputs and set includeTranscript to true. You get
the full transcript as text in the transcript field, along with its language
and whether YouTube auto-generated it. No API key and no browser extension.
{ "inputs": ["https://www.youtube.com/watch?v=dQw4w9WgXcQ"],"includeTranscript": true }
How do I get the script of a YouTube video?
The same way — what YouTube calls captions or subtitles is what most people mean
by the script. transcript holds it as one block of text, ready to paste into a
document or feed to a model.
How do I download YouTube transcripts for a whole channel at once?
Give it the channel instead of the video. inputs accepts an @handle, a
channel URL or a /channel/UC… ID, and maxResultsPerSource decides how many
videos deep to go. With includeTranscript: true, every video comes back as a
row with its own transcript.
Can I get the transcript with timestamps?
Yes. Turn on includeTranscriptSegments and each row also carries
transcript_segments — one entry per cue with start_s, duration_s and
text — so you can cite the exact moment something was said. It costs nothing
extra. It is off by default because spreadsheets flatten it into thousands of
columns; transcript_cue_count ships either way.
What happens if a video has no subtitles?
transcript_status comes back as NO_CAPTIONS and you are not charged for
that video at all — neither the transcript nor the video row. It is a different value from UNAVAILABLE and
TRACK_FETCH_FAILED (the transcript request failed), so you can always tell
"this video has no subtitles" apart from "we could not reach it" — and re-run
only the second kind. A failed request is never served from the cache: the next
run asks again. Age-restricted and members-only videos come back as
UNAVAILABLE: AGE_RESTRICTED / UNAVAILABLE: MEMBERS_ONLY — final, never
charged, and not asked again inside the cache window.
Do I need the YouTube Data API or an API key?
No. There is no key to obtain, no project to create, and no daily quota to run out of. That is the main reason people use this instead of the official API.
Is there a free way to do this?
Apify's free plan includes $5 of platform usage every month, no credit card required. At the Free-plan price ($5 per 1,000 transcripts plus $0.50 per 1,000 videos) that is about 900 transcripts a month at no cost to you. On top of that, failed, filtered and cached video rows cost nothing, and so does any video whose transcript was not delivered, so you pay for the transcripts you actually receive — normally once per video inside the cache window.
Can I get transcripts in another language?
transcriptLanguages is a preference order, for example ["en", "es"].
Human-written subtitles are preferred over auto-generated ones when both exist.
transcript_language and transcript_is_generated tell you which you got.
If none of your languages exists for a video, its own first track comes back
(human-written before auto-generated) and transcript_language_matched is
false, so you can filter on it. Turn on strictTranscriptLanguage to withhold
those instead: transcript_status becomes NO_REQUESTED_LANGUAGE, there is no
text, and the transcript is not charged.
Can I use this from Claude, Cursor or another AI assistant?
Yes. It is exposed through Apify's MCP server at mcp.apify.com, so any client
that speaks Model Context Protocol can discover it with search-actors and run
it with call-actor under the name aquixlabs/youtube-channel-scraper. Nothing
to install and nothing to configure.
A useful shape for an agent: ask it for the transcript of a video or a channel, and it can call this directly rather than scraping the page itself.
Can I get the comments as well as the transcript?
Yes, in the same run. Set includeComments and each comment arrives as its own
row — not a column — tagged _row_type: comment. The dataset's Comments
view is where to read them.
Was this useful?
If it saved you time, a short review on the Store is the main way other people find it. If something broke or a field is missing, open an issue on the Issues tab instead; I read every one, and a broken run is fixed before anything new is built.
Terms
You are responsible for your use of the data you collect, including under YouTube's Terms of Service and any applicable data protection law.
中文说明
YouTube 字幕 / 转录 API。 输入单个视频、整个频道、播放列表或搜索词,一次运行即可批量获取 YouTube 视频的完整字幕文本(含时间戳和语言),以及播放量、发布日期和评论。输出 JSON、CSV 或 Excel,适用于 AI、RAG 和大语言模型(LLM)数据管道。无需 YouTube API 密钥,没有配额限制。
价格: 字幕每 1,000 条起价 $3(Apify 免费套餐为 $5),另加每 1,000 个视频 $0.50。 没有字幕的视频不收取字幕费用,失败的视频免费。
输入示例:
{ "inputs": ["https://www.youtube.com/@veritasium"],"includeTranscript": true, "transcriptLanguages": ["zh", "en"] }