Search documentation

Search pages and headings

API Reference

Overview

Conventions shared by every endpoint — transport, the request cycle, and how a session is acquired.

Conventions

Base path /v1
Method POST on every endpoint — there are no GET routes
Request body JSON, Content-Type: application/json
Response body JSON
CORS Enabled on all routes, so browsers may call the API directly
Auth None. The server holds no credentials of its own

Request bodies are decoded with a schema at the boundary. A body that does not match returns 400 INVALID_REQUEST with the validation message.

The endpoints

Endpoint Purpose
POST /v1/sessions Bootstrap a chat: returns credentials and the first continuation token
POST /v1/poll Exchange a token for messages and the next token
POST /v1/probe Diagnose which innertube clients work for a video

The request cycle

sessions is called once. poll is called repeatedly, each time with the token returned by the previous call.

sessions ──> { apiKey, webVersion, client, endpoint, token, timeoutMs }

                  └──> poll ──> { messages, token, timeoutMs }

                                     └──> poll ──> ...  until token === null

Because the server keeps nothing between requests, the four values apiKey, webVersion, client and endpoint must be echoed back on every poll along with the current token. Treat the session response as an opaque bundle you carry forward.

What happens during a session bootstrap

Understanding this explains most of the error codes.

1. Resolve the video id

The input is accepted as a full URL or a bare id and reduced to an 11-character id. Anything else fails with INVALID_VIDEO_ID.

2. Fetch innertube credentials

The server requests https://www.youtube.com/sw.js and extracts INNERTUBE_API_KEY and INNERTUBE_CLIENT_VERSION from it. These are fetched live for every session rather than hardcoded, because YouTube rotates them. A failure here is SW_JS_FAILED.

3. Find the chat renderer

The server loads the popout chat page first (/live_chat?is_popout=1&v=<id>) and falls back to the watch page (/watch?v=<id>). From the HTML it brace-matches the ytInitialData JSON object, then walks it looking for a liveChatRenderer (live) or liveChatReplayRenderer (replay) that carries a continuation token. Which one was found is reported back as chatType.

If neither page yields a renderer with a token, the result is NO_LIVE_CHAT — the video is not live, has chat disabled, has no replay chat, or is unavailable.

4. Pick a working client

A continuation token alone is not enough; it has to be redeemed against an innertube client that YouTube will accept for this video. The server tries WEB, then TVHTML5, and for each tries the get_live_chat endpoint before get_live_chat_replay, stopping at the first combination that returns a parseable response. That winning pair is what comes back as client and endpoint.

If every combination fails, the result is NO_WORKING_CLIENT.

Reading YouTube's failures

YouTube returns HTTP 200 with an error object in the body when an innertube call fails. The server treats such a response as a failure, not a success, and surfaces it as UPSTREAM_ERROR. See Errors for the complete list.