Skip to content

Instantly share code, notes, and snippets.

@limhenry
Created July 29, 2026 17:04
Show Gist options
  • Select an option

  • Save limhenry/d9ba0f65234a496a16999714fc87ac78 to your computer and use it in GitHub Desktop.

Select an option

Save limhenry/d9ba0f65234a496a16999714fc87ac78 to your computer and use it in GitHub Desktop.
Get Synced Lyrics from YouTube Music API
// Get Synced Lyrics from YouTube Music API
const videoId = "B7kKeTRV0Xs";
const API_KEY = "AIzaSyC9XL3ZjWddXya6X74dJoCTL-WEYFDNX30"; // this is hard coded
const getTimestamp = (ms) => {
const h = String(Math.floor(ms / 3600000)).padStart(2, "0");
const m = String(Math.floor((ms % 3600000) / 60000)).padStart(2, "0");
const s = String(Math.floor((ms % 60000) / 1000)).padStart(2, "0");
return `${h}:${m}:${s}`;
};
const getBrowserId = async (videoId) => {
// Step 1: Call /next to get watch data
const req = await fetch(
`https://music.youtube.com/youtubei/v1/next?alt=json&key=${API_KEY}`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
context: {
client: {
clientName: "WEB_REMIX",
clientVersion: "1.20240101.01.00",
},
},
videoId: videoId,
}),
},
);
const res = await req.json();
// Find lyrics tab browseId
const tabs =
res?.contents?.singleColumnMusicWatchNextResultsRenderer?.tabbedRenderer
?.watchNextTabbedResultsRenderer?.tabs || [];
for (const tab of tabs) {
const endpoint = tab.tabRenderer?.endpoint?.browseEndpoint;
const pageType =
endpoint?.browseEndpointContextSupportedConfigs
?.browseEndpointContextMusicConfig?.pageType;
if (pageType === "MUSIC_PAGE_TYPE_TRACK_LYRICS") return endpoint.browseId;
}
};
const getSyncedLyrics = async (browseId) => {
const browseRes = await fetch(
`https://music.youtube.com/youtubei/v1/browse?alt=json&key=${API_KEY}`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
context: {
client: { clientName: "ANDROID_MUSIC", clientVersion: "7.21.50" },
},
browseId,
}),
},
);
const browseData = await browseRes.json();
const lyricsData =
browseData?.contents?.elementRenderer?.newElement?.type?.componentType
?.model?.timedLyricsModel?.lyricsData;
const parsedLyrics = lyricsData?.timedLyricsData.map(
(l) => `[${getTimestamp(l.cueRange.startTimeMilliseconds)}] ${l.lyricLine}`,
);
return parsedLyrics;
};
const browserId = await getBrowserId(videoId);
const lyrics = await getSyncedLyrics(browserId);
console.log(lyrics); // lyrics is array of text like "[00:00:02] 經過沿海的公路 越過漫漫河流"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment