YouTube Data API · Captions · Troubleshooting
YouTube Data API captions.download Only Works on Your Own Videos
captions.download needs OAuth and permission to edit the video. An API key and a public video ID return 403. Quota on the default project is about 50 downloads a day.
· 3 min read · YTAPI
You enabled the YouTube Data API, created an API key, and called captions.download with a video ID you do not own. The response is 403 forbidden, with this message:
The permissions associated with the request are not sufficient to download the caption track. The request might not be properly authorized.That is the method working as documented.
The captions.download reference says the caller needs permission to edit the video. The accepted credentials are OAuth scopes youtube.force-ssl or youtubepartner. An API key is not on that list. tfmt and tlang change the file format and can ask for a machine translation. They do not grant access to someone else's track. onBehalfOfContentOwner is for a YouTube content partner acting for a channel they already manage.
captions.list is the same kind of call. It returns track IDs, not the text, it costs quota, and it answers 403 when the token cannot see those tracks. Listing with a public video ID and an API key is not a documented path to the caption file.
Checked against those two reference pages on 2026-09-27. The download page was last updated 2026-09-15.
What you get if the video is yours
Upload the captions in YouTube Studio, or with captions.insert. Then:
GET https://www.googleapis.com/youtube/v3/captions?part=snippet&videoId=VIDEO_ID
Authorization: Bearer OAUTH_TOKENTake an id from that response and download it:
GET https://www.googleapis.com/youtube/v3/captions/CAPTION_ID?tfmt=srt
Authorization: Bearer OAUTH_TOKENThe body is the caption file. Content-Type is application/octet-stream. tfmt accepts the formats listed on the tfmt parameter, including srt and vtt.
The captions.download reference puts that call at 200 units. On the quota calculator, captions.list is 50, and a new project gets 10,000 units a day for this bucket, shared with the other methods. Fifty downloads, if you spend the day on nothing else. List plus download is 250 units, so forty videos. The quota resets at midnight Pacific time. Raising it is a Google Cloud quota request, and it is still limited to videos the token can edit.
videos.list with an API key is the call that works on public videos. It returns title, description, duration, and statistics. It does not return caption cues.
Public videos
For a video you do not own, the official API is the wrong tool. People then reach for one of these:
youtube-transcript-api. It reads the caption track the player uses. It works from a laptop and raisesRequestBlockedorIpBlockedfrom AWS, GCP, and most serverless hosts. How to tell that apart from a video that really has no captions: TranscriptsDisabled, NoTranscriptFound, and CouldNotRetrieveTranscript.- yt-dlp, subtitles only.
--skip-download --write-subs --write-auto-subswrites a caption file and still has to get past the bot check and HTTP 429. - A transcript API. One request, no OAuth consent screen, no edit permission on the video.
curl -s https://api.ytapi.dev/v1/transcripts \
-H "Authorization: Bearer $YTAPI_KEY" \
-H "Content-Type: application/json" \
-d '{"video_id": "dQw4w9WgXcQ", "format": "srt"}'That returns the caption text YouTube already generated or the creator already uploaded. It does not return a video you could not download through the Data API either. No captions is a 404, and it is free. Parameters: Extract transcript.
FAQ
Why does captions.download return 403 forbidden?
Because the request isn't authorized by someone who can edit the video. The Data API only lets the owner, or someone the owner granted access, download a caption track, whatever the API key or OAuth scopes you use.
How much quota does captions.download use?
200 units per call, against a default daily quota of 10,000 units, so about 50 downloads a day even for your own videos.
How do I get captions for other people's public videos?
Not through the Data API. Use a library that reads the captions the YouTube player loads (see the Python guide), or a transcript API like the call above.