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/zvwJ29RFVwwExtra 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 minuteQueued-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.