Skip to main content
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 typeWhen it fires
welcomeOnce, on connect. First frame.
snapshotOnce, right after welcome. Full state of every live game as a baseline.
new_gameFirst time we see a given game_id (e.g., matchday starts)
score_changeScore, period, or clock changes
status_changeGame transitions unplayed → live → final
game_finalGame 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

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

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 — full schema for score_change, status_change, game_final, new_game
  • Odds Channel — for price events alongside live state