HomeBlogTranscribePlansAPI Keys

YouTube Data API로 스크립트 받기: captions.download가 실패하는 이유

YouTube Data API captions.download 엔드포인트의 403 Forbidden 응답을 보여주는 터미널

YouTube Data API로 스크립트를 얻으려 했다면 아마 이미 벽에 부딪혔을 겁니다. captions.list는 어떤 영상이든 자막 트랙 ID를 순순히 반환하지만, 그중 하나에 captions.download를 호출하는 순간 403이 떨어집니다. API 키를 다시 확인하고, youtube.force-ssl 스코프를 추가하고, OAuth를 시도해도 여전히 403입니다. 이것은 버그도 아니고 할당량 문제도 아닙니다. 의도된 설계이며, 문서는 그 핵심을 깊숙이 묻어 놓았습니다.

제한 사항, 정확하게

captions.download영상을 소유한 계정의 OAuth 토큰을 요구합니다. 아무 인증된 계정이 아니라 채널 소유자(또는 해당 권한을 가진 콘텐츠 관리자)여야 합니다. 자막 엔드포인트는 크리에이터가 자기 업로드 영상의 자막 트랙을 관리하라고 존재하는 것입니다. .srt 업로드, 오타 수정, 잘못된 자동 번역 삭제 같은 일 말이죠. 임의의 제3자 영상에서 자막을 다운로드하는 것은 애초에 계약에 없던 일입니다.

오류 메시지를 잘 읽어 보면 그대로 적혀 있습니다:

The permissions associated with the request are not sufficient
to download the caption track.

그래서 흔한 격상 경로 — API 키 → 본인 OAuth → 더 넓은 스코프 → 서비스 계정 — 는 어디에도 닿지 않습니다. 어느 단계든 같은 소유권 검사에서 실패합니다. 저도 이걸 받아들이기까지 한나절을 태웠고, Stack Overflow 트래픽으로 판단컨대 인터넷의 절반이 그랬습니다.

captions.list가 여전히 쓸모 있는 곳

Data API를 공정하게 평가하자면, captions.list는 일반 API 키로 동작하고 할당량 약 50 유닛이 듭니다. 어떤 트랙이 존재하는지, 언어는 무엇인지, 자동 생성(trackKind: "asr")인지 사람이 올린 것인지 알려줍니다. 사전 점검용으로는 정말 유용합니다. 영상에 트랙이 아예 없다면, 자막 추출 대신 음성-텍스트 변환이 필요하다는 걸 미리 알 수 있으니까요. Data API는 검색, 재생목록 열거, 메타데이터에도 여전히 올바른 도구입니다. 다만 스크립트 텍스트는 내주지 않을 뿐입니다.

옵션 1: 플레이어가 하는 방식으로 자막 가져오기

영상 플레이어는 당연히 소유자의 OAuth 토큰 없이 자막 데이터를 받으며, youtube-transcript-api(Python) 같은 라이브러리도 같은 방식으로 가져옵니다. 노트북에서는 잘 동작하고 세 줄이면 됩니다.

문제는 프로덕션에서 드러납니다. YouTube는 데이터센터 IP 대역에서 오는 요청을 공격적으로 검문합니다. 같은 세 줄을 AWS, GCP, Heroku에 배포하면 "Sign in to confirm you're not a bot"과 IpBlocked 오류가 나타나기 시작합니다. 이 실패 모드와 우회 방법은 youtube-transcript-api가 클라우드에서 차단되는 이유에 따로 정리해 두었습니다. 요약하자면, 주거용 프록시로 해결할 수 있지만 그 자체가 하나의 프로젝트입니다.

옵션 2: 호스팅된 스크립트 API

URL에서 텍스트만 뽑고 싶다면, 호스팅된 엔드포인트가 두 문제를 모두 건너뜁니다. 소유권 요건도 없고 프록시 돌보기도 없습니다. youtube2text.org는 서버 측에서 로테이팅 주거용 프록시 뒤에서 자막 수집을 수행하므로, 여러분의 요청이 어디서 오든 상관없습니다:

# grab a free demo key (shared, 5 videos/month per IP)
curl -s https://youtube2text.org/api/demo-key

# fetch a transcript
curl -s "https://youtube2text.org/api/transcribe?url=https://www.youtube.com/watch?v=VIDEO_ID" \
  -H "x-api-key: yt_YOUR_KEY"

응답은 일반 JSON입니다:

{"result": {"videoId": "...", "title": "...", "pubDate": "...",
            "content": "full transcript text ...", "contentSize": 12345, "truncated": false}}

영상에 그냥 자막이 없는 경우라면 404와 함께 TRANSCRIPT_UNAVAILABLE을 받습니다. captions.list가 빈 트랙 목록으로 보여줬을 상황을 두 번이 아닌 한 번의 호출로 감지하는 것뿐입니다.

오류 처리를 포함한 더 긴 안내는 curl로 YouTube 스크립트 추출하기에 있고, requests 기반 코드가 더 편하다면 youtube-transcript-api 마이그레이션 가이드에 Python 버전이 있습니다.

합리적인 조합 구성

양쪽에서 다 싸워 본 입장에서 오늘 실제로 만들 구성은 이렇습니다:

captions.download에게 그 중간 역할을 억지로 시키는 것만은, 소유하지 않은 영상에 대해서는 범주적으로 불가능한 경로입니다. 어떤 스코프, 키, 콘솔 설정도 그걸 바꾸지 못합니다.

공용 데모 키 대신 자신만의 키를 원한다면 youtube2text.org/app/keys에서 Google 로그인만으로 가능합니다. 전화번호도, 카드도 필요 없습니다. 무료 요금제는 월 5개 영상이고, 유료 플랜은 50개에 $5.99부터 시작합니다.