> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://statpad.dev/docs/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://statpad.dev/docs/_mcp/server.

# Read a game's change stream

GET https://api.statpad.dev/v1/{league}/games/{game_id}/changes

The same changes the WebSocket stream `game:<id>` carries, after a seq, oldest first.
Poll it with the last seq you applied. A game's stream stays readable for at least 7 days
after the game ends; after that, or when `after_seq` is older than what is retained,
it returns 410 with code `resync_required`, and the client fetches the game again.

Reference: https://statpad.dev/docs/api-reference/changes/list-game-changes

## Authentication

- `Authorization` header (bearer token, required) — An API key, sent as `Authorization: Bearer <key>`.

## Request

### Path parameters

- `league` (string, required) — The league's alias.
- `game_id` (string, required)

### Query parameters

- `after_seq` (long, required) — The last seq the client applied, or 0 for the start.
- `limit` (integer, optional, default: 500) — Page size.

## Response

### 200

The next changes, and where the stream stands.

- `stream` (string, required) — `game:<game id>` carries one game: the game, its live state, game roster, play groups, plays and stats. `league:<alias>` carries the league's shared data: teams, players, venues, seasons and weeks, plus rosters, injuries, transactions and depth charts.
- `data` (list of Change, required)
- `head_seq` (long, required) — The stream's last seq visible to this plan. Fewer changes than `limit` and the last one at `head_seq` means caught up.
- `min_seq` (long, required) — The oldest seq the stream still holds.

## Errors

### 400 Bad Request Error

A parameter is missing or invalid (`bad_request`).

- `type` (string, required) — Identifies the problem type.
- `title` (string, required)
- `status` (integer, required)
- `code` (enum, required) — `bad_request` a malformed request or message. `unauthorized` a missing or invalid API key. `forbidden` the plan doesn't include the league or feature. `not_found`. `rate_limited`. `resync_required` the requested position is older than the retained history, so subscribe again without `resume` for a fresh snapshot (for a game stream, a REST game read with its `seq` also works). `invalid_resume` a resume seq beyond the stream's head. `slow_consumer` the client fell too far behind and was dropped from a stream, so resume from `resume_from`. `too_many_subscriptions`. `unknown_stream`. `internal`. `unavailable`. `quota_exhausted` the account has used its request allowance for the current period.
  - Allowed values: `bad_request`, `unauthorized`, `forbidden`, `not_found`, `rate_limited`, `resync_required`, `invalid_resume`, `slow_consumer`, `too_many_subscriptions`, `unknown_stream`, `internal`, `unavailable`, `quota_exhausted`
- `detail` (string, optional)
- `request_id` (string, optional)
- `min_seq` (long, optional) — With `resync_required`, the oldest seq the stream still holds.
- `resets_at` (datetime, optional) — With `quota_exhausted`, when the next period's allowance starts.

### 401 Unauthorized Error

The API key is missing, unknown or revoked (`unauthorized`).

- `type` (string, required) — Identifies the problem type.
- `title` (string, required)
- `status` (integer, required)
- `code` (enum, required) — `bad_request` a malformed request or message. `unauthorized` a missing or invalid API key. `forbidden` the plan doesn't include the league or feature. `not_found`. `rate_limited`. `resync_required` the requested position is older than the retained history, so subscribe again without `resume` for a fresh snapshot (for a game stream, a REST game read with its `seq` also works). `invalid_resume` a resume seq beyond the stream's head. `slow_consumer` the client fell too far behind and was dropped from a stream, so resume from `resume_from`. `too_many_subscriptions`. `unknown_stream`. `internal`. `unavailable`. `quota_exhausted` the account has used its request allowance for the current period.
  - Allowed values: `bad_request`, `unauthorized`, `forbidden`, `not_found`, `rate_limited`, `resync_required`, `invalid_resume`, `slow_consumer`, `too_many_subscriptions`, `unknown_stream`, `internal`, `unavailable`, `quota_exhausted`
- `detail` (string, optional)
- `request_id` (string, optional)
- `min_seq` (long, optional) — With `resync_required`, the oldest seq the stream still holds.
- `resets_at` (datetime, optional) — With `quota_exhausted`, when the next period's allowance starts.

### 403 Forbidden Error

The plan doesn't include this league or feature (`forbidden`).

- `type` (string, required) — Identifies the problem type.
- `title` (string, required)
- `status` (integer, required)
- `code` (enum, required) — `bad_request` a malformed request or message. `unauthorized` a missing or invalid API key. `forbidden` the plan doesn't include the league or feature. `not_found`. `rate_limited`. `resync_required` the requested position is older than the retained history, so subscribe again without `resume` for a fresh snapshot (for a game stream, a REST game read with its `seq` also works). `invalid_resume` a resume seq beyond the stream's head. `slow_consumer` the client fell too far behind and was dropped from a stream, so resume from `resume_from`. `too_many_subscriptions`. `unknown_stream`. `internal`. `unavailable`. `quota_exhausted` the account has used its request allowance for the current period.
  - Allowed values: `bad_request`, `unauthorized`, `forbidden`, `not_found`, `rate_limited`, `resync_required`, `invalid_resume`, `slow_consumer`, `too_many_subscriptions`, `unknown_stream`, `internal`, `unavailable`, `quota_exhausted`
- `detail` (string, optional)
- `request_id` (string, optional)
- `min_seq` (long, optional) — With `resync_required`, the oldest seq the stream still holds.
- `resets_at` (datetime, optional) — With `quota_exhausted`, when the next period's allowance starts.

### 404 Not Found Error

No such league or entity (`not_found`).

- `type` (string, required) — Identifies the problem type.
- `title` (string, required)
- `status` (integer, required)
- `code` (enum, required) — `bad_request` a malformed request or message. `unauthorized` a missing or invalid API key. `forbidden` the plan doesn't include the league or feature. `not_found`. `rate_limited`. `resync_required` the requested position is older than the retained history, so subscribe again without `resume` for a fresh snapshot (for a game stream, a REST game read with its `seq` also works). `invalid_resume` a resume seq beyond the stream's head. `slow_consumer` the client fell too far behind and was dropped from a stream, so resume from `resume_from`. `too_many_subscriptions`. `unknown_stream`. `internal`. `unavailable`. `quota_exhausted` the account has used its request allowance for the current period.
  - Allowed values: `bad_request`, `unauthorized`, `forbidden`, `not_found`, `rate_limited`, `resync_required`, `invalid_resume`, `slow_consumer`, `too_many_subscriptions`, `unknown_stream`, `internal`, `unavailable`, `quota_exhausted`
- `detail` (string, optional)
- `request_id` (string, optional)
- `min_seq` (long, optional) — With `resync_required`, the oldest seq the stream still holds.
- `resets_at` (datetime, optional) — With `quota_exhausted`, when the next period's allowance starts.

### 410 Gone Error

The requested position is older than the stream's retained history (`resync_required`). Fetch the game again and continue from the seq it returns.

- `type` (string, required) — Identifies the problem type.
- `title` (string, required)
- `status` (integer, required)
- `code` (enum, required) — `bad_request` a malformed request or message. `unauthorized` a missing or invalid API key. `forbidden` the plan doesn't include the league or feature. `not_found`. `rate_limited`. `resync_required` the requested position is older than the retained history, so subscribe again without `resume` for a fresh snapshot (for a game stream, a REST game read with its `seq` also works). `invalid_resume` a resume seq beyond the stream's head. `slow_consumer` the client fell too far behind and was dropped from a stream, so resume from `resume_from`. `too_many_subscriptions`. `unknown_stream`. `internal`. `unavailable`. `quota_exhausted` the account has used its request allowance for the current period.
  - Allowed values: `bad_request`, `unauthorized`, `forbidden`, `not_found`, `rate_limited`, `resync_required`, `invalid_resume`, `slow_consumer`, `too_many_subscriptions`, `unknown_stream`, `internal`, `unavailable`, `quota_exhausted`
- `detail` (string, optional)
- `request_id` (string, optional)
- `min_seq` (long, optional) — With `resync_required`, the oldest seq the stream still holds.
- `resets_at` (datetime, optional) — With `quota_exhausted`, when the next period's allowance starts.

### 429 Too Many Requests Error

The account's request rate (`rate_limited`) or period allowance (`quota_exhausted`, with `resets_at`) is used up. No data is served.

- `type` (string, required) — Identifies the problem type.
- `title` (string, required)
- `status` (integer, required)
- `code` (enum, required) — `bad_request` a malformed request or message. `unauthorized` a missing or invalid API key. `forbidden` the plan doesn't include the league or feature. `not_found`. `rate_limited`. `resync_required` the requested position is older than the retained history, so subscribe again without `resume` for a fresh snapshot (for a game stream, a REST game read with its `seq` also works). `invalid_resume` a resume seq beyond the stream's head. `slow_consumer` the client fell too far behind and was dropped from a stream, so resume from `resume_from`. `too_many_subscriptions`. `unknown_stream`. `internal`. `unavailable`. `quota_exhausted` the account has used its request allowance for the current period.
  - Allowed values: `bad_request`, `unauthorized`, `forbidden`, `not_found`, `rate_limited`, `resync_required`, `invalid_resume`, `slow_consumer`, `too_many_subscriptions`, `unknown_stream`, `internal`, `unavailable`, `quota_exhausted`
- `detail` (string, optional)
- `request_id` (string, optional)
- `min_seq` (long, optional) — With `resync_required`, the oldest seq the stream still holds.
- `resets_at` (datetime, optional) — With `quota_exhausted`, when the next period's allowance starts.

### 500 Internal Server Error

Something failed on our side (`internal`). Retry with backoff.

- `type` (string, required) — Identifies the problem type.
- `title` (string, required)
- `status` (integer, required)
- `code` (enum, required) — `bad_request` a malformed request or message. `unauthorized` a missing or invalid API key. `forbidden` the plan doesn't include the league or feature. `not_found`. `rate_limited`. `resync_required` the requested position is older than the retained history, so subscribe again without `resume` for a fresh snapshot (for a game stream, a REST game read with its `seq` also works). `invalid_resume` a resume seq beyond the stream's head. `slow_consumer` the client fell too far behind and was dropped from a stream, so resume from `resume_from`. `too_many_subscriptions`. `unknown_stream`. `internal`. `unavailable`. `quota_exhausted` the account has used its request allowance for the current period.
  - Allowed values: `bad_request`, `unauthorized`, `forbidden`, `not_found`, `rate_limited`, `resync_required`, `invalid_resume`, `slow_consumer`, `too_many_subscriptions`, `unknown_stream`, `internal`, `unavailable`, `quota_exhausted`
- `detail` (string, optional)
- `request_id` (string, optional)
- `min_seq` (long, optional) — With `resync_required`, the oldest seq the stream still holds.
- `resets_at` (datetime, optional) — With `quota_exhausted`, when the next period's allowance starts.

### 503 Service Unavailable Error

Account access or its limits could not be checked because a dependency is unavailable (`unavailable`). No data is served.

- `type` (string, required) — Identifies the problem type.
- `title` (string, required)
- `status` (integer, required)
- `code` (enum, required) — `bad_request` a malformed request or message. `unauthorized` a missing or invalid API key. `forbidden` the plan doesn't include the league or feature. `not_found`. `rate_limited`. `resync_required` the requested position is older than the retained history, so subscribe again without `resume` for a fresh snapshot (for a game stream, a REST game read with its `seq` also works). `invalid_resume` a resume seq beyond the stream's head. `slow_consumer` the client fell too far behind and was dropped from a stream, so resume from `resume_from`. `too_many_subscriptions`. `unknown_stream`. `internal`. `unavailable`. `quota_exhausted` the account has used its request allowance for the current period.
  - Allowed values: `bad_request`, `unauthorized`, `forbidden`, `not_found`, `rate_limited`, `resync_required`, `invalid_resume`, `slow_consumer`, `too_many_subscriptions`, `unknown_stream`, `internal`, `unavailable`, `quota_exhausted`
- `detail` (string, optional)
- `request_id` (string, optional)
- `min_seq` (long, optional) — With `resync_required`, the oldest seq the stream still holds.
- `resets_at` (datetime, optional) — With `quota_exhausted`, when the next period's allowance starts.

## Types

### Change

One entry of a stream's journal. `seq` is gapless and in commit order within the stream, so a client that has applied seq N has applied everything before it.

- `seq` (long, required) — A position in a stream. 0 is before the first change.
- `at` (datetime, required) — When the change was recorded.
- `op` (enum, required) — `upsert` carries the entity's full current state, so replace what you hold by id. `delete` removes it.
  - Allowed values: `upsert`, `delete`
- `entity_type` (enum, required) — The kind of entity a change carries, which fixes the payload schema: `game` Game, `live_state` LiveState, `team` Team, `venue` Venue, `season` Season, `season_week` SeasonWeek, `player` Player, `game_roster` GameRosterEntry, `play_group` PlayGroup, `play` Play, `player_game_stats` PlayerGameStats, `team_game_stats` TeamGameStats, `injury` Injury, `transaction` Transaction, `roster` RosterEntry and `depth_chart` DepthChart. A deletion's payload is a Tombstone.
  - Allowed values: `game`, `live_state`, `team`, `venue`, `season`, `season_week`, `player`, `game_roster`, `play_group`, `play`, `player_game_stats`, `team_game_stats`, `injury`, `transaction`, `roster`, `depth_chart`
- `entity_id` (string, required) — The entity's id. For `game_roster` and `player_game_stats` it is the player's id.
- `version` (integer, required) — Bumps on every change to the entity, deletions included. A client holding a version can ignore any state with a lower one.
- `payload_version` (integer, required) — The payload schema version. This document describes version 1.
- `payload` (EntityPayload, required) — The entity's DTO, chosen by `entity_type`, or a Tombstone when `op` is `delete`.

### EntityPayload

The entity's DTO, chosen by `entity_type`, or a Tombstone when `op` is `delete`.

- Game: Game
- LiveState: LiveState
- Team: Team
- Venue: Venue
- Season: Season
- SeasonWeek: SeasonWeek
- Player: Player
- GameRosterEntry: GameRosterEntry
- PlayGroup: PlayGroup
- Play: Play
- PlayerGameStats: PlayerGameStats
- TeamGameStats: TeamGameStats
- Injury: Injury
- Transaction: Transaction
- RosterEntry: RosterEntry
- DepthChart: DepthChart
- Tombstone: Tombstone

### Game

- `id` (string, required)
- `league` (string, required) — A league's public alias, such as `nfl` or `ncaaf`.
- `season_id` (string, required)
- `week_id` (string, required, nullable)
- `title` (string, required, nullable) — A named game's title, such as a bowl.
- `game_type` (enum, required)
  - Allowed values: `regular`, `playoff`, `preseason`, `exhibition`, `allstar`
- `status` (enum, required) — `scheduled`, then `created` (data available), `inprogress` (with `halftime` and `delayed` along the way), `complete` (final, stats may still be corrected) and `closed` (validated). Also `time_tbd`, `suspended`, `postponed` and `cancelled`. Depending on the league, a postponed game either goes back to `scheduled` at its new time under the same id, or stays `postponed` while its makeup is a new game.
  - Allowed values: `scheduled`, `time_tbd`, `created`, `inprogress`, `halftime`, `delayed`, `suspended`, `complete`, `closed`, `postponed`, `cancelled`
- `coverage` (enum, required) — How much the feed covers. `full` has play-by-play; `score_only` has the score alone.
  - Allowed values: `full`, `basic`, `score_only`
- `scheduled_at` (datetime, required) — When the game starts. For a `time_tbd` game it is the start of the game's US Eastern day, not a start time, and a game whose date isn't fixed yet, such as an NFL flex game, can still move to another day.
- `venue_id` (string, required, nullable)
- `neutral_site` (boolean, required)
- `home_team_id` (string, required)
- `away_team_id` (string, required)
- `home_score` (integer, required, nullable)
- `away_score` (integer, required, nullable)
- `attendance` (integer, required, nullable)
- `started_at` (datetime, required, nullable) — When the first play happened, from the play-by-play's own wall clock, counting only plays that carry one. Null until such a play arrives.
- `ended_at` (datetime, required, nullable) — When the last play happened, from the play-by-play's own wall clock, counting only plays that carry one, once the game is over. Null until then, or when no play carries one.
- `rescheduled_from_id` (string, required, nullable) — For a makeup game, the postponed game it replaces, when known.
- `version` (integer, required) — Bumps on every change to the entity, deletions included. A client holding a version can ignore any state with a lower one.
- `updated_at` (datetime, required)

### LiveState

The live situation of a game, keyed by `game_id`. Sport-specific context is in the block named after the league's sport; the others are null.

- `game_id` (string, required)
- `period` (integer, required, nullable)
- `period_half` (enum, required, nullable) — Top or bottom of an inning. Other sports have no halves, so the field is null.
  - Allowed values: `top`, `bottom`
- `clock_seconds` (integer, required, nullable)
- `clock_running` (boolean, required, nullable) — Whether the game clock is running, or null when the feed doesn't say. Soccer sets it: true while a half or a period of extra time is being played. Football and basketball feeds have no running flag, so it is null there; use clock_seconds, which updates as plays post.
- `possession_team_id` (string, required, nullable)
- `home_score` (integer, required)
- `away_score` (integer, required)
- `last_play_id` (string, required, nullable) — The game's newest play by sequence, of any type, so a timeout or the end of a period counts. Null before the first play.
- `football` (FootballSituation, required, nullable)
- `soccer` (SoccerSituation, required, nullable) — The live soccer context. Periods 1 and 2 are the halves, 3 and 4 the halves of extra time and 5 the penalty shoot-out. `minute` and `added_minute` are the clock as shown, so 90'+6' is minute 90 and added minute 6.
- `basketball` (BasketballSituation, required, nullable) — The live basketball context. NBA periods 1-4 are quarters and NCAA periods 1-2 halves; every later period is a 5-minute overtime. Each count is null when the provider doesn't report it.
- `version` (integer, required) — Bumps on every change to the entity, deletions included. A client holding a version can ignore any state with a lower one.
- `updated_at` (datetime, required)

### Team

- `id` (string, required)
- `league` (string, required) — A league's public alias, such as `nfl` or `ncaaf`.
- `market` (string, required)
- `name` (string, required)
- `alias` (string, required) — Abbreviation. Two teams in a league can share one, so identify teams by id.
- `venue_id` (string, required, nullable)
- `active` (boolean, required)
- `version` (integer, required) — Bumps on every change to the entity, deletions included. A client holding a version can ignore any state with a lower one.
- `updated_at` (datetime, required)

### Venue

- `id` (string, required)
- `name` (string, required)
- `city` (string, required, nullable)
- `state` (string, required, nullable)
- `country` (string, required, nullable)
- `timezone` (string, required, nullable) — IANA time zone name.
- `capacity` (integer, required, nullable)
- `surface` (string, required, nullable)
- `roof_type` (enum, required, nullable)
  - Allowed values: `outdoor`, `dome`, `retractable`
- `version` (integer, required) — Bumps on every change to the entity, deletions included. A client holding a version can ignore any state with a lower one.

### Season

A league year and phase. `year` is the year the season starts.

- `id` (string, required)
- `league` (string, required) — A league's public alias, such as `nfl` or `ncaaf`.
- `year` (integer, required)
- `type` (enum, required) — Preseason, regular season, postseason, play-in tournament or all-star.
  - Allowed values: `PRE`, `REG`, `PST`, `PIT`, `AST`
- `name` (string, required)
- `starts_on` (date, required, nullable)
- `ends_on` (date, required, nullable)

### SeasonWeek

- `id` (string, required)
- `season_id` (string, required)
- `sequence` (integer, required)
- `title` (string, required)
- `starts_on` (date, required, nullable)
- `ends_on` (date, required, nullable)

### Player

A person. One id follows a player across leagues, from college to the pros.

- `id` (string, required)
- `first_name` (string, required)
- `last_name` (string, required)
- `full_name` (string, required)
- `abbr_name` (string, required, nullable)
- `birth_date` (date, required, nullable)
- `birth_place` (string, required, nullable)
- `height_in` (integer, required, nullable)
- `weight_lb` (integer, required, nullable)
- `bats` (enum, required, nullable) — Left, right or switch.
  - Allowed values: `L`, `R`, `S`
- `throws` (enum, required, nullable) — Left, right or switch.
  - Allowed values: `L`, `R`, `S`
- `primary_position` (string, required, nullable)
- `college` (string, required, nullable)
- `rookie_year` (integer, required, nullable)
- `draft` (Draft, required, nullable) — What is known of the player's draft. Present when any of its fields is known.
- `version` (integer, required) — Bumps on every change to the entity, deletions included. A client holding a version can ignore any state with a lower one.
- `updated_at` (datetime, required)

### GameRosterEntry

A player dressed for one game, with the team, jersey and position for that game.

- `game_id` (string, required)
- `player_id` (string, required)
- `team_id` (string, required)
- `status` (enum, required)
  - Allowed values: `active`, `inactive`, `did_not_play`, `ejected`
- `starter` (boolean, required)
- `position` (string, required, nullable)
- `jersey` (string, required, nullable)
- `version` (integer, required) — Bumps on every change to the entity, deletions included. A client holding a version can ignore any state with a lower one.

### PlayGroup

A drive (football), an at-bat (baseball) or a possession. Ordered by `sequence`, which a provider can renumber, so identify groups by id.

- `id` (string, required)
- `game_id` (string, required)
- `kind` (enum, required)
  - Allowed values: `drive`, `at_bat`, `possession`
- `sequence` (integer, required)
- `period` (integer, required)
- `period_half` (enum, required, nullable) — Top or bottom of an inning. Other sports have no halves, so the field is null.
  - Allowed values: `top`, `bottom`
- `team_id` (string, required, nullable) — The team with the ball or at bat.
- `result` (string, required, nullable)
- `football` (FootballDrive, required, nullable)
- `version` (integer, required) — Bumps on every change to the entity, deletions included. A client holding a version can ignore any state with a lower one.
- `updated_at` (datetime, required)

### Play

One event in a game. `sequence` is the provider's total order within the game; two plays can share it, so order by (`sequence`, `id`). Corrections bump `version`.

- `id` (string, required)
- `game_id` (string, required)
- `group_id` (string, required, nullable)
- `sequence` (long, required)
- `period` (integer, required)
- `period_half` (enum, required, nullable) — Top or bottom of an inning. Other sports have no halves, so the field is null.
  - Allowed values: `top`, `bottom`
- `clock_seconds` (integer, required, nullable)
- `wall_clock` (datetime, required, nullable)
- `type` (string, required) — The play's type in its sport's vocabulary. Football uses the FootballPlayType values, soccer the SoccerPlayType values and basketball the BasketballPlayType values.
- `description` (string, required, nullable)
- `team_id` (string, required, nullable) — The team with the ball or at bat; in soccer and basketball, the team credited with the event, which for a shot is the shooting team.
- `is_scoring` (boolean, required)
- `points` (integer, required, nullable)
- `home_score` (integer, required, nullable) — Score after the play.
- `away_score` (integer, required, nullable) — Score after the play.
- `is_official` (boolean, required)
- `participants` (list of Participant, required)
- `football` (FootballPlay, required, nullable)
- `soccer` (SoccerPlay, required, nullable) — A soccer event. Positions run 0-100 from the acting team's view: x from its own goal line to the one it attacks, y across the pitch. A shoot-out kick has `is_shootout` set, is in period 5 and never counts toward the score.
- `basketball` (BasketballPlay, required, nullable) — A basketball event. A shot is worth `points_attempted` (1 for a free throw, 2 or 3 for a field goal, 0 for anything else) and `is_made` says whether it went in. Court positions are in feet: x across the court from 0 to 50, y along it from the basket attacked, negative behind the basket.
- `version` (integer, required) — Bumps on every change to the entity, deletions included. A client holding a version can ignore any state with a lower one.
- `updated_at` (datetime, required)

### PlayerGameStats

One player's stats in one game, every category together.

- `game_id` (string, required)
- `player_id` (string, required)
- `team_id` (string, required)
- `stats` (map from string to StatLine, required) — Stat lines keyed by category (`passing`, `rushing`, `defense`).
- `version` (integer, required) — Bumps on every change to the entity, deletions included. A client holding a version can ignore any state with a lower one.

### TeamGameStats

- `game_id` (string, required)
- `team_id` (string, required)
- `stats` (map from string to StatLine, required) — Stat lines keyed by category (`passing`, `rushing`, `defense`).
- `version` (integer, required) — Bumps on every change to the entity, deletions included. A client holding a version can ignore any state with a lower one.

### Injury

One injury episode with its latest report. Reports are kept as history; this is the current one.

- `id` (string, required)
- `league` (string, required) — A league's public alias, such as `nfl` or `ncaaf`.
- `player_id` (string, required)
- `team_id` (string, required, nullable)
- `body_part` (string, required, nullable)
- `detail` (string, required, nullable)
- `side` (enum, required, nullable)
  - Allowed values: `left`, `right`, `both`
- `started_on` (date, required, nullable)
- `resolved_on` (date, required, nullable)
- `estimated_return` (date, required, nullable)
- `status` (enum, required, nullable) — Game designation from the latest report.
  - Allowed values: `probable`, `questionable`, `doubtful`, `out`, `day_to_day`, `injured_reserve`, `il_7`, `il_10`, `il_15`, `il_60`, `season_ending`
- `practice_status` (enum, required, nullable)
  - Allowed values: `full`, `limited`, `did_not_participate`, `not_listed`
- `reported_at` (datetime, required, nullable)
- `comment` (string, required, nullable)

### Transaction

- `id` (string, required)
- `league` (string, required) — A league's public alias, such as `nfl` or `ncaaf`.
- `player_id` (string, required, nullable)
- `from_team_id` (string, required, nullable)
- `to_team_id` (string, required, nullable)
- `type` (string, required)
- `description` (string, required)
- `effective_on` (date, required)
- `announced_at` (datetime, required)

### RosterEntry

A stint of a player on a team. `ended_on` is the last day, null while current.

- `id` (string, required)
- `league` (string, required) — A league's public alias, such as `nfl` or `ncaaf`.
- `player_id` (string, required)
- `team_id` (string, required)
- `status` (enum, required)
  - Allowed values: `active`, `injured_reserve`, `practice_squad`, `suspended`, `reserve`, `pup`, `nfi`, `injured_list`, `minors`, `restricted`, `redshirt`
- `position` (string, required, nullable)
- `jersey` (string, required, nullable)
- `started_on` (date, required)
- `ended_on` (date, required, nullable)

### DepthChart

A team's depth chart as of `effective_at`. Each one replaces the previous chart whole.

- `id` (string, required)
- `team_id` (string, required)
- `season_id` (string, required)
- `week_id` (string, required, nullable)
- `effective_at` (datetime, required)
- `entries` (list of DepthChartEntry, required)

### Tombstone

The payload of a deletion.

- `id` (string, required)
- `version` (integer, required) — Bumps on every change to the entity, deletions included. A client holding a version can ignore any state with a lower one.
- `deleted_at` (datetime, required)

### FootballSituation

- `down` (integer, required, nullable)
- `yards_to_go` (integer, required, nullable)
- `yardline` (integer, required, nullable) — Line of scrimmage, 0-100 from the possessing team's own goal line.
- `home_timeouts` (integer, required, nullable) — Timeouts the home team has left in the half, or null when the feed doesn't report them, as after the final.
- `away_timeouts` (integer, required, nullable) — Timeouts the away team has left in the half, or null when the feed doesn't report them, as after the final.

### SoccerSituation

The live soccer context. Periods 1 and 2 are the halves, 3 and 4 the halves of extra time and 5 the penalty shoot-out. `minute` and `added_minute` are the clock as shown, so 90'+6' is minute 90 and added minute 6.

- `minute` (integer, required, nullable)
- `added_minute` (integer, required, nullable)
- `added_time_announced` (integer, required, nullable) — Minutes of stoppage time the fourth official announced for the current period.
- `home_formation` (string, required, nullable)
- `away_formation` (string, required, nullable)
- `home_red_cards` (integer, required)
- `away_red_cards` (integer, required)
- `home_shootout` (integer, required, nullable) — Penalties scored in a shoot-out.
- `away_shootout` (integer, required, nullable) — Penalties scored in a shoot-out.

### BasketballSituation

The live basketball context. NBA periods 1-4 are quarters and NCAA periods 1-2 halves; every later period is a 5-minute overtime. Each count is null when the provider doesn't report it.

- `home_timeouts` (integer, required, nullable) — Timeouts the home team has left.
- `away_timeouts` (integer, required, nullable) — Timeouts the away team has left.
- `home_fouls` (integer, required, nullable) — The home team's fouls in the current period.
- `away_fouls` (integer, required, nullable) — The away team's fouls in the current period.

### Draft

What is known of the player's draft. Present when any of its fields is known.

- `year` (integer, required, nullable)
- `round` (integer, required, nullable)
- `pick` (integer, required, nullable)
- `team_id` (string, required, nullable)

### FootballDrive

- `start_clock` (integer, required, nullable)
- `end_clock` (integer, required, nullable)
- `start_yardline` (integer, required, nullable)
- `end_yardline` (integer, required, nullable)
- `play_count` (integer, required, nullable)
- `yards` (integer, required, nullable)
- `first_downs` (integer, required, nullable)
- `duration_seconds` (integer, required, nullable)
- `points` (integer, required, nullable)

### Participant

- `player_id` (string, required)
- `team_id` (string, required)
- `role` (string, required)

### FootballPlay

- `play_type` (enum, required)
  - Allowed values: `pass`, `rush`, `punt`, `kickoff`, `field_goal`, `extra_point`, `two_point`, `penalty`, `kneel`, `spike`, `timeout`, `other`
- `down` (integer, required, nullable)
- `yards_to_go` (integer, required, nullable)
- `start_yardline` (integer, required, nullable)
- `end_yardline` (integer, required, nullable)
- `yards_gained` (integer, required, nullable)
- `is_first_down` (boolean, required)
- `is_touchdown` (boolean, required)
- `is_turnover` (boolean, required)
- `is_penalty` (boolean, required)
- `play_action` (boolean, required, nullable)
- `screen_pass` (boolean, required, nullable)
- `run_pass_option` (boolean, required, nullable)
- `air_yards` (integer, required, nullable)
- `penalty_yards` (integer, required, nullable) — Net yards penalties moved the ball, for (+) or against (-) the offense, so that start_yardline + yards_gained + penalty_yards = end_yardline. Null without a penalty.

### SoccerPlay

A soccer event. Positions run 0-100 from the acting team's view: x from its own goal line to the one it attacks, y across the pitch. A shoot-out kick has `is_shootout` set, is in period 5 and never counts toward the score.

- `play_type` (enum, required)
  - Allowed values: `goal`, `own_goal`, `penalty_goal`, `penalty_missed`, `penalty_saved`, `shot_on_target`, `shot_off_target`, `shot_blocked`, `shot_woodwork`, `foul`, `handball`, `offside`, `corner`, `yellow_card`, `red_card`, `substitution`, `var_decision`, `goal_disallowed`, `delay_start`, `delay_end`, `period_start`, `period_end`, `other`
- `minute` (integer, required, nullable) — The match minute shown for the event.
- `added_minute` (integer, required, nullable) — The stoppage minute after it.
- `is_shootout` (boolean, required)
- `body_part` (enum, required, nullable)
  - Allowed values: `left_foot`, `right_foot`, `head`, `other`
- `field_x` (float, required, nullable) — Where the event happened.
- `field_y` (float, required, nullable)
- `end_x` (float, required, nullable) — Where a shot ended.
- `end_y` (float, required, nullable)

### BasketballPlay

A basketball event. A shot is worth `points_attempted` (1 for a free throw, 2 or 3 for a field goal, 0 for anything else) and `is_made` says whether it went in. Court positions are in feet: x across the court from 0 to 50, y along it from the basket attacked, negative behind the basket.

- `play_type` (enum, required)
  - Allowed values: `field_goal`, `free_throw`, `offensive_rebound`, `defensive_rebound`, `rebound`, `turnover`, `steal`, `block`, `foul`, `technical_foul`, `flagrant_foul`, `violation`, `timeout`, `substitution`, `jump_ball`, `review`, `ejection`, `period_start`, `period_end`, `game_end`, `other`
- `shot_type` (enum, required, nullable)
  - Allowed values: `jump_shot`, `layup`, `dunk`, `hook_shot`, `tip_shot`
- `points_attempted` (integer, required)
- `is_made` (boolean, required)
- `free_throw` (integer, required, nullable) — A free throw's place in its trip to the line, 2 in 2 of 2.
- `free_throws` (integer, required, nullable) — Free throws in the trip.
- `court_x` (integer, required, nullable)
- `court_y` (integer, required, nullable)

### StatLine

- double
- string

### DepthChartEntry

- `unit` (enum, required)
  - Allowed values: `offense`, `defense`, `special_teams`, `lineup`, `rotation`, `bullpen`
- `position` (string, required)
- `depth` (integer, required) — 1 is the starter.
- `player_id` (string, required)

## Examples

**Response**

```json
{
  "stream": "game:01a1b987-64e7-74e3-8db1-b939d352190d",
  "data": [
    {
      "seq": 281,
      "at": "2026-11-08T05:00:20.681Z",
      "op": "upsert",
      "entity_type": "play",
      "entity_id": "01a1b987-6544-7ede-a02b-c65c5c1ef848",
      "version": 1,
      "payload_version": 1,
      "payload": {
        "away_score": 43,
        "basketball": null,
        "clock_seconds": 808,
        "description": "(13:32) Shotgun #22 J.Goff rush middle for 0 yards to the CSU01 (#5 D.Hagans)",
        "football": {
          "air_yards": null,
          "down": 2,
          "end_yardline": 99,
          "is_first_down": false,
          "is_penalty": false,
          "is_touchdown": false,
          "is_turnover": false,
          "penalty_yards": null,
          "play_action": null,
          "play_type": "rush",
          "run_pass_option": null,
          "screen_pass": null,
          "start_yardline": 99,
          "yards_gained": 0,
          "yards_to_go": 1
        },
        "game_id": "01a1b987-64e7-74e3-8db1-b939d352190d",
        "group_id": "01a1b987-6502-79b3-a1f4-392748c82d5b",
        "home_score": 24,
        "id": "01a1b987-6544-7ede-a02b-c65c5c1ef848",
        "is_official": true,
        "is_scoring": false,
        "participants": [],
        "period": 4,
        "period_half": null,
        "points": 0,
        "sequence": 131000,
        "soccer": null,
        "team_id": "01a1b987-64e7-7d57-bdf8-52aecf5e4216",
        "type": "rush",
        "updated_at": "2026-11-08T05:00:20.583197Z",
        "version": 1,
        "wall_clock": "2026-11-08T01:39:39Z"
      }
    },
    {
      "seq": 282,
      "at": "2026-11-08T05:00:20.681Z",
      "op": "upsert",
      "entity_type": "play",
      "entity_id": "01a1b987-6545-7914-9c9a-45d880fcf0d7",
      "version": 1,
      "payload_version": 1,
      "payload": {
        "away_score": 43,
        "basketball": null,
        "clock_seconds": 778,
        "description": "Timeout Boise State, clock 12:58",
        "football": {
          "air_yards": null,
          "down": null,
          "end_yardline": 99,
          "is_first_down": false,
          "is_penalty": false,
          "is_touchdown": false,
          "is_turnover": false,
          "penalty_yards": null,
          "play_action": null,
          "play_type": "timeout",
          "run_pass_option": null,
          "screen_pass": null,
          "start_yardline": 99,
          "yards_gained": 0,
          "yards_to_go": null
        },
        "game_id": "01a1b987-64e7-74e3-8db1-b939d352190d",
        "group_id": "01a1b987-6502-79b3-a1f4-392748c82d5b",
        "home_score": 24,
        "id": "01a1b987-6545-7914-9c9a-45d880fcf0d7",
        "is_official": true,
        "is_scoring": false,
        "participants": [],
        "period": 4,
        "period_half": null,
        "points": 0,
        "sequence": 131001,
        "soccer": null,
        "team_id": "01a1b987-64e7-7d57-bdf8-52aecf5e4216",
        "type": "timeout",
        "updated_at": "2026-11-08T05:00:20.583197Z",
        "version": 1,
        "wall_clock": "2026-11-08T01:40:30Z"
      }
    },
    {
      "seq": 283,
      "at": "2026-11-08T05:00:20.681Z",
      "op": "upsert",
      "entity_type": "live_state",
      "entity_id": "01a1b987-64e7-74e3-8db1-b939d352190d",
      "version": 1,
      "payload_version": 1,
      "payload": {
        "away_score": 43,
        "basketball": null,
        "clock_running": null,
        "clock_seconds": 778,
        "football": {
          "away_timeouts": 0,
          "down": 3,
          "home_timeouts": 2,
          "yardline": 99,
          "yards_to_go": 1
        },
        "game_id": "01a1b987-64e7-74e3-8db1-b939d352190d",
        "home_score": 24,
        "last_play_id": null,
        "period": 4,
        "period_half": null,
        "possession_team_id": "01a1b987-64e7-7d57-bdf8-52aecf5e4216",
        "soccer": null,
        "updated_at": "2026-11-08T05:00:20.583197Z",
        "version": 1
      }
    }
  ],
  "head_seq": 283,
  "min_seq": 1
}
```

**SDK Code**

```python The last plays and live state of Boise State at Colorado State
import requests

url = "https://api.statpad.dev/v1/nfl/games/game_id/changes"

querystring = {"after_seq":"1"}

headers = {"Authorization": "Bearer <token>"}

response = requests.get(url, headers=headers, params=querystring)

print(response.json())
```

```javascript The last plays and live state of Boise State at Colorado State
const url = 'https://api.statpad.dev/v1/nfl/games/game_id/changes?after_seq=1';
const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go The last plays and live state of Boise State at Colorado State
package main

import (
	"fmt"
	"net/http"
	"io"
)

func main() {

	url := "https://api.statpad.dev/v1/nfl/games/game_id/changes?after_seq=1"

	req, _ := http.NewRequest("GET", url, nil)

	req.Header.Add("Authorization", "Bearer <token>")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby The last plays and live state of Boise State at Colorado State
require 'uri'
require 'net/http'

url = URI("https://api.statpad.dev/v1/nfl/games/game_id/changes?after_seq=1")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'

response = http.request(request)
puts response.read_body
```

```java The last plays and live state of Boise State at Colorado State
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.get("https://api.statpad.dev/v1/nfl/games/game_id/changes?after_seq=1")
  .header("Authorization", "Bearer <token>")
  .asString();
```

```php The last plays and live state of Boise State at Colorado State
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('GET', 'https://api.statpad.dev/v1/nfl/games/game_id/changes?after_seq=1', [
  'headers' => [
    'Authorization' => 'Bearer <token>',
  ],
]);

echo $response->getBody();
```

```csharp The last plays and live state of Boise State at Colorado State
using RestSharp;

var client = new RestClient("https://api.statpad.dev/v1/nfl/games/game_id/changes?after_seq=1");
var request = new RestRequest(Method.GET);
request.AddHeader("Authorization", "Bearer <token>");
IRestResponse response = client.Execute(request);
```

```swift The last plays and live state of Boise State at Colorado State
import Foundation

let headers = ["Authorization": "Bearer <token>"]

let request = NSMutableURLRequest(url: NSURL(string: "https://api.statpad.dev/v1/nfl/games/game_id/changes?after_seq=1")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```