> ## Documentation Index
> Fetch the complete documentation index at: https://docs.polynode.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Live Channel (/ws/live)

> Live game state only. Scores, clocks, period changes, game start/final.

The `/ws/live` channel emits only events that describe game state. No price changes, no market movement, no odds. **If you want a scoreboard UI, this is your channel.**

```
wss://books.polynode.dev/ws/live?key=pn_live_YOUR_KEY
```

## Event types on this channel

| Event `type`    | When it fires                                                           |
| --------------- | ----------------------------------------------------------------------- |
| `welcome`       | Once, on connect. First frame.                                          |
| `snapshot`      | Once, right after welcome. Full state of every live game as a baseline. |
| `new_game`      | First time we see a given `game_id` (e.g., matchday starts)             |
| `score_change`  | Score, period, or clock changes                                         |
| `status_change` | Game transitions `unplayed → live → final`                              |
| `game_final`    | Game ends (paired with the matching `status_change`)                    |

No `price_change` events are delivered on this channel.

## Real captured sample

This is the exact wire content from `wss://books.polynode.dev/ws/live` during a live Copa Libertadores match (Estudiantes 2-1 Cusco, 94' stoppage):

```
{"type":"welcome","channel":"live","conn_id":1,"message":"connected to books-relayer live channel"}
{"type":"snapshot","channel":"live","ts":1776210900000,"count":11,"games":[{"game_id":"40664-16839-2026-04-14","pn_slug":"lib-gar-est-2026-04-14","pn_league_code":"lib","home_team":{"name":"Club Estudiantes de La Plata",...},"away_team":{"name":"Cusco FC",...},"scores":{"score_home":2,"score_away":1,"period":"2H","clock":"94","is_live":true,...},...}, ... 10 more games ...]}
{"type":"score_change","game_id":"10035-20332-2026-04-14","pn_slug":null,"pn_league_code":"lib","score_home":1,"score_away":0,"period":"2H","clock":"64","ts":1776208972203}
{"type":"score_change","game_id":"40664-16839-2026-04-14","pn_slug":"lib-gar-est-2026-04-14","pn_league_code":"lib","score_home":2,"score_away":1,"period":"2H","clock":"64","ts":1776208972203}
```

Notice the sequence: **welcome → snapshot → deltas**. The snapshot gives you the baseline state of all 11 currently-live games, then deltas flow from that point forward.

The first game (`10035-20332-2026-04-14`) is polynode-unmapped (`pn_slug: null`) — a Nacional @ Tolima fixture whose teams aren't in polynode's roster. The second is `lib-gar-est-2026-04-14` (Estudiantes vs Cusco FC, currently 2-1).

## Why is this channel quiet sometimes?

During calm windows — no goals, no score changes, no clock rollovers, no games going live — this channel emits nothing except `welcome` and occasional heartbeat pings. **That's correct**. It's a state-change channel, not a heartbeat channel.

If you're building an idle-tolerance UI: show connection status from the TCP/WebSocket state, not from "did we get a message in the last N seconds." A live `/ws/live` stream with zero messages for 5 minutes just means no goals were scored.

## Filtering

Server-side filtering is **not yet implemented** in v1. All subscribers to `/ws/live` receive all live events for all mapped sports and leagues. Filter client-side on `pn_league_code`, `sport_category` (via a lookup from `pn_league_code`), or `pn_slug`.

## Example: Python client

```python theme={null}
import asyncio, json, websockets

KEY = "pn_live_YOUR_KEY"

async def main():
    uri = f"wss://books.polynode.dev/ws/live?key={KEY}"
    async with websockets.connect(uri) as ws:
        async for raw in ws:
            event = json.loads(raw)
            if event["type"] == "score_change":
                print(f"{event['pn_league_code']} {event['pn_slug'] or event['game_id']}: "
                      f"{event['score_away']}-{event['score_home']} "
                      f"({event['period']} {event['clock']})")

asyncio.run(main())
```

Sample output during an active soccer match:

```
lib lib-gar-est-2026-04-14: 1-2 (2H 50)
lib lib-gar-est-2026-04-14: 1-2 (2H 51)
lib lib-gar-est-2026-04-14: 1-2 (2H 52)
```

## Example: Node.js client

```javascript theme={null}
import WebSocket from 'ws';

const KEY = process.env.POLYNODE_KEY || 'pn_live_YOUR_KEY';
const ws = new WebSocket(`wss://books.polynode.dev/ws/live?key=${KEY}`);

ws.on('message', (raw) => {
  const e = JSON.parse(raw);
  if (e.type === 'score_change') {
    console.log(
      `${e.pn_league_code} ${e.pn_slug ?? e.game_id}: ` +
      `${e.score_away}-${e.score_home} (${e.period} ${e.clock})`
    );
  }
});
```

## See also

* [Event Reference](/websocket/books/events) — full schema for `score_change`, `status_change`, `game_final`, `new_game`
* [Odds Channel](/websocket/books/odds) — for price events alongside live state
