Search documentation

Search pages and headings

Client Library

Usage

Construct the client, connect, stream messages, and stop cleanly.

The shortest version

import { LiveChatApiClient } from '@gettersethya/yt-livechat-client'

const client = new LiveChatApiClient({
  baseUrl: 'http://localhost:3000',
  videoUrl: 'https://www.youtube.com/watch?v=zvwJ29RFVww',
})

client.on('message', (message) => {
  console.log(`${message.author}: ${message.message}`)
})

await client.connect()
await client.start()

Constructing

new LiveChatApiClient(
  { baseUrl: string, videoUrl: string },
  options?: LiveChatClientOptions,
)
Argument Field Description
required baseUrl Your API server's origin. Trailing slashes are trimmed
required videoUrl A YouTube URL or a bare 11-character id
optional options See Events & Options

The constructor resolves videoUrl immediately and throws an ApiHttpError with code INVALID_VIDEO_ID if it cannot. This is a synchronous throw, not a rejected promise:

import { ApiHttpError, LiveChatApiClient } from '@gettersethya/yt-livechat-client'

try {
  const client = new LiveChatApiClient({ baseUrl, videoUrl: userInput })
} catch (error) {
  if (error instanceof ApiHttpError) {
    console.error(error.code) // "INVALID_VIDEO_ID"
  }
}

Accepted video URL forms

zvwJ29RFVww
https://www.youtube.com/watch?v=zvwJ29RFVww
https://youtu.be/zvwJ29RFVww
https://www.youtube.com/shorts/zvwJ29RFVww
https://www.youtube.com/embed/zvwJ29RFVww
https://www.youtube.com/live/zvwJ29RFVww

Extra query parameters are fine. To validate input before constructing, use extractVideoId, which returns null instead of throwing:

import { extractVideoId } from '@gettersethya/yt-livechat-client'

if (extractVideoId(userInput) === null) {
  showError('That does not look like a YouTube video link.')
}

The lifecycle

connect()

Calls /v1/sessions, stores the session on the instance, emits connected, and resolves with the client itself. It fetches the backlog but delivers no message events yet.

await client.connect()
console.log(client.videoId)          // "zvwJ29RFVww"
console.log(client.session?.chatType) // "liveChatRenderer"

Calling start() before connect() throws an ApiHttpError with code INTERNAL and the message call connect() before start().

start()

Runs the polling loop. Messages arrive via the message event; the returned promise resolves only once the stream has ended — because the chat finished, because too many errors accumulated, or because you called stop().

await client.start()
console.log('stream finished')

Because it is long-running, do not await it if you have other work to do on the same task:

void client.start()

stop()

Returns immediately and never blocks. It sets a stop flag the loop notices within about 250 ms and interrupts message delivery.

setTimeout(() => client.stop(), 60_000) // stop after a minute

Queued-but-undelivered messages are dropped on stop(). When a stream ends naturally the queue is drained in full first — so stop() is a cancel, not a graceful flush.

Reading a video's chat into an array

import { LiveChatApiClient } from '@gettersethya/yt-livechat-client'
import type { MessageSchema } from '@gettersethya/yt-livechat-client'

async function collect(videoUrl: string) {
  const client = new LiveChatApiClient({ baseUrl: 'http://localhost:3000', videoUrl })
  const messages: MessageSchema[] = []

  client.on('message', (message) => messages.push(message))

  await client.connect()
  await client.start()   // resolves when the replay is exhausted

  return messages
}

This works well for a finished stream's replay, which terminates on its own. A live stream will not stop until the broadcast does, so pair it with stop().

Reconnecting to the same video

Create a new client. connect() may be called again on an existing instance — it is what the automatic TOKEN_EXPIRED recovery does internally — but the instance keeps one session at a time, so a fresh client is clearer.

Rendering in a UI

message.message is plain text and fine for logs. For a chat UI, render from message.parts so custom emoji appear as images — see Rendering Message Bodies.