Search documentation

Search pages and headings

API Reference

POST /v1/poll

Exchange a continuation token for the next batch of messages and the token that follows it.

Advances the stream by one step. Call it in a loop until token comes back null.

POST /v1/poll
Content-Type: application/json

Request

Field Type Required Description
token string yes The continuation token from the previous response
client string yes The client label from the session response
endpoint "get_live_chat" | "get_live_chat_replay" yes The endpoint from the session response
apiKey string yes The API key from the session response
webVersion string conditional Required when client is "WEB"
{
  "token": "0ofMyAN...",
  "client": "WEB",
  "endpoint": "get_live_chat",
  "apiKey": "AIzaSy...",
  "webVersion": "2.20240726.00.00"
}

webVersion is what makes the WEB client's version string valid; omitting it for WEB is rejected up front with 400 INVALID_REQUEST rather than being sent to YouTube. Other clients carry a pinned version and ignore the field.

Response

Field Type Description
token string | null The next continuation token, or null when the chat has ended
timeoutMs number How long YouTube suggests waiting before the next poll
messages Message[] Messages in this batch; frequently empty on a quiet stream
{
  "token": "0ofMyANyaHR0cHM6...",
  "timeoutMs": 5000,
  "messages": [
    {
      "type": "chat",
      "id": "ChwKGkNJ...",
      "timestamp": "1:23 PM",
      "author": "Some Viewer",
      "message": "first",
      "amount": "",
      "color": "",
      "parts": [{ "kind": "text", "text": "first" }]
    }
  ]
}

An empty messages array is normal and is not an error — it means nothing was said in that window. Keep polling.

Token discipline

Each response's token replaces the one you sent. There is exactly one live token per stream at a time.

let token = session.token

while (true) {
  const poll = await postPoll({ ...session, token })

  handle(poll.messages)

  if (poll.token === null) break   // chat is over
  token = poll.token               // advance
  await sleep(poll.timeoutMs)
}

Reusing a spent token, or resuming with one saved from an earlier run, is the usual cause of 410 TOKEN_EXPIRED. Recover by calling /v1/sessions again for a fresh token — the client library does this automatically.

Pacing

timeoutMs is YouTube's own suggestion and varies with chat volume. When YouTube sends no value, the server substitutes a 1000 ms default.

The endpoint itself imposes no rate limit, but polling far faster than timeoutMs gains you nothing and risks upstream throttling. The client library clamps the delay between 500 ms and 15 000 ms.

Errors

Status Code Cause
400 INVALID_REQUEST Body failed validation, or client is WEB with no webVersion
410 TOKEN_EXPIRED The continuation token is no longer accepted
422 UNKNOWN_CLIENT client is not one of the known innertube client labels
502 UPSTREAM_ERROR YouTube returned an error for this poll

Full descriptions in Errors.