YouTube Transcript Scraper: Captions & Subtitles to Text avatar

YouTube Transcript Scraper: Captions & Subtitles to Text

Pricing

$4.00 / 1,000 transcript returneds

Go to Apify Store
YouTube Transcript Scraper: Captions & Subtitles to Text

YouTube Transcript Scraper: Captions & Subtitles to Text

Get the transcript of any YouTube video, Short, channel or playlist as timestamped caption lines and plain text, with title, channel, views and date. $4 per 1,000 transcripts, proxy included; videos without captions are free.

Pricing

$4.00 / 1,000 transcript returneds

Rating

0.0

(0)

Developer

Changefeeds Tools

Changefeeds Tools

Maintained by Community

Actor stats

0

Bookmarked

3

Total users

1

Monthly active users

20 days ago

Last modified

Categories

Features

Limited permissions

Share

  • What you get: the full transcript of each YouTube video, as timestamped caption lines and as one plain-text string, with title, channel, duration, views and publish date.
  • Works on: video, Shorts, channel and playlist links; manual and auto-generated captions in any language the video has. No login, no API key.
  • Price: $4 per 1,000 transcripts, proxy included. Videos with no captions are free.

Sample output

The prefilled input (two videos), run on 2026-10-23:

video_idtitlechannel_nameduration_secondslanguageis_generatedsegment_countword_count
dQw4w9WgXcQRick Astley - Never Gonna Give You Up (Official Video) (4K Remaster)Rick Astley213enfalse61366
jNQXAC9IVRwMe at the zoojawed19enfalse639

One row in full, with segments cut to three entries:

{
"type": "transcript",
"video_id": "jNQXAC9IVRw",
"url": "https://www.youtube.com/watch?v=jNQXAC9IVRw",
"title": "Me at the zoo",
"channel_name": "jawed",
"channel_id": "UC4QobU6STFB0P71PMvOGN5A",
"duration_seconds": 19,
"view_count": 445947210,
"published_at": "2005-04-23T20:31:52-07:00",
"language": "en",
"language_name": "English",
"is_generated": false,
"requested_language_matched": true,
"available_languages": [
{ "language": "en", "name": "English", "is_generated": false },
{ "language": "de", "name": "German", "is_generated": false }
],
"transcript_text": "All right, so here we are, in front of the elephants the cool thing about these guys is that they have really... really really long trunks and that's cool (baaaaaaaaaaahhh!!) and that's pretty much all there is to say",
"segments": [
{ "start": 1.2, "duration": 2.16, "text": "All right, so here we are, in front of the elephants" },
{ "start": 5.318, "duration": 2.656, "text": "the cool thing about these guys is that they have really..." },
{ "start": 7.974, "duration": 4.642, "text": "really really long trunks" }
],
"word_count": 39,
"segment_count": 6,
"thumbnail": "https://i.ytimg.com/vi/jNQXAC9IVRw/hqdefault.jpg",
"fetched_at": "2026-10-23T03:49:32.053Z"
}

How to run in 30 seconds

  1. Click Try for free. The input already holds two video links.
  2. Click Start. Our test run of that input took 16 seconds and cost $0.008 (two transcripts).
  3. Open the Output tab, or export the dataset as JSON, CSV or Excel.

For your own videos, replace the links in Videos. You can paste watch URLs, youtu.be links, Shorts, bare video ids, a channel URL (https://www.youtube.com/@name) or a playlist URL. Set Preferred language if you want a track other than English.

Pricing

  • $0.004 per transcript ($4 per 1,000), Apify residential proxy included.
  • Free: status rows for videos with no captions, or that are private, age-restricted, removed, blocked or not YouTube links.
  • The run stops cleanly at the maximum total charge you set and never fetches a video it could not bill within that limit. If a run delivers nothing, it leaves one free run_info row explaining why.

FAQ

Can I feed the transcript to ChatGPT, Claude or another LLM? Yes, that is the main use. Take transcript_text (one clean paragraph), or turn on Timestamps in plain text if you want the model to cite times.

Does it work for Shorts and live streams? Shorts, yes, when they have captions. Live streams usually get captions only after they end; until then they return no_captions.

Can I get SRT or VTT subtitle files? Not directly. The segments array has everything an SRT needs (start, duration, text) and converts in a few lines of code.

Does it download the video or audio? No. It reads only the caption text and the video's public metadata.

How fast is it? Three videos run in parallel. In our tests a transcript took 1 to 2 seconds of run time, including a 3.5-hour video with 5,700+ caption lines; ten mixed videos took about 8 seconds. Add the platform start-up: the 2-video prefill run took 16 seconds end to end, 8 of them inside the actor.

Is this allowed? It reads publicly available captions the way YouTube's own apps do, without signing in. You are responsible for how you use the transcripts (copyright and YouTube's terms apply to the content).

Limits

  • Only videos that have captions. There is no speech-to-text: a video with neither manual nor auto-generated captions gets a free no_captions row.
  • YouTube can block some requests, especially at high volume. Blocked videos come back as free blocked rows, never as empty transcripts. They are retried through two fresh residential proxy IPs first.
  • Channel and playlist URLs cover the first page YouTube lists: up to 30 of a channel's latest regular uploads (not Shorts or live streams) or a playlist's first videos. For more, list the video URLs directly.
  • Age-restricted and private videos need a signed-in account and are not supported. The actor does not machine-translate.
  • YouTube can change its caption endpoints without notice. If that happens, affected videos fail as free rows until the actor is updated.

Input reference

FieldDefaultNotes
videosrequiredWatch URLs, youtu.be links, /shorts/, /live/, /embed/ URLs or bare 11-character ids. A channel URL (/@name, /channel/UC…) adds its latest uploads; a playlist URL (/playlist?list=…) adds its first videos. Duplicates are fetched once.
languageenPreferred caption language (en, es, de, pt-BR, ja…). See "Which caption track?" below.
outputFormatbothsegments (timestamped lines), text (one string), or both.
includeTimestampsInTextfalseWrite the text as [hh:mm:ss] line rows instead of one paragraph.
includeDescriptionfalseAdd the video description to each row.
maxVideos1000Cap on videos per run (after expanding channels and playlists).
maxVideosPerChannel10For channel and playlist URLs: how many videos to take, up to 30.
proxyConfigurationApify residentialYouTube bot-checks cloud IPs, so requests go through residential proxy by default. Included in the price.

Spanish transcripts with timestamps, from a video and the first two videos of a playlist:

{
"videos": [
"https://www.youtube.com/watch?v=kJQP7kiw5Fk",
"https://www.youtube.com/playlist?list=PLZHQObOWTQDPD3MizzM2xVFitgF8hE_ab"
],
"language": "es",
"maxVideosPerChannel": 2,
"outputFormat": "text",
"includeTimestampsInText": true
}

With includeTimestampsInText: true the text looks like this:

[00:00:10] El elemento fundamental y raíz de todo el álgebra lineal es el vector.

Output reference

  • start and duration are seconds (millisecond precision). Auto-generated captions often overlap the next line; the values are YouTube's own.
  • is_generated: true means YouTube's automatic speech recognition: no punctuation or capitals, and occasional wrong words.
  • published_at is best effort; it is null on the rare video where YouTube does not report it.
  • Rows are written as each video finishes, so their order can differ from the input order. A run summary (counts per status, HTTP requests) is saved as OUTPUT in the key-value store.

Videos without a transcript get a free video row instead:

{
"type": "video",
"input": "https://www.youtube.com/watch?v=EColTNIbOko",
"video_id": "EColTNIbOko",
"url": "https://www.youtube.com/watch?v=EColTNIbOko",
"status": "no_captions",
"error": "This video has no captions (manual or auto-generated).",
"title": "Forests from Above (No Sound) — 10 Hours Screensaver of 4K UHD Drone Aerials",
"available_languages": [],
"fetched_at": "2026-09-30T04:34:22.976Z"
}
statusMeaning
no_captionsThe video has no manual or auto-generated captions (common for music-only, silent and live videos).
unavailableRemoved, never existed, members-only, region-blocked, or a premiere that has not started.
privateThe video is private.
age_restrictedYouTube requires a signed-in adult account; this actor does not sign in.
blockedYouTube refused the request (bot check or rate limit) even after fresh proxy IPs. Try again later.
invalidThe input is not a YouTube video, channel or playlist link.
errorAnything else; the error field has the details.

Which caption track?

For language: "en" the actor takes, in order: a manual English track (exact code first, so en before en-GB), then the auto-generated English track. If the video has no English track at all, it returns the first manual track in another language (or the first track) and sets

requested_language_matched: false
, so you can filter those rows out. Every row lists all tracks in available_languages.

Troubleshooting: youtube-transcript-api RequestBlocked / IpBlocked

If you run the open-source youtube-transcript-api on a cloud server and it works on your laptop but throws RequestBlocked or IpBlocked in production, the cause is YouTube's IP-based bot detection, not your code.

Why it happens

YouTube treats requests from AWS, GCP, Azure, Railway, Heroku and other datacenter ranges far more aggressively than residential ISP ranges. The library's own README says: "YouTube has started blocking most IPs that are known to belong to cloud providers (like AWS, Google Cloud Platform, Azure, etc.), which means you will most likely run into RequestBlocked or IpBlocked exceptions when deploying your code to any cloud solutions."

Free fixes

  • Add retries and backoff, and cache results so you never re-fetch a transcript you already have.
  • Route through rotating residential proxies. The library ships proxy support via youtube_transcript_api.proxies:
from youtube_transcript_api import YouTubeTranscriptApi
from youtube_transcript_api.proxies import GenericProxyConfig
ytt_api = YouTubeTranscriptApi(
proxy_config=GenericProxyConfig(
http_url="http://user:pass@your-proxy.org:port",
https_url="https://user:pass@your-proxy.org:port",
)
)
  • Space out requests to one video or channel, and re-test whenever YouTube adjusts its detection.

What this actor does about it

It uses residential proxy by default, rotates IPs on any block and retries through fresh proxies. Blocked, private and caption-less videos are not charged.