Skip to content
Product Documentation

Core concepts

Public game ids are readable: LEAGUE_AWAY_HOME_YYYYMMDD, e.g. NBA_GSW_MIA_20260401.

  • The date is the US Eastern calendar date of the scheduled start.
  • A _2 suffix marks the second game of a doubleheader.
  • Ids are case-insensitive.
  • The public id is the only accepted game id; internal UUIDs are rejected.
  • Discover ids via GET /v1/games (filter by league, ET date range, team, status) or betflux games.

Within a (game, operator), a market is identified by the operator’s own market id — market_external_id on every lines dataset. Every lines dataset also carries market_key = OPERATOR:market_external_id (e.g. FANDUEL:123456), a globally unique market identity and the join key across closing-lines, market-results, and sportsbook-lines. market_type and period are a classification of the market, not its identity: a book can offer several markets with the same type and period (a main spread and its alternate-lines market, one “1+ hits” market per player), and market_name is the operator’s label for telling them apart.

Within a market, a selection is identified by selection_external_id, the operator’s own selection id. selection_name (e.g. a player for prop markets, Over; null for plain team markets) is display text shared across selections, never an identity. A selection carries one or more priced sides (side: HOME, AWAY, OVER, UNDER, …). closing-lines has one row per selection per side; market-results one row per selection.

What a selection id means differs by book, and that shapes how a moving line shows up:

Book Selection id A line move looks like
FanDuel (main markets) stable; the line moves on the same id one row, opening_total 212.5 → closing_total 224.5
FanDuel (alternate lines), BetMGM, Pinnacle one id per line value; the name embeds the line (Over 10, Baltimore Orioles +1.5) one row per line value, opening_* == closing_*
DraftKings one id per line value; the name is bare (Over, BAL Orioles) one row per line value within the market; a new id appears when the line moves
  • Opening values come from the first record BetFlux observed for that side.
  • Closing values come from the last eligible quote before the scheduled start (game_start), with an active market and selection, valid prices, and the required values for a recognized market. Price, threshold and timestamp come from one observation. A side with no eligible pre-game quote retains opening values but has null closing values; later suspended records remain in history. See closing-lines semantics.
  • first_seen_at / last_seen_at span all records, including live ones.

Book-only CLV compares a side’s own open to its own close at the same book:

  • clv_odds = closing_odds − opening_odds (American odds points)
  • clv_implied = opening_implied − closing_implied (implied-probability delta; implied probabilities include vig)

Both are null when a price is unavailable or the opening and closing observations represent incompatible market meanings, such as different periods or metrics.

For cross-book fair-value work, sportsbook-lines carries devigged fair-value columns computed by six de-vig methods.

Timestamp columns are real Parquet timestamps (UTC) — pyarrow and DuckDB decode them as native datetime values, and the CLI renders them as ISO 8601. game_date is a Parquet date: the ET calendar date of the start — use it for “games on April 1” semantics; use game_start for exact times. The one exception is the timeline’s ts, a plain int64 of epoch milliseconds UTC.

How far back you can fetch depends on your tier, enforced per game: fetching a dataset file for a game older than your plan’s history window returns a 403 with problem type history-restricted (DEMO keys are pinned to a fixed sample month). This applies to closing-lines, market-results, and sportsbook-lines; game-state-timeline is not history-gated.