league slug (nba / wnba) tells them apart. Feed reads carry no sport in the URL — the catalog, WebSocket, history backfill, and session health are all top-level endpoints addressed by session id.
Basketball state is split into independent parts — possession and the clock move separately. During free throws, for example, the ball is dead but the shooting team still has possession, so one combined status field couldn’t capture both. Each part has its own event.
Game state
Every WebSocket frame carries a fullGameState snapshot. The history backfill returns events only — to rebuild this state from the log, replay the events; each event’s entry below specifies its exact effect on state. Fields are camelCase on the wire. See the Sports overview for the conventions and cross-sport events (score_set, game_end, undo, scout_status) shared across every sport.
Score
Clock and period
The clock tracks whether play is live or stopped.LIVE is set only by a live press; the stopped states (DEAD, BREAK, TIMEOUT) are each pressed directly.
Possession
Shot attempt
At most one attempt is open at a time. Amade or miss resolves it, and self-describes the shot it resolved on its own event payload — see made below. The state snapshot itself only ever carries the open attempt, not the most recent resolution.
OpenAttempt
Display metadata
The side display names are not onGameState. They are fixed at session creation and ride the top-level matchup {a, b} on every WS frame and on the /api/history / /api/health responses (from the session manifest). Scoring stays on the abstract A/B sides.
Enum values
Clock values
Shot-type values
Events
Events arrive on the WebSocket asframe.event. The type field is the discriminator; payload carries the event’s fields minus type.
State snapshot field names are camelCase; event payload field names are snake_case (shot_type, not shotType).
Possession and clock
possession — who has the ball
possession — who has the ball
Sets
state.possession — the only event that does. It can be set while the ball is dead (an inbound during a timeout) and never moves the clock. The first possession of the game also fixes tipoffTeam (the possession arrow).live — ball in play
live — ball in play
The only event that starts the clock. Resumes to
LIVE from PREGAME (tip-off) or any stopped state (DEAD/BREAK/TIMEOUT), and clears the free-throw countdown (play resuming means the trip is over). Resuming from a BREAK is the quarter tip: it applies the period advance the period_end press set up, so period increases here. Nothing else starts the clock — possession and shot attempts never do.No payload fields.dead — same-period pause
dead — same-period pause
Ball out of play — a brief pause (foul, out-of-bounds, held ball). Pressing it stops play directly. It’s the general-purpose stop; the scout records the cause afterward while the ball is dead (
to_the_line → award_free_throws for a foul). Leaves the period unchanged; play resumes on the next live.No payload fields.period_end — the current period ended
period_end — the current period ended
Ball out of play — end of a quarter, half, or regulation. When it fires,
period still shows the period that just ended, so the display can read “End of Q1”. It sets up the next advance, so the next live press — the quarter tip — increases period. The scout never types the new number for a normal rollover.No payload fields.timeout — timeout pause
timeout — timeout pause
Ball out of play — a timeout, called out as its own event so the tape reads it as a timeout rather than a generic dead ball. Self-contained: pressing it stops play directly. Carries no team or count — only that play paused is recorded. A same-period pause (like
dead); play resumes on the next live.No payload fields.Shots and scoring
2pt_attempt — two-point field goal up
2pt_attempt — two-point field goal up
A live two-point field goal is in the air — opens an attempt. The shot type is the event;
team is the shooter’s side. If the clock is not LIVE, this is flagged as an anomaly — the scout likely missed the live press.3pt_attempt — three-point field goal up
3pt_attempt — three-point field goal up
A live three-point field goal is in the air — opens an attempt. Behaves exactly like
2pt_attempt; only the point value differs.ft_attempt — free-throw attempt
ft_attempt — free-throw attempt
A free-throw attempt — opens an attempt. A one-point shot taken while the ball is dead. Unlike a field goal, it does not resume
LIVE. A free throw while the clock is LIVE, or with no count awarded, is flagged as an anomaly.made — shot made
made — shot made
Resolves the open attempt and increases the score by the shot’s point value. A resolved free throw also lowers
ftsRemaining by one. Self-describes the shot it resolved — team and shot_type ride the event’s own payload (matching the attempt that opened it), so you don’t need the state snapshot to attribute the row.miss — shot missed
miss — shot missed
Resolves the open attempt with no score change. A resolved free throw still lowers
ftsRemaining by one. It deliberately leaves the clock and possession unchanged — any change of possession comes as a separate possession press. Self-describes the shot it resolved, same as made.score_set — manual score correction
score_set — manual score correction
Overwrites the running score. Scoring continues from the new values. Used to correct a drifted score; rare during normal play.
Free throws
to_the_line — foul awards free throws
to_the_line — foul awards free throws
A foul that awards free throws has been called — a player is heading to the line. Stops the clock (a foul makes the ball dead) and signals that free throws are coming. The count follows in a separate
award_free_throws once it’s known; this event carries no count of its own.No payload fields.award_free_throws — set the free-throw count
award_free_throws — set the free-throw count
The number of free throws, sent once it’s known.
count is the number of shots (1, 2, or 3 in the NBA). For a shooting foul, your client can derive it from the field goal just taken (made → 1, the and-one; missed 2pt → 2; missed 3pt → 3); for a technical or team foul, the scout states it. Sets ftsRemaining.count: 0 is the cancel — it sets ftsRemaining back to 0, calling off free throws that were signalled but never happened. It only resets the countdown; it does not touch the clock.Period and corrections
period — period correction
period — period correction
Sets the period directly to the stated value (overtime is just
period > 4). Use it to fix a period that has drifted, or to label start-of-quarter free throws before they’re shot. Normal quarter changes happen on the period_end → live tip. This clears any pending advance, so the next live does not also increase the period.game_end — game is final
game_end — game is final
Terminal:
clock → FINAL. Consumers should close the WebSocket after receiving this — no further frames will arrive.No payload fields.Server advisory
scout_status — liveness signal
scout_status — liveness signal
Comes from the server, not from a scout. The liveness monitor sends it when the scout’s status changes. See Core Concepts — Advisories.
seq is null on these frames.Meta
undo — retract last press
undo — retract last press
Retracts the most recent scout press. Both the original press and the
undo appear in /history as raw events — a client replaying the log resolves it there: each undo cancels the most recent non-undo event. An undo can lower the score or change possession back — the one case where a scout press reverses earlier state.The scout sends no payload fields; the served payload carries the server-stamped retractsSeq — the seq of the press the undo cancelled (null when nothing remained) — on the live frame and in /history alike, so a display can strike the retracted row without replaying.