The short answer
YouTube has no button to download the transcripts of a whole channel, and its official API only gives captions of videos you can edit. Give the channel's link to our YouTube Transcript Scraper: it reads the channel's list, newest first, and returns each video's transcript in the language spoken in it. A short Python script then saves one text file per video and an index in CSV. The latest 10 episodes of a podcast channel, 36.6 hours of speech and 355,094 words, took a 57-second call and cost $0.055.
Why not one video at a time
On a video's page, “Show transcript” opens the captions in a side panel, which you can select and copy. For one video it works. For a channel of 500 videos it means 500 pages. The official YouTube Data API does not help either: downloading a caption track requires permission to edit the video, and costs 200 of a project's 10,000 daily quota units (details in our guide on the YouTube transcript API).
Choose what to read on the channel
Any form of the channel's address works: @lexfridman, https://www.youtube.com/@lexfridman, its /channel/UC… link. Then choose:
| Setting | What it does |
|---|---|
channelContent | videos (the Videos tab, default), shorts, streams (past live streams) or all (every upload, newest first). A tab in the link, such as @lexfridman/shorts, does the same. |
maxVideosPerSource | How many videos to take from the channel, newest first: 10 by default, up to 10,000. |
channelSort | popular takes the most viewed videos first (Videos and Shorts tabs). |
publishedAfter | Only videos published after a date (2026-01-01) or a duration (30 days). Older ones are skipped before their transcript is downloaded, and not charged. |
languages | Empty: each video in its spoken language. Or a list in order of preference, such as ["en", "es"]. |
transcriptFormat, removeSoundTags | text for plain text only; removeSoundTags drops tags such as [Music] and [Applause], handy before an LLM. |
Several channels, playlists and single videos can go in the same run. A video that appears in two of them is transcribed and charged once.
One text file per video, in Python
import csv
import pathlib
from apify_client import ApifyClient
client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("quarra/youtube-transcript-scraper").call(run_input={
"urls": ["https://www.youtube.com/@lexfridman"],
"maxVideosPerSource": 10,
"transcriptFormat": "text",
"removeSoundTags": True,
})
folder = pathlib.Path("transcripts")
folder.mkdir(exist_ok=True)
with open(folder / "index.csv", "w", newline="", encoding="utf-8") as f:
w = csv.writer(f)
w.writerow(["videoId", "publishedAt", "title", "durationSeconds", "language", "wordCount", "status", "file"])
videos = list(client.dataset(run.default_dataset_id).iterate_items())
videos.sort(key=lambda v: v.get("position") or 0) # records arrive as transcripts finish
for v in videos:
file = ""
if v["status"] == "ok":
file = f"{v['videoId']}.txt"
(folder / file).write_text(v["transcript"], encoding="utf-8")
w.writerow([v.get("videoId"), v.get("publishedAt"), v.get("title"), v.get("durationSeconds"),
v.get("language"), v.get("wordCount"), v["status"], file])
print(run.status, "->", folder.resolve())
Tested with apify-client 3.3 on October 10, 2026: 10 transcripts, from 2 to 5 hours of speech each, in a 57-second call. Some rows of the index it wrote:
| Episode | Published | Length | Words |
|---|---|---|---|
| Psychiatry, Insane Asylums, Mental Illness, ECT, Lobotomies… | Sep 17, 2026 | 3 h 37 min | 29,466 |
| DHH: Future of Programming, AI, Agentic Engineering… | Aug 26, 2026 | 5 h 16 min | 54,046 |
| The Rise and Fall of the Roman Empire and the Byzantine Empire | Jun 30, 2026 | 3 h 52 min | 38,002 |
| Jensen Huang: NVIDIA - The $4 Trillion Company & the AI Revolution | Mar 23, 2026 | 2 h 26 min | 23,098 |
A video without a transcript still gets its row, with its status (missing or error) and no file; its record says why, and it is free. With apify-client 2.x, write run["defaultDatasetId"] instead of run.default_dataset_id.
Keep the folder up to date
Turn on onlyNewVideos and schedule the Actor on Apify, daily or hourly: each run returns only the videos that earlier runs of the same watch did not deliver, and nothing is charged twice. A run with nothing new charges only its start fee. Give the watch a watchName to keep its memory when you change its channels.
{
"urls": ["@lexfridman", "@hubermanlab"],
"maxVideosPerSource": 10,
"onlyNewVideos": true,
"watchName": "podcasts",
"transcriptFormat": "text",
"removeSoundTags": true
}
Run the script above with that input: each run writes the files of the new episodes only, and an index of that run.
Limits
- It reads captions; it does not transcribe audio. A video without captions, such as many silent Shorts and some live streams, comes back as a free
missingrecord. - A run lasts at most its timeout, one hour by default. For a channel of thousands of videos, raise the timeout or cap the run with
maxItems; a run that reaches its timeout keeps the transcripts it delivered and says so. - Free records are not unlimited: a run that keeps meeting videos without a transcript stops early and says why. Split lists that have few captions.
- The Shorts tab shows no dates, so with
publishedAfterShorts come newest first and reading stops at the first older one. - Age-restricted, members-only and paid videos need an account; we do not use one, so they come back as free error records.
What it costs
$5.00 per 1,000 transcripts on every Apify plan, plus a start fee of $0.005 per GB of memory per run ($0.005 at the default memory). A video over 10 hours counts once per 10 hours started; long podcasts under 10 hours count once. The 10 episodes above cost $0.055; a channel's latest 100 videos with transcripts, $0.505. Apify's free plan includes $5 of usage a month.
Run it on the Apify Store
Transcripts cost $5.00 per 1,000 on every Apify plan, plus $0.005 per run at the default memory; videos without captions are free. Errors and empty results are not charged, and you can set a maximum cost per run.