npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

ytmusic-advanced

v2.1.0

Published

A powerful, type-safe YouTube Music API client with audio extraction, signature deciphering, and intelligent search parsing

Downloads

82

Readme

ytmusic-advanced

A powerful, type-safe YouTube Music API client with audio extraction, signature deciphering, and intelligent search parsing.

Current version: 2.1.0 — a complete TypeScript rewrite. See MIGRATION.md if you're upgrading from v1.x.

npm version TypeScript License: MIT Node


✨ Features

  • 🎵 Search YouTube Music — songs, albums, artists, playlists, videos
  • 🗂️ Core Entities — full metadata extraction for Albums, Artists, and Playlists
  • 🌍 Discovery & Explore — fetch Trending Charts, Moods & Genres, and New Releases
  • 📝 Track Details — fetch synchronized lyrics and related/Up Next tracks
  • 👤 User Activity — retrieve Liked Songs and Listening History (requires cookies)
  • 🎧 Direct audio extraction — get playable audio URLs without a browser
  • 🔐 Signature deciphering — automatic YouTube signature decryption
  • 🔄 Multi-client fallbackandroid_testtvweb strategy
  • 📊 Type-safe — full TypeScript types and Zod runtime validation
  • 🍪 Session management — automatic cookie handling for consistency
  • Smart retries — exponential backoff with jitter
  • 🧱 Modular internals — clean, testable parser/request architecture

📦 Installation

npm install ytmusic-advanced

Requirements: Node.js 16 or later.


🚀 Quick Start

CommonJS

const YTMusic = require("ytmusic-advanced");

const client = new YTMusic();

// Search for music
const results = await client.search("Bohemian Rhapsody");
console.log(results);

// Get a playable audio URL
const audioUrl = await client.getAudioURL("fJ9rUzIMcZQ");
console.log(audioUrl);

ESM / TypeScript

import YTMusic, {
  SearchResult,
  SongDetailed,
  AudioFormat,
  VERSION,
} from "ytmusic-advanced";

const client = new YTMusic();

const results: SearchResult[] = await client.search("Queen");
for (const item of results) {
  if (item.type === "SONG") {
    // TypeScript narrows the union automatically
    const song: SongDetailed = item;
    console.log(`${song.name} — ${song.artist.name}`);
  }
}

📖 API Documentation

new YTMusic(options?)

| Option | Type | Default | Description | | -------------- | --------- | ------- | ------------------------------------------ | | mode | 'youtube' \| 'music' | 'youtube' | Initial request mode | | language | string | 'en' | Preferred language for requests | | cacheEnabled | boolean | true | Enable in-memory response caching | | cacheTTL | number | 300000 | Cache TTL in ms (5 min) | | timeout | number | 30000 | Per-request timeout in ms | | retryAttempts| number | 3 | Max retry attempts | | retryDelay | number | 1000 | Base delay for exponential backoff (ms) |

const client = new YTMusic({
  mode: "music",
  language: "en",
  cacheEnabled: true,
  cacheTTL: 5 * 60 * 1000,
});

client.search(query, options?): Promise<SearchResult[]>

Search YouTube Music. Returns a discriminated union of SearchResult.

const results = await client.search("Imagine Dragons");

results.forEach((item) => {
  switch (item.type) {
    case "SONG":
      console.log(`🎵 ${item.name} — ${item.artist.name}`);
      break;
    case "ALBUM":
      console.log(`💿 ${item.name} — ${item.artist.name}`);
      break;
    case "ARTIST":
      console.log(`👤 ${item.name}`);
      break;
    case "PLAYLIST":
      console.log(`📜 ${item.name}`);
      break;
    case "VIDEO":
      console.log(`🎬 ${item.name}`);
      break;
  }
});

client.getAudioURL(videoId, options?): Promise<string>

Extract a direct, playable audio stream URL for a YouTube video.

const audioUrl = await client.getAudioURL("dQw4w9WgXcQ");

// Use with any HTML5 audio player
const audio = new Audio(audioUrl);
audio.play();

Note: URLs typically expire after ~6 hours. Re-extract when needed.


client.getAudioFormats(videoId, clientType?): Promise<AudioFormat[]>

Get all available audio formats for a video. Useful for picking a specific bitrate/codec.

const formats = await client.getAudioFormats("dQw4w9WgXcQ");

formats.forEach((f) => {
  console.log(`${f.mimeType} — ${f.bitrate} bps — ${f.quality}`);
});

Core Entities & Discovery

| Method | Returns | Description | | ------------------------------------ | ------------------------ | ------------------------------------------ | | client.albums.getAlbum(id) | Promise<AlbumDetailed> | Fetch tracks and metadata for an Album | | client.artists.getArtist(id) | Promise<ArtistFull> | Fetch top songs, albums, singles, videos | | client.playlists.getPlaylist(id) | Promise<PlaylistFull> | Fetch tracks for a Playlist | | client.getCharts() | Promise<HomeSection[]> | Fetch trending music charts | | client.getNewReleases() | Promise<HomeSection[]> | Fetch new album & single releases | | client.getGenres() | Promise<HomeSection[]> | Fetch music genres and moods | | client.getLyrics(videoId) | Promise<string> | Fetch synchronized lyrics for a track | | client.getRelatedTracks(videoId) | Promise<UpNextsDetails[]>| Fetch 'Up Next' or related tracks | | client.library.getLikedSongs() | Promise<PlaylistFull> | Fetch liked songs (Requires cookies) | | client.library.getListeningHistory()| Promise<HomeSection[]> | Fetch history (Requires cookies) |


Configuration Methods

| Method | Returns | Description | | ---------------------------- | ------------------------ | ------------------------------------------ | | client.init() | Promise<YTMusic> | Eagerly initialize the request layer | | client.switchMode(mode) | YTMusic | Switch between 'youtube' and 'music' | | client.setLanguage(lang) | YTMusic | Update preferred language | | client.enableCache(bool?) | YTMusic | Toggle caching | | client.setCacheTTL(ms) | YTMusic | Update cache TTL | | client.clearCache() | Promise<void> | Drop all cached responses | | client.getStatus() | object | Inspect cache + mode state |


🎯 TypeScript Types

SearchResult

A discriminated union returned by client.search().

type SearchResult =
  | SongDetailed
  | VideoDetailed
  | AlbumDetailed
  | ArtistDetailed
  | PlaylistDetailed;

SongDetailed

interface SongDetailed {
  type: "SONG";
  videoId: string;
  name: string;
  artist: { name: string; artistId: string | null };
  album: { name: string; albumId: string } | null;
  duration: number | null; // seconds
  thumbnails: ThumbnailFull[];
}

AlbumDetailed / ArtistDetailed / PlaylistDetailed / VideoDetailed

interface AlbumDetailed {
  type: "ALBUM";
  albumId: string;
  playlistId: string;
  name: string;
  artist: { name: string; artistId: string | null };
  year: number | null;
  thumbnails: ThumbnailFull[];
}

interface ArtistDetailed {
  type: "ARTIST";
  artistId: string;
  name: string;
  thumbnails: ThumbnailFull[];
}

interface PlaylistDetailed {
  type: "PLAYLIST";
  playlistId: string;
  name: string;
  artist: { name: string; artistId: string | null };
  thumbnails: ThumbnailFull[];
}

interface VideoDetailed {
  type: "VIDEO";
  videoId: string;
  name: string;
  artist: { name: string; artistId: string | null };
  duration: number | null;
  thumbnails: ThumbnailFull[];
}

AudioFormat

interface AudioFormat {
  itag: number;
  url?: string;          // may be undefined until deciphered
  signatureCipher?: string;
  mimeType: string;      // e.g. "audio/webm; codecs=\"opus\""
  bitrate: number;       // bits per second
  contentLength?: string;
  quality?: string;      // e.g. "tiny", "medium", "hd720"
  qualityLabel?: string;
  audioQuality?: string; // e.g. "AUDIO_QUALITY_MEDIUM"
  approxDurationMs?: string;
  audioSampleRate?: string;
  audioChannels?: number;
}

ExtractionOptions

interface ExtractionOptions {
  quality?: "high" | "medium" | "low";
  format?: "mp4" | "webm" | "any";
}

🆚 Migration: v1 → v2

See MIGRATION.md for the full guide.

Quick highlights:

| v1.x | v2.0 | | ------------------- | ----------------------- | | result.type === 'song' | result.type === 'SONG' | | result.title | result.name | | result.artists[0] | result.artist.name | | getAudioURLs(id) | getAudioURL(id) | | No TypeScript types | Full type definitions | | vm2 dependency | Native node:vm |


🐛 Troubleshooting

"All clients failed" when calling getAudioURL

The library tries android_testtvweb in order. If all three fail:

  • The video may be private, age-restricted, or region-locked
  • The videoId may be invalid
  • YouTube may be rate-limiting your IP — wait a few minutes
try {
  const url = await client.getAudioURL("invalid_id");
} catch (err) {
  if (err instanceof YTMusicError) {
    console.error(err.message, err.context);
  }
}

Audio URL returns 403 when played

Stream URLs include a signature that expires (~6 hours). Re-extract if the cached URL is stale.

TypeScript can't find types

Ensure your tsconfig.json uses a modern moduleResolution:

{
  "compilerOptions": {
    "module": "NodeNext",
    "moduleResolution": "NodeNext"
  }
}

🔧 Advanced Usage

Error handling

import { YTMusicError, YTMusicAPIError } from "ytmusic-advanced";

try {
  const url = await client.getAudioURL(videoId);
} catch (err) {
  if (err instanceof YTMusicAPIError) {
    console.error(`HTTP ${err.context.statusCode}`);
  } else if (err instanceof YTMusicError) {
    console.error(err.message);
  }
}

Custom retry policy

The library exposes the same retry helper it uses internally:

import { withRetry, DEFAULT_RETRY_OPTIONS } from "ytmusic-advanced";

const data = await withRetry(() => fetchSomething(), {
  maxRetries: 5,
  initialDelay: 500,
});

Mode switching

import { YTMusic } from "ytmusic-advanced";

const client = new YTMusic({ mode: "music" });
// Later
client.switchMode("youtube");

📄 License

MIT © Ganesh

⚠️ Disclaimer

This project is not affiliated with YouTube or Google. Use it responsibly and in accordance with YouTube's Terms of Service.

🙏 Acknowledgments