type field that disambiguates the payload shape. This page documents all five event types plus the welcome handshake message.
welcome
Sent exactly once, immediately after a successful WebSocket upgrade. Always the first frame.
Real captured:
snapshot
The second frame every subscriber receives, immediately after welcome and before any delta events. Contains the complete list of currently-live games with their full state. Use this as your baseline — apply all subsequent score_change / status_change / game_final / price_change events on top of this snapshot to maintain a correct live view.
Emitted once, per-subscriber. Not broadcast. Not repeated. Each new connection gets its own fresh snapshot.
Real captured (from the public endpoint during a Copa Libertadores match):
Why snapshot-on-connect?
Before this existed, a new subscriber on/ws/live would see nothing until the next score change fired — which could be seconds or minutes depending on the match. With the snapshot, you know the complete live state from the moment you connect, and can start rendering a scoreboard immediately.
Apply events in order:
- Receive
snapshot— initialize your in-memory view of every live game. - Receive
score_change/status_change/game_finalevents — update the matching entry in your view. - On disconnect + reconnect — discard your state, wait for the new snapshot, restart from step 2.
What snapshot does NOT contain
- Market snapshots are not included even on
/ws/odds. For initial market state, call the REST endpointGET /v1/games/{id}/marketsonce per game you care about, then streamprice_changedeltas from there. Blasting full market state for every live game × every book × every outcome on connect would be tens of MB and pointless for most consumers. - Unplayed games are not included. Only games where
scores.is_live = trueat snapshot build time appear. - Final games are not included. Once a game transitions to
final, it drops out of the live set immediately.
score_change
Emitted whenever score_home, score_away, period, or clock changes on any polled game.
Real captured (Estudiantes 2-1 Cusco, Copa Libertadores, 64th minute):
Channels:
/ws/live, /ws/odds
status_change
Emitted when a game transitions between unplayed → live → final. Fires alongside score_change when the transition happens at a boundary.
Schema:
Channels:
/ws/live, /ws/odds
game_final
Fires immediately after a status_change into final or ended. Provides the final score as a convenience so you don’t have to join status_change + the last score_change.
Schema:
Channels:
/ws/live, /ws/odds
new_game
Emitted the first time the poller sees a particular game_id. Typically fires when a game first appears on upstream’s schedule (days before start), not when it kicks off. For kickoff, watch status_change from unplayed → live.
Schema:
Channels:
/ws/live, /ws/odds
price_change
The core odds-movement event. Fires whenever any book’s price on any outcome changes.
Real captured (888sport shortening their “Estudiantes 2:1” correct-score price from +275 to +230 as Estudiantes went up 2-1):
Channels:
/ws/odds only. (This event is not delivered on /ws/live.)
pong
Response to a client-sent {"action":"ping"} JSON message. Used for application-level keepalive when WebSocket protocol Pings are stripped by intermediate proxies.
Client sends:
Putting it together — correlating events after a goal
The real wire sequence after a goal in a soccer game:- Poll cycle runs, detects
is_live=true+ new score. score_changefires on both channels with the new score.- Multiple
price_changeevents fire on/ws/oddsas each book reprices the relevant outcomes (moneyline, correct score, totals, next goal, anytime goalscorer, etc.). - Clients see the score before the price movements because the diff engine always emits state changes first.

