Skip to main content
The basketball feed covers live NBA and WNBA games — both leagues speak the same event vocabulary and state shape; the catalog row’s 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 full GameState 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. A made 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 on GameState. 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 as frame.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

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).
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.
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_lineaward_free_throws for a foul). Leaves the period unchanged; play resumes on the next live.No payload fields.
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.
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

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.
A live three-point field goal is in the air — opens an attempt. Behaves exactly like 2pt_attempt; only the point value differs.
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.
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.
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.
Overwrites the running score. Scoring continues from the new values. Used to correct a drifted score; rare during normal play.

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.
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

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_endlive tip. This clears any pending advance, so the next live does not also increase the period.
Terminal: clock → FINAL. Consumers should close the WebSocket after receiving this — no further frames will arrive.No payload fields.

Server advisory

Comes from the server, not from a scout. The liveness monitor sends it when the scout’s status changes. seq is null on these frames.
See Core Concepts — Advisories.

Meta

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.