closing-lines
One row per (game, operator, market, selection, side): where the side opened, where it closed, how much it moved, and book-only CLV. Built from change-only odds records observed from scrape start through game end. One row per priced selection id per side — ≈ 12,000 rows for an MLB game with full props coverage — the compact, analysis-ready summary of a game’s odds; reach for sportsbook-lines when you need every intermediate move.
Fetching
Section titled “Fetching”curl -s "https://api.betflux.ai/v1/games/NBA_GSW_MIA_20260401/closing-lines" \ -H "Authorization: Bearer $BETFLUX_API_KEY" -o closing.parquetOne Parquet file per game. Date-range queries are a client-side loop over
/v1/games — the CLI and SDK do it
for you, with local filters (operator, market_type, team, player_id,
side):
betflux get closing-lines --league NBA \ --date-from 2026-04-01 --date-to 2026-04-07 --operator FANDUELSemantics
Section titled “Semantics”- One row per selection id per side:
market_key(=OPERATOR:market_external_id) andselection_external_ididentify the market and selection by the operator’s own ids, so two markets or two players that share a name never share a row — see Market identity. - Books that mint a new selection id per line value (DraftKings, BetMGM,
Pinnacle, FanDuel alternates) show a moved line as a new row whose opening
equals its closing. To get DraftKings’ main-line close for a side, take the
row with the latest
closing_tsamong the market’s rows for that side — a heuristic until a line-stable key ships. FanDuel main markets keep one id, so that row’s opening → closing is the whole pre-game move. - Opening = first observed record for the side. Closing = the latest
eligible quote strictly before
game_start: both the market and selection must be explicitly active, prices must be valid, and a recognized market must have its required selection values and compatible sides. Its observed market meaning must also agree with the closing classification; an earlier points quote cannot become a rebounds quote or another player’s quote through relabeling. Missing or unknown status does not establish an active quote. - A closing quote’s price, applicable spread/total, and timestamp come from the same observation. A later suspended or incomplete observation does not replace it, and an old threshold is never combined with a newer price.
- If no eligible pre-game quote exists, all closing columns are null, including for live-only or all-suspended histories. The row, opening observation, and history statistics remain present. Unclassified and excluded markets can retain active priced quotes, but their unknown dimensions are not inferred.
- Odds come in three encodings per snapshot: American (
*_odds), decimal (*_decimal), and implied probability (*_implied, vig included). line_move_countcounts odds changes between consecutive records;min_odds/max_odds/distinct_odds_countspan all records including live ones.clv_odds/clv_impliedcompare the side’s own open to its own close. They are null if either price is unavailable or the opening observation’s market meaning or selection identity is incompatible with the close — see Core concepts.scopeis reserved and always null.
Columns
Section titled “Columns”| Column | Type | Nullable | Description |
|---|---|---|---|
game_id | string | no | Public game id: LEAGUE_AWAY_HOME_YYYYMMDD (ET date; `_2` suffix for the second game of a doubleheader). |
league | string | no | League code, e.g. NBA, NFL, MLB, NHL, NCAAM. |
game_date | string | no | Calendar date of the scheduled start in US Eastern time (YYYY-MM-DD). |
game_start | string | no | Scheduled start time, ISO-8601 UTC. |
home_team | string | no | Canonical home team name. |
away_team | string | no | Canonical away team name. |
operator | string | no | Sportsbook operator code, e.g. FANDUEL, DRAFTKINGS, PINNACLE. |
market_type | string | no | Market classification, e.g. SPREAD, MONEYLINE, TOTAL, PLAYER_METRIC_OVER_UNDER. A classification, not an identity: a book can offer several markets of one type (main + alternate lines, one per player). |
period | string | no | Game segment the market covers, e.g. the full game, a half, or a quarter. |
scope | string | yes | Reserved for future market scoping; always null. |
market_key | string | no | Global market identity: `OPERATOR:market_external_id` — the operator code plus the operator's own market id. Join key across closing-lines, market-results and sportsbook-lines. |
selection_name | string | yes | Operator's label for the selection (e.g. a player for prop markets, `Over`); display only — shared across selections, never an identity. Null for plain team markets. |
side | string | no | Priced side within the selection, e.g. HOME, AWAY, OVER, UNDER. |
opening_odds | integer | yes | American odds of the first observed record for this side. |
opening_decimal | number | yes | Decimal odds at open. |
opening_implied | number | yes | Implied probability at open (vig included, 0-1). |
opening_spread | number | yes | Point spread (handicap) at open; null for non-spread markets. |
opening_total | number | yes | Total (over/under line) at open; null for non-total markets. |
opening_ts | string | yes | Timestamp of the first observed record, ISO-8601 UTC. |
closing_odds | integer | yes | American odds of the last eligible active quote strictly before game_start; null when no eligible pre-game quote exists. |
closing_decimal | number | yes | Decimal odds at close. |
closing_implied | number | yes | Implied probability at close (vig included, 0-1). |
closing_spread | number | yes | Point spread (handicap) at close. |
closing_total | number | yes | Total (over/under line) at close. |
closing_ts | string | yes | Timestamp of the last eligible active pre-game quote, ISO-8601 UTC; null when no eligible pre-game quote exists. |
min_odds | integer | yes | Lowest American odds observed across all records, pre-game and live. |
max_odds | integer | yes | Highest American odds observed across all records, pre-game and live. |
distinct_odds_count | integer | yes | Number of distinct American-odds values observed. |
line_move_count | integer | yes | Number of records where the odds changed from the previous record. |
pre_game_records | integer | yes | Change-only records observed before game_start. |
live_records | integer | yes | Change-only records observed at or after game_start. |
first_seen_at | string | yes | Timestamp this side was first observed, ISO-8601 UTC. |
last_seen_at | string | yes | Timestamp this side was last observed (including live), ISO-8601 UTC. |
clv_odds | integer | yes | closing_odds - opening_odds in American odds; null when either is missing or the opening and closing market meanings are incompatible. |
clv_implied | number | yes | opening_implied - closing_implied; null when either is missing or the opening and closing market meanings are incompatible. |
market_player_ids | string[] | yes | Player UUIDs referenced by the market, as of the closing record. |
selection_player_ids | string[] | yes | Player UUIDs referenced by the selection, as of the closing record. |
market_metric | string[] | yes | Structured stat metric(s) the market prices (SportsMetric enum, e.g. FOOTBALL_RECEIVING_YARDS); null for non-player-metric markets. Join key for prop analysis — no name parsing needed (adr-0027). |
selection_metric | string | yes | Structured metric for the priced selection; set only for method-of-score selections, else null. |
market_external_id | string | no | Operator's market id — the identity of the market within (game, operator); `market_key` is `operator` + `:` + this value. Same value as `market_external_id` on market-results and sportsbook-lines. |
market_name | string | yes | Operator's market label, e.g. `Run Line Alternate`; display only. |
selection_external_id | string | no | Operator's selection id — the row identity together with `market_key` and `side`. For FanDuel main markets it is stable and the line moves on it (one row, opening → closing). DraftKings, BetMGM, Pinnacle and FanDuel alternates mint a new id per line value, so one market carries several rows per side and a moved line is a new row with opening == closing. |