YouTube.js

Unofficial JavaScript/TypeScript client for YouTube's internal InnerTube API, for Node.js, Deno, and browsers.

SDK
npm
v18.0.0
5,101stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
89/100Excellent
Development Activity96
Maintenance100
Community64
Maturity56
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
79/100Good
Architecture85
Code Quality82
Innovation80
Learning Curve68

youtubei.js (published to npm as youtubei.js) is an unofficial client for YouTube’s private InnerTube API — the same protocol YouTube’s own web, mobile, TV, and music clients use internally. Instead of scraping HTML or relying on the limited official Data API, it speaks InnerTube directly, exposing video info, streaming formats, search, playlists, channels, comments, notifications, and live chat as strongly-typed objects.

It runs on Node.js, Deno, Cloudflare Workers, React Native, and modern browsers via dedicated build targets, and ships first-class support for YouTube Music, YouTube Kids, and YouTube Studio surfaces in addition to the main site. Because it emulates real InnerTube clients (web, Android, iOS, TV, and more), it can access data and formats the public Data API does not expose, at the cost of needing to track YouTube’s undocumented, frequently-changing internal API surface.

What You Get

  • A fully typed Innertube client (Innertube.create()) exposing getInfo, getBasicInfo, search, getChannel, getPlaylist, getComments, and dozens more high-level methods over generated protobuf-backed request/response types
  • Emulation of multiple real InnerTube client types (WEB, ANDROID, iOS, TV, ANDROID_MUSIC, WEB_KIDS, WEB_CREATOR, and more) via a ClientType enum, letting callers pick the surface with the access level or format availability they need
  • Dedicated parser namespaces for YouTube Music (ytmusic), YouTube Kids (ytkids), YouTube Shorts (ytshorts), and YouTube Studio, each with their own typed response classes under src/parser
  • Platform-specific entry points (node.js, web.js, deno.ts, react-native.js, cf-worker.js) plus a pre-bundled browser build, so the same library runs across server, edge, and client environments
  • Built-in OAuth2 device-flow authentication, session caching (UniversalCache), player signature/cipher decryption, and DASH/HLS manifest generation for downloadable streams

Common Use Cases

  • Building a self-hosted YouTube frontend or proxy (à la Invidious/Piped) that needs richer data and format access than the official Data API quota allows
  • Downloading or transcoding video/audio streams by resolving InnerTube’s adaptive format list and signature-ciphered URLs
  • Reading and posting to live chat during a livestream, or polling comments and notifications programmatically
  • Powering YouTube Music-style apps (search, playlists, lyrics, up-next queues) using the Music client namespace
  • Scripting bulk metadata extraction (channel uploads, playlist contents, transcripts) for archival, research, or ML dataset pipelines

Under The Hood

Architecture — The library centers on a single Innertube facade (src/Innertube.ts) that wraps a Session (src/core/Session.ts), which owns the InnerTube Context payload, an Actions dispatcher (src/core/Actions.ts) for signed API calls, a Player (src/core/Player.ts) responsible for extracting and running YouTube’s obfuscated signature/cipher-decryption logic, and an HTTPClient/cache layer. Raw JSON responses are handed to a large parser layer (600+ files under src/parser, split into youtube, ytmusic, ytkids, ytshorts, and generic classes/nodes mixins) that turns InnerTube’s deeply nested, loosely-typed continuation-based responses into typed domain objects like VideoInfo, Search, Playlist, and Channel. Protobuf-encoded request parameters are generated from .proto definitions in protos/ via ts-proto, and platform entry points under src/platform (node.js, web.js, deno.ts, react-native.js, cf-worker.js) swap out runtime-specific pieces (fetch, crypto, storage) behind shared interfaces.

Tech Stack — Pure TypeScript (98.8% of the codebase) compiled with a patched tsc (ts-patch/tspc) under strict: true, targeting ESM type: module output with per-platform builds bundled via esbuild (bundle:browser, bundle:cf-worker, bundle:react-native). Runtime dependencies are minimal and deliberate: @bufbuild/protobuf for protobuf wire encoding, fflate for zlib/gzip decompression, and meriyah (a JS parser) used to statically analyze YouTube’s player script for signature-cipher extraction — notably it has zero HTTP-client or DOM-scraping dependencies, doing everything through raw fetch and hand-rolled response parsing.

Code Quality — Tests live in a single tests/main.test.ts (475 lines) run via Vitest, which is thin relative to the ~667 source files and reads as smoke/integration coverage of core client flows rather than exhaustive unit coverage of the parser layer — reasonable given how much of the parser surface mirrors a constantly-shifting external API rather than internal logic worth isolating. Strict TypeScript, consistent private-field (#session) encapsulation, an ESLint config with the typescript-eslint recommended rules, and a throwIfMissing guard pattern for required arguments all point to a codebase that takes correctness seriously; the main quality risk is structural rather than stylistic — coupling to YouTube’s undocumented response shapes that can break without warning on YouTube’s side.

API Design — The public surface is intentionally small and high-level: Innertube.create() plus a handful of verbs (getInfo, search, getChannel, getPlaylist, getComments) return rich typed objects with further chainable methods (e.g. pagination via getContinuation()), so common tasks need very little boilerplate. Client-type switching (ClientType.ANDROID, .TV, etc.) is exposed as a simple enum rather than requiring separate SDKs per surface, and platform selection happens automatically via package.json exports conditions, so the same import statement works across Node, Deno, and browser without user-side branching. The main ergonomic cost is conceptual: understanding which client type or po_token/auth setup a given operation needs requires reading the docs site (ytjs.dev) rather than being fully self-evident from types alone.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search