개발자로서 YouTube 성적표를 받는 방법 (2026년에 적용되는 4가지 방법)

작성자

카테고리:

← 피드로
DEV Community · Bloody Valentine · 2026-09-05 개발(SW)

How to Get YouTube Transcripts as a Developer (4 Methods That Work in 2026)

YouTube transcripts unlock a lot: AI video summarizers, searchable course
databases, RAG over video libraries, dataset generation for fine-tuning,
repurposing videos into articles. But getting transcripts programmatically
is full of sharp edges: disabled captions, rate limits, datacenter IP
blocks, and YouTube’s ever-changing frontend.

This guide walks through every practical method with working code. Method 4
is the managed service I run. Skip ahead if you just want the API call.
The DIY methods below are real and will serve you well for small jobs.

What you’re actually fetching

YouTube stores captions as timed tracks in two flavors:

  • Manual captions: uploaded by creators, best accuracy
  • Auto-generated captions: YouTube’s speech recognition, most videos

Each track is text plus timing (text/start/duration), servable as SRT,
VTT, or YouTube’s timedtext XML. Everything below ultimately resolves to
that shape.

Method 1: youtube-transcript-api (Python)

The standard open-source library. Start here for scripts and prototypes.

pip install youtube-transcript-api

Enter fullscreen mode Exit fullscreen mode

from youtube_transcript_api import YouTubeTranscriptApi

video_id = "dQw4w9WgXcQ"  # the ID from the watch URL

transcript = YouTubeTranscriptApi.get_transcript(video_id)
for entry in transcript:
    print(f"[{entry['start']:.2f}s] {entry['text']}")

Enter fullscreen mode Exit fullscreen mode

It returns a list of dicts, one {'text', 'start', 'duration'} per segment.
For other languages, list what’s available first, then fetch or translate:

tl = YouTubeTranscriptApi.list_transcripts(video_id)
for t in tl:
    print(t.language_code, "generated:" , t.is_generated)

transcript = YouTubeTranscriptApi.get_transcript(video_id, languages=["id", "en"])

track = tl.find_transcript(["en"])
translated = track.translate("id").fetch()  # free, server-side

Enter fullscreen mode Exit fullscreen mode

Handle the caption-less case explicitly instead of catching bare
Exception. You’ll need these codes downstream:

from youtube_transcript_api._errors import TranscriptsDisabled, NoTranscriptFound

try:
    transcript = YouTubeTranscriptApi.get_transcript(video_id)
except TranscriptsDisabled:
    print("captions disabled: needs audio transcription (Method 3)")
except NoTranscriptFound:
    print("no track in the requested languages, try others first")

Enter fullscreen mode Exit fullscreen mode

And format for output with the built-in formatters rather than
hand-rolling SRT timestamps:

from youtube_transcript_api.formatters import TextFormatter, SRTFormatter, WebVTTFormatter

plain = TextFormatter().format_transcript(transcript)
srt = SRTFormatter().format_transcript(transcript)
vtt = WebVTTFormatter().format_transcript(transcript)

Enter fullscreen mode Exit fullscreen mode

Method 2: yt-dlp subtitles (CLI)

If you live in the terminal, yt-dlp pulls subtitle files without a line
of Python:

# auto-generated subs, no media download
yt-dlp --write-auto-sub --skip-download "https://youtube.com/watch?v=VIDEO_ID"

# manual subs as SRT
yt-dlp --write-sub --sub-format srt --skip-download "https://youtube.com/watch?v=VIDEO_ID"

# see what's on offer first
yt-dlp --list-subs "https://youtube.com/watch?v=VIDEO_ID"

Enter fullscreen mode Exit fullscreen mode

Great for one-off downloads. Weak for pipelines: you parse files off disk,
and there’s no fallback when neither track exists.

Method 3: Whisper fallback for caption-less videos

When captions don’t exist, someone has to listen to the audio. The DIY
pipeline is download-then-transcribe:

pip install yt-dlp openai-whisper torch

Enter fullscreen mode Exit fullscreen mode

import whisper
import yt_dlp

with yt_dlp.YoutubeDL({"format": "bestaudio/best", "quiet": True}) as ydl:
    info = ydl.extract_info("https://www.youtube.com/watch?v=VIDEO_ID")
    audio = info["requested_downloads"][0]["filepath"]  # or --extract-audio to wav

model = whisper.load_model("small")  # base = fast/rough, small+ = better
result = model.transcribe(audio, word_timestamps=True)
segments = [
    {"text": s["text"].strip(), "start": s["start"],
     "duration": s["end"] - s["start"]}
    for s in result["segments"]
]

Enter fullscreen mode Exit fullscreen mode

Honest costs of this path: a GPU (or very patient CPU), model downloads,
audio storage, and minutes of compute per video. Fine occasionally,
painful at 100 videos/day. This is exactly the step a managed service
should absorb for you.

The problems everyone hits (with fixes)

1. Rate limits and IP blocks. A fresh datacenter IP (AWS/GCP/most VPS)
works for a while, then YouTube starts returning 429s and bot-check pages.
I measured a block after ~100–200 requests in a few hours from a cloud IP.
Fix: add delays between requests for small jobs; for bulk workloads, route
through residential proxies (real ISP IPs), not more cloud boxes. I run my
own bulk jobs on WebShare residential proxies.
They’re cheap per-GB and work out of the box (signing up through that link
supports this project at no extra cost to you).

import time
for vid in video_ids:
    try:
        transcripts[vid] = YouTubeTranscriptApi.get_transcript(vid)
    except Exception as e:
        transcripts[vid] = None  # record it, don't crash the batch
    time.sleep(1)

Enter fullscreen mode Exit fullscreen mode

2. One bad video kills the batch. Private, removed, or caption-less
videos raise, so isolate per-video errors (as above) and keep going.

3. Dirty text. Auto captions come with [Music]/[Applause] artifacts
and shaky casing. Minimal cleanup before embedding:

import re
text = re.sub(r"\[.*?\]", "", " ".join(e["text"] for e in transcript))
text = re.sub(r"\s+", " ", text).strip()

Enter fullscreen mode Exit fullscreen mode

Real-world example: RAG over a playlist

This is where DIY glues everything together: fetch every video, chunk by
segment timestamps so citations link back to the exact moment:

playlist = ["https://www.youtube.com/watch?v=VID1",
            "https://www.youtube.com/watch?v=VID2"]
chunks = []
for url in playlist:
    vid = url.split("v=")[-1]
    try:
        t = YouTubeTranscriptApi.get_transcript(vid)
    except Exception:
        continue  # failed video ≠ failed pipeline
    chunks += [{"videoId": vid, "start": s["start"], "text": s["text"]}
               for s in t]
# chunks → embed → vector DB. Re-run weekly; new videos append.

Enter fullscreen mode Exit fullscreen mode

It works, until a playlist is half caption-less videos, or your cloud IP
gets blocked mid-run, or you need it to run unattended every night.

Method 4: the managed shortcut (my Actor)

I packaged the whole guide above into one API call: caption fetching
with language fallback, free server-side translation, playlist expansion,
per-video error isolation, plus a GPU Whisper fallback for caption-less
videos:
lexiie/youtube-transcript-api.

curl -s -X POST "https://api.apify.com/v2/acts/lexiie~youtube-transcript-api/run-sync-get-dataset-items?token=${APIFY_TOKEN}" \
  -H 'Content-Type: application/json' \
  -d '{"urls": ["https://www.youtube.com/watch?v=dQw4w9WgXcQ"], "language": "en"}'

Enter fullscreen mode Exit fullscreen mode

One dataset item per video, same 4-format shape from both engines
(transcriptType: MANUAL | AUTO_GENERATED | ASR_GENERATED), with
playlistId/videoIndex on playlist runs and a structured error.code
on failures. Thin clients so you never touch HTTP:
Python
(pip install requests only) and
Node
(native fetch only), plus an agent skill
and copy-paste examples
for playlists, translation, ASR fallback, and partial failure.

Pricing is pay-per-event, failures free: caption transcripts ~$0.003 each
(tiered down to ~$0.0015), AI fallback a flat ~$0.10 per video regardless
of duration. Per-minute STT APIs bill $0.12–$4.00 per audio hour by comparison.
(Exact rates on the Store Pricing tab.)

Build vs. buy, honestly

DIY (Methods 1–3) if you’re learning, processing a handful of videos a
month, or have requirements no API meets. The Actor if you need
unattended reliability, caption-less videos transcribed instead of
erroring, or playlist-scale runs without babysitting proxies and GPUs.

Either way, transcripts turn YouTube from a watch-only archive into
queryable data. Happy building, and if you ship something on it (RAG,
summarizer, research corpus), tell me what broke. That’s how the roadmap
gets written.

원문에서 계속 ↗