Skip to content

Captions

getInfo lists the tracks:

const info = await yt.getInfo("yuT7iQC-yro");
info.captionTracks; // [{ languageCode, name, isAutoGenerated, isDefault, url }]

The list is empty for a video without captions.

import { CaptionFormat } from "@distube/ytdl-core";
const vtt = await yt.getCaptions("yuT7iQC-yro");
const json = await yt.getCaptions(info, { lang: "en", format: CaptionFormat.Json3 });

getCaptions returns the text YouTube renders. It does not parse cues. Pass a getInfo result to skip the lookup; the call looks the video up again when the info is about to expire.

CaptionFormat Text
Vtt (default) WebVTT
Srv3 XML timed text with per-word timing and styling
Json3 JSON events

lang must equal a track’s languageCode. de does not match de-DE. A manual track wins over an auto-generated one for the same code. Without lang, the call uses the default track, else the first manual track, else the first track.

A language the video lacks rejects with NO_CAPTIONS, and the message lists the codes on offer.

YouTube’s caption endpoint answers HTTP 429 after about eight quick requests from one IP. The call rejects with RATE_LIMITED and does not retry. Cache the text and retry later.

Translation (tlang) is not offered. YouTube answers it with its abuse page (HTTP 429) from server IPs.

Caption URLs need no PO token today. If YouTube starts requiring one, getCaptions fails until it gains a token option. Captions that only a signed-in account can see are not requested with the cookie.