Back to blog

yt-dlp · Subtitles · Troubleshooting

yt-dlp HTTP Error 429 When Downloading Subtitles

HTTP 429 on a subtitle download means YouTube rate-limited that client. Wait, sleep between subtitle requests, and stop retrying in a loop. A bot-check message is a different error.

· 3 min read · YTAPI

The watch page loads, and then yt-dlp dies while saving the captions:

ERROR: Unable to download video subtitles for 'en': HTTP Error 429: Too Many Requests

Some versions print WARNING instead of ERROR for the same line. A different 429, Unable to download webpage, happens earlier, on the watch page itself. Both mean YouTube is refusing more requests from this client for a while.

The limit can also be worded around translation:

429 error: YouTube has limited machine-translated subtitle requests for this device

That one names the usual cause outright: auto-translated subtitle tracks. A translated track is generated on request, and YouTube limits those harder than the captions the video actually has. Ask for the language the video was captioned in and translate the text yourself.

Subtitles are a separate request from the video page. --skip-download skips the video file and still requests each caption track. Asking for every auto-translated language, on a playlist, from a few parallel processes, is a common way to get the subtitle line. YouTube has rate-limited those translated tracks harder than the original. A 5-second pause is sometimes too short. People have needed on the order of a minute between subtitle files, and a datacenter address can stay limited after that.

If the message is Sign in to confirm you're not a bot, you are on the other error. Sleeping does not clear that one. The two show up together on hosting IPs, because a refused datacenter address answers with whichever response YouTube feels like that minute. Fix the rate limit first only when the status really is 429.

Stop, then slow the subtitle requests down

A retry loop that starts the moment 429 comes back extends the limit. Wait a few minutes. Then put a pause on the requests that caused it. yt-dlp has a switch for the subtitle download specifically:

yt-dlp --skip-download \
  --write-subs --write-auto-subs \
  --sub-langs "en.*" \
  --sleep-subtitles 5 \
  --sleep-requests 1 \
  "https://www.youtube.com/watch?v=dQw4w9WgXcQ"

--sleep-subtitles waits before each subtitle file. --sleep-requests waits between the requests made while reading the page. Raise the subtitle pause if 5 seconds still returns 429. Translated auto-subs are the usual reason. There is a preset, -t sleep, which the current README expands to --sleep-subtitles 5 --sleep-requests 0.75 --sleep-interval 10 --max-sleep-interval 20. Check yt-dlp --help if you rely on the preset. The numbers are the project's, and they can change.

The yt-dlp FAQ treats 429 as a soft block: open YouTube in a browser, complete the CAPTCHA if one is shown, then pass that browser's cookies. Use the same machine, because the cleared session is tied to the address that solved it. That is a reasonable step for a download you are running yourself. It still attaches the job to your Google account.

--list-subs shows the tracks without writing them. Fetch the language you need. --sub-langs all on a video that has a dozen auto-translated tracks is a dozen extra requests.

One process. Several containers with the same command share nothing except YouTube's view of the IP, and they burn the limit together.

--retry-sleep is there for the retries yt-dlp itself performs. An HTTP 429 that your own shell script catches and re-runs immediately never reaches that option.

When slowing down stops working

A laptop that fetched a few hundred captions and then got a 429 will usually recover after a pause. A cloud server that gets a 429 on the first videos of the day is often being refused because of the address, and the next response may be the bot check instead. More sleep will not change the network the packets come from. Residential proxy exits, or moving the job off that server, are the options left. The same IP story for the Python transcript library is written up for AWS and Google Cloud.

If the file you wanted was the transcript

For the caption text, one HTTP call replaces the subtitle download. There is no video file on the other side of it.

curl -s https://api.ytapi.dev/v1/transcripts \
  -H "Authorization: Bearer $YTAPI_KEY" \
  -H "Content-Type: application/json" \
  -d '{"video_id": "dQw4w9WgXcQ", "format": "srt"}'

srt and vtt come back as the caption file itself. Failed calls are free, including rate-limit (429) responses and videos with no captions. The request fields are in the extract reference.

FAQ

Which yt-dlp options slow down subtitle requests?

--sleep-subtitles waits before each subtitle download, --sleep-requests between the requests yt-dlp makes while extracting, and --sleep-interval before each video download. For subtitle-only runs, the first two matter.

How long does a 429 last?

YouTube doesn't say, and it varies. Stop and wait rather than retrying right away: retries during a block tend to extend it.

Do cookies help with 429?

Sometimes, but they tie your requests to an account, and an account that makes a lot of automated requests can get flagged too. The trade-offs are in Sign in to confirm you're not a bot.