# The Betting Insider — Insider API (v1)

Graded historical betting data for MLB, NFL, NCAAF, NBA and NHL: every completed game since 2022 (NBA 2022-23, NHL 2023-24) with final scores, consensus and per-book opening/closing lines for moneyline, spread, total and team totals, period markets, venue, weather and result flags. HISTORICAL ONLY: games appear once final; there are no upcoming or live lines.

Base URL: https://api.thebettinginsider.com/v1
OpenAPI 3.1: https://api.thebettinginsider.com/v1/openapi.json
Human docs: https://www.thebettinginsider.com/api/docs
Keys & trial: https://www.thebettinginsider.com/api/manage
License: https://www.thebettinginsider.com/api/legal — commercial use and redistribution inside your own product are allowed; reselling or republishing the data as an API or raw feed is not.

## Authentication

Send `Authorization: Bearer tbi_live_…` (or `X-API-Key: tbi_live_…`). 401 = missing/unknown key. 403 = revoked key, ended trial, past-due or canceled subscription (message links to /api/manage). Trial: 3 days, 2,000 requests, 30 req/min. Standard: 100,000 requests/month, 120 req/min.

## Pagination & CSV

- List endpoints return `{ data: [...], next_cursor: string|null, meta: { count, request_id } }`.
- Pass `next_cursor` back as `?cursor=` with the SAME filters until it is null. Changing filters with an old cursor → 400 invalid_param (param=cursor).
- `limit` 1–1000, default 100. No offset, no total count. Ordered by start_time, then game_id (odds also by book_id), oldest first.
- CSV: `?format=csv` or `Accept: text/csv`. Nested objects become dotted columns; next cursor is in the `X-Next-Cursor` response header.

## Rate limits, quota, caching

- Sliding 1-minute window per key. Headers: X-RateLimit-Limit / -Remaining / -Reset (unix seconds).
- Monthly quota per account counts requests (not rows): X-Quota-Limit / -Remaining / -Reset. Use big pages.
- 429 with `error.code` = `rate_limited` (sleep `Retry-After` seconds, retry) or `quota_exceeded` (stop until reset).
- Every 200 has `ETag` + `Cache-Control: public, max-age=86400` for past seasons (900 when today/yesterday can still change). `If-None-Match` → 304 (still counts).

## Errors

Envelope: `{ "error": { "code", "message", "param"? } }`. Codes: invalid_param (400), unauthorized (401), forbidden (403), not_found (404), rate_limited (429), quota_exceeded (429), internal (500). Unknown query parameters are REJECTED with 400 naming the parameter. `X-Request-Id` on every response.

## Conventions

- game_id = SPORT-YYYYMMDD-AWAY-HOME (e.g. NFL-20240905-BAL-KC); stable join key between /v1/games and /v1/odds.
- season: mlb = calendar year; nfl/ncaaf = year the season started; nba/nhl = year the season ENDED (2026 = 2025-26). season_type: regular | postseason. No preseason/exhibitions/all-star games.
- game_date = slate date in America/New_York (YYYY-MM-DD); from/to filter on it, inclusive. start_time = ISO-8601 UTC.
- Odds are American. Spreads are home-anchored (negative = home favored). open = first captured number, close = last pregame number. *_juice_open is often null before 2024.
- consensus.* on /v1/games = modal line across published US books priced at the mean implied probability (not any single book, not stored). Result flags on /v1/games are graded vs consensus close; on /v1/odds vs that book's close.
- home_covered / went_over: true/false, or null on a push OR when no closing line existed. /v1/odds also has spread_push / total_push.
- Books: US-facing as captured per game (DraftKings, FanDuel, BetMGM, Caesars, BetRivers, Bovada, BetOnline.ag, BetUS, MyBookie.ag, LowVig.ag). No Pinnacle, no betting splits, no props/futures/live odds in v1.

## Endpoints

### GET /v1/sports

Sports, seasons and markets currently published Start here. Lists every sport with the seasons that are published (complete) or in progress (rows added nightly as games go final), the markets available in /v1/bets, and the date of the most recent completed game.

Returns a paginated list of Sport.

Sport fields:

- `sport` enum — Sport. `ncaaf` is FBS college football. [mlb | nfl | ncaaf | nba | nhl]
- `name` string
- `season_label` string — How `season` is numbered for this sport.
- `seasons` object[]
- `seasons[].season` integer
- `seasons[].status` enum — `in_progress` = season still has unplayed games; rows are added nightly. [published | in_progress]
- `seasons[].first_game` string | null — YYYY-MM-DD
- `seasons[].last_game` string | null — YYYY-MM-DD of the most recent completed game
- `seasons[].games` integer
- `seasons[].games_with_closing_line` integer — Games where at least one US book posted a closing line.
- `bet_types` string[] — Markets published in /v1/bets for this sport.
- `last_completed_game` string | null — YYYY-MM-DD
- `teams` integer

Example (captured 2026-10-13, trimmed): `GET /v1/sports`

```json
{
 "data": [
  {
   "sport": "mlb",
   "name": "Major League Baseball",
   "season_label": "calendar year",
   "seasons": [
    {
     "season": 2022,
     "status": "published",
     "first_game": "2022-04-07",
     "last_game": "2022-11-05",
     "games": 2470,
     "games_with_closing_line": 2467
    },
    {
     "season": 2023,
     "status": "published",
     "first_game": "2023-03-30",
     "last_game": "2023-11-01",
     "games": 2471,
     "games_with_closing_line": 2468
    },
    {
     "season": 2024,
     "status": "published",
     "first_game": "2024-03-28",
     "last_game": "2024-10-30",
     "games": 2470,
     "games_with_closing_line": 2470
    },
    {
     "season": 2025,
     "status": "published",
     "first_game": "2025-03-27",
     "last_game": "2025-11-01",
     "games": 2474,
     "games_with_closing_line": 2474
    },
    {
     "season": 2026,
     "status": "in_progress",
     "first_game": "2026-03-25",
     "last_game": "2026-10-12",
     "games": 2457,
     "games_with_closing_line": 2451
    }
   ],
   "bet_types": [
    "moneyline",
    "run_line",
    "total",
    "first_5_moneyline",
    "first_5_runline",
    "inn5_total",
    "inn1_total"
   ],
   "last_completed_game": "2026-10-12",
   "teams": 30
  },
  {
   "sport": "nfl",
   "name": "National Football League",
   "season_label": "year the season started (2025 = 2025-26)",
   "seasons": [
    {
     "season": 2022,
     "status": "published",
     "first_game": "2022-09-08",
     "last_game": "2023-02-12",
     "games": 284,
     "games_with_closing_line": 284
    },
    {
     "season": 2023,
     "status": "published",
     "first_game": "2023-09-07",
     "last_game": "2024-02-11",
     "games": 285,
     "games_with_closing_line": 285
    },
    {
     "season": 2024,
     "status": "published",
     "first_game": "2024-09-05",
     "last_game": "2025-02-09",
     "games": 285,
     "games_with_closing_line": 285
    },
    {
     "season": 2025,
     "status": "published",
     "first_game": "2025-09-04",
     "last_game": "2026-02-08",
     "games": 285,
     "games_with_closing_line": 285
    },
    {
     "season": 2026,
     "status": "in_progress",
     "first_game": "2026-09-09",
     "last_game": "2026-10-12",
     "games": 79,
     "games_with_closing_line": 79
    }
   ],
   "bet_types": [
    "moneyline",
    "spread",
    "total",
    "team_total_over",
    "team_total_under",
    "first_half_moneyline",
    "first_half_spread",
    "first_half_total",
    "second_half_moneyline",
    "second_half_spread",
    "second_half_total",
    "q1_moneyline",
    "q1_spread",
    "q1_total",
    "q2_moneyline",
    "q2_spread",
    "q2_total",
    "q3_moneyline",
    "q3_spread",
    "q3_total",
    "q4_moneyline",
    "q4_spread",
    "q4_total"
   ],
   "last_completed_game": "2026-10-12",
   "teams": 32
  },
  {
   "sport": "ncaaf",
   "name": "NCAA Football (FBS)",
   "season_label": "year the season started",
   "seasons": [
    {
     "season": 2022,
     "status": "published",
     "first_game": "2022-08-27",
     "last_game": "2023-02-04",
     "games": 945,
     "games_with_closing_line": 872
    },
    {
     "season": 2023,
     "status": "published",
     "first_game": "2023-08-26",
     "last_game": "2024-01-08",
     "games": 937,
     "games_with_closing_line": 890
    },
    {
     "season": 2024,
     "status": "published",
     "first_game": "2024-08-24",
     "last_game": "2025-01-20",
     "games": 939,
     "games_with_closing_line": 915
    },
    {
     "season": 2025,
     "status": "published",
     "first_game": "2025-08-23",
     "last_game": "2026-01-19",
     "games": 946,
     "games_with_closing_line": 940
    },
    {
     "season": 2026,
     "status": "in_progress",
     "first_game": "2026-08-29",
     "last_game": "2026-10-10",
     "games": 447,
     "games_with_closing_line": 446
    }
   ],
   "bet_types": [
    "moneyline",
    "spread",
    "total",
    "team_total_over",
    "team_total_under",
    "first_half_moneyline",
    "first_half_spread",
    "first_half_total",
    "second_half_moneyline",
    "second_half_spread",
    "second_half_total",
    "q1_moneyline",
    "q1_spread",
    "q1_total",
    "q2_moneyline",
    "q2_spread",
    "q2_total",
    "q3_moneyline",
    "q3_spread",
    "q3_total",
    "q4_moneyline",
    "q4_spread",
    "q4_total"
   ],
   "last_completed_game": "2026-10-10",
   "teams": 243
  },
  {
   "sport": "nba",
   "name": "National Basketball Association",
   "season_label": "year the season ended (2026 = 2025-26)",
   "seasons": [
    {
     "season": 2023,
     "status": "published",
     "first_game": "2022-10-18",
     "last_game": "2023-06-12",
     "games": 1320,
     "games_with_closing_line": 1320
    },
    {
     "season": 2024,
     "status": "published",
     "first_game": "2023-10-24",
     "last_game": "2024-06-17",
     "games": 1319,
     "games_with_closing_line": 1319
    },
    {
     "season": 2025,
     "status": "published",
     "first_game": "2024-10-22",
     "last_game": "2025-06-22",
     "games": 1321,
     "games_with_closing_line": 1321
    },
    {
     "season": 2026,
     "status": "published",
     "first_game": "2025-10-21",
     "last_game": "2026-06-13",
     "games": 1322,
     "games_with_closing_line": 1322
    }
   ],
   "bet_types": [
    "moneyline",
    "spread",
    "total",
    "team_total_over",
    "team_total_under",
    "first_half_moneyline",
    "first_half_spread",
    "first_half_total",
    "q1_moneyline",
    "q1_spread",
    "q1_total"
   ],
   "last_completed_game": "2026-06-13",
   "teams": 30
  },
  {
   "sport": "nhl",
   "name": "National Hockey League",
   "season_label": "year the season ended (2026 = 2025-26)",
   "seasons": [
    {
     "season": 2024,
     "status": "published",
     "first_game": "2023-10-10",
     "last_game": "2024-06-24",
     "games": 1400,
     "games_with_closing_line": 1400
    },
    {
     "season": 2025,
     "status": "published",
     "first_game": "2024-10-04",
     "last_game": "2025-06-17",
     "games": 1398,
     "games_with_closing_line": 1398
    },
    {
     "season": 2026,
     "status": "published",
     "first_game": "2025-10-07",
     "last_game": "2026-06-14",
     "games": 1394,
     "games_with_closing_line": 1394
    },
    {
     "season": 2027,
     "status": "in_progress",
     "first_game": "2026-09-29",
     "last_game": "2026-10-12",
     "games": 89,
     "games_with_closing_line": 89
    }
   ],
   "bet_types": [
    "moneyline",
    "puck_line",
    "total",
    "team_total_over",
    "team_total_under",
    "p1_moneyline",
    "p1_spread",
    "p1_total"
   ],
   "last_completed_game": "2026-10-12",
   "teams": 33
  }
 ],
 "next_cursor": null,
 "meta": {
  "count": 5,
  "request_id": "7f011caa-7f1f-4a87-98a1-c4663b1c1978"
 }
}
```

### GET /v1/teams

Teams for a sport

Parameters:

- `sport` (query, enum, required) Sport. `ncaaf` is FBS college football. one of: mlb, nfl, ncaaf, nba, nhl. e.g. nfl

Returns a paginated list of Team.

Team fields:

- `team_id` integer
- `abbreviation` string
- `name` string
- `city` string | null
- `school` string | null — College only
- `mascot` string | null
- `conference` string | null
- `division` string | null
- `logo_url` string | null

Example (captured 2026-10-13, trimmed): `GET /v1/teams?sport=nba`

```json
{
 "data": [
  {
   "team_id": 94,
   "abbreviation": "ATL",
   "name": "Atlanta Hawks",
   "city": "Atlanta",
   "school": "Atlanta",
   "mascot": "Hawks",
   "conference": "Eastern Conference",
   "division": "Southeast",
   "logo_url": "https://a.espncdn.com/i/teamlogos/nba/500-dark/atl.png"
  },
  {
   "team_id": 96,
   "abbreviation": "BKN",
   "name": "Brooklyn Nets",
   "city": "Newark",
   "school": "Brooklyn",
   "mascot": "Nets",
   "conference": "Eastern Conference",
   "division": "Atlantic",
   "logo_url": "https://a.espncdn.com/i/teamlogos/nba/500-dark/bkn.png"
  }
 ],
 "next_cursor": null,
 "meta": {
  "count": 30,
  "request_id": "0ee31d32-813c-4b1b-8952-ccc9d56a91c1"
 }
}
```

### GET /v1/seasons

Season summaries for a sport

Parameters:

- `sport` (query, enum, required) Sport. `ncaaf` is FBS college football. one of: mlb, nfl, ncaaf, nba, nhl. e.g. nfl

Returns a paginated list of Season.

Season fields:

- `sport` enum — Sport. `ncaaf` is FBS college football. [mlb | nfl | ncaaf | nba | nhl]
- `season` integer
- `status` enum [published | in_progress]
- `first_game` string | null — YYYY-MM-DD
- `last_game` string | null — YYYY-MM-DD
- `games` integer
- `regular_season_games` integer | null
- `postseason_games` integer | null
- `weeks` integer | null — NFL/NCAAF: highest completed week

Example (captured 2026-10-13, trimmed): `GET /v1/seasons?sport=mlb`

```json
{
 "data": [
  {
   "sport": "mlb",
   "season": 2022,
   "status": "published",
   "first_game": "2022-04-07",
   "last_game": "2022-11-05",
   "games": 2470,
   "regular_season_games": 2430,
   "postseason_games": 40,
   "weeks": null
  },
  {
   "sport": "mlb",
   "season": 2023,
   "status": "published",
   "first_game": "2023-03-30",
   "last_game": "2023-11-01",
   "games": 2471,
   "regular_season_games": 2430,
   "postseason_games": 41,
   "weeks": null
  }
 ],
 "next_cursor": null,
 "meta": {
  "count": 5,
  "request_id": "f234d2ff-f52d-4c2d-84c6-24442a627bd0"
 }
}
```

### GET /v1/games

Completed games with consensus open/close and result One row per completed game. Consensus moneyline/spread/total open and close, final score, and result flags graded against the consensus close. Paginate with `next_cursor`. CSV with `?format=csv`.

Parameters:

- `sport` (query, enum, required) Sport. `ncaaf` is FBS college football. one of: mlb, nfl, ncaaf, nba, nhl. e.g. nfl
- `from` (query, string) Game date (America/New_York), inclusive. `YYYY-MM-DD`. e.g. 2024-09-08
- `to` (query, string) Game date (America/New_York), inclusive. `YYYY-MM-DD`. e.g. 2024-09-08
- `season` (query, integer) Season label. MLB: calendar year. NFL/NCAAF: year the season started. NBA/NHL: year the season ENDED (2026 = 2025-26). e.g. 2024
- `team` (query, string) Team id or abbreviation; matches home OR away. e.g. KC
- `home` (query, string) Home team id or abbreviation. e.g. KC
- `away` (query, string) Away team id or abbreviation. e.g. KC
- `week` (query, integer | null) NFL/NCAAF week number.
- `season_type` (query, enum) Regular season, postseason, or both (default). one of: regular, postseason, all.
- `limit` (query, integer) Page size, 1–1000. e.g. 100
- `cursor` (query, string) Opaque cursor from `next_cursor`.
- `format` (query, enum) Response format. CSV also via `Accept: text/csv`. one of: json, csv.

Returns a paginated list of Game; CSV available.

Game fields:

- `game_id` string
- `sport` enum — Sport. `ncaaf` is FBS college football. [mlb | nfl | ncaaf | nba | nhl]
- `season` integer
- `season_type` string | null — regular | postseason
- `week` integer | null — NFL/NCAAF week
- `game_date` string — YYYY-MM-DD in America/New_York.
- `start_time` string — ISO-8601 UTC kickoff / first pitch / tip.
- `status` enum [final]
- `home` object
- `home.team_id` integer | null — Team id
- `home.abbreviation` string | null — Abbreviation as in /v1/teams
- `home.name` string | null — Team name
- `home.score` integer | null — Final score
- `away` object
- `away.team_id` integer | null — Team id
- `away.abbreviation` string | null — Abbreviation as in /v1/teams
- `away.name` string | null — Team name
- `away.score` integer | null — Final score
- `neutral_site` boolean | null — Neutral site
- `overtime` boolean | null — Went to OT / extra innings / shootout
- `venue` object | null
- `venue.venue_id` integer | null — Venue id
- `venue.name` string | null — Venue name
- `venue.city` string | null — City
- `venue.indoor` boolean | null — Indoor / dome
- `referee` string | null — Lead official / home-plate umpire name
- `result` object
- `result.margin_home` integer | null — home score − away score
- `result.home_covered` boolean | null — Home covered the consensus closing spread (null on push or no line).
- `result.went_over` boolean | null — Final total went over the consensus closing total (null on push or no line).
- `result.favorite` enum | null — Consensus closing favorite. [home | away]
- `consensus` object — Market-wide consensus: the modal line across US books and the mean implied-probability price of the books on that line. Same numbers the web builder grades against. Null when no US book posted the market.
- `consensus.moneyline` object
- `consensus.moneyline.home_open` integer | null — Home ML open. American odds (e.g. -110).
- `consensus.moneyline.home_close` integer | null — Home ML close. American odds (e.g. -110).
- `consensus.moneyline.away_open` integer | null — Away ML open. American odds (e.g. -110).
- `consensus.moneyline.away_close` integer | null — Away ML close. American odds (e.g. -110).
- `consensus.spread` object
- `consensus.spread.open` number | null — Home spread open (negative = home favored). Decimal line (e.g. -3.5, 47.5).
- `consensus.spread.close` number | null — Home spread close. Decimal line (e.g. -3.5, 47.5).
- `consensus.spread.home_juice_open` integer | null — Home spread price at open. American odds (e.g. -110).
- `consensus.spread.home_juice_close` integer | null — Home spread price at close. American odds (e.g. -110).
- `consensus.spread.away_juice_open` integer | null — Away spread price at open. American odds (e.g. -110).
- `consensus.spread.away_juice_close` integer | null — Away spread price at close. American odds (e.g. -110).
- `consensus.total` object
- `consensus.total.open` number | null — Total open. Decimal line (e.g. -3.5, 47.5).
- `consensus.total.close` number | null — Total close. Decimal line (e.g. -3.5, 47.5).
- `consensus.total.over_juice_close` integer | null — Over price at close. American odds (e.g. -110).
- `consensus.total.under_juice_close` integer | null — Under price at close. American odds (e.g. -110).

Example (captured 2026-10-13, trimmed): `GET /v1/games?sport=nfl&season=2024&team=KC&season_type=regular&limit=2`

```json
{
 "data": [
  {
   "game_id": "NFL-20240905-BAL-KC",
   "sport": "nfl",
   "season": 2024,
   "season_type": "regular",
   "week": 1,
   "game_date": "2024-09-05",
   "start_time": "2024-09-06T00:40:00.000Z",
   "status": "final",
   "home": {
    "team_id": 139,
    "abbreviation": "KC",
    "name": "Kansas City Chiefs",
    "score": 27
   },
   "away": {
    "team_id": 126,
    "abbreviation": "BAL",
    "name": "Baltimore Ravens",
    "score": 20
   },
   "neutral_site": false,
   "overtime": null,
   "venue": {
    "venue_id": 2300,
    "name": "GEHA Field at Arrowhead Stadium",
    "city": "Kansas City",
    "indoor": false
   },
   "referee": "Shawn Hochuli",
   "result": {
    "margin_home": 7,
    "home_covered": true,
    "went_over": null,
    "favorite": "home"
   },
   "consensus": {
    "moneyline": {
     "home_open": -148,
     "home_close": -148,
     "away_open": 124,
     "away_close": 124
    },
    "spread": {
     "open": -3,
     "close": -3,
     "home_juice_open": null,
     "home_juice_close": -105,
     "away_juice_open": null,
     "away_juice_close": -115
    },
    "total": {
     "open": 46.5,
     "close": 47,
     "over_juice_close": -112,
     "under_juice_close": -108
    }
   }
  },
  {
   "game_id": "NFL-20240915-CIN-KC",
   "sport": "nfl",
   "season": 2024,
   "season_type": "regular",
   "week": 2,
   "game_date": "2024-09-15",
   "start_time": "2024-09-15T20:25:00.000Z",
   "status": "final",
   "home": {
    "team_id": 139,
    "abbreviation": "KC",
    "name": "Kansas City Chiefs",
    "score": 26
   },
   "away": {
    "team_id": 130,
    "abbreviation": "CIN",
    "name": "Cincinnati Bengals",
    "score": 25
   },
   "neutral_site": false,
   "overtime": null,
   "venue": {
    "venue_id": 2300,
    "name": "GEHA Field at Arrowhead Stadium",
    "city": "Kansas City",
    "indoor": false
   },
   "referee": "Alex Kemp",
   "result": {
    "margin_home": 1,
    "home_covered": false,
    "went_over": true,
    "favorite": "home"
   },
   "consensus": {
    "moneyline": {
     "home_open": -180,
     "home_close": -285,
     "away_open": 150,
     "away_close": 235
    },
    "spread": {
     "open": -3.5,
     "close": -6.5,
     "home_juice_open": null,
     "home_juice_close": -115,
     "away_juice_open": null,
     "away_juice_close": -105
    },
    "total": {
     "open": 48.5,
     "close": 48,
     "over_juice_close": -105,
     "under_juice_close": -115
    }
   }
  }
 ],
 "next_cursor": "eyJrIjp7InQiOiIyMDI0LTA5LTE1IDIwOjI1OjAwLjAwMCIsImlkIjoiTkZMLTIwMjQwOTE1LUNJTi1LQyJ9LCJmIjoiYWY0OWI3ZGQzN2NkIn0",
 "meta": {
  "count": 2,
  "request_id": "d3c69063-3e97-4040-afe2-e045eb201d8d"
 }
}
```

### GET /v1/games/{game_id}

One game: per-book odds, period odds, period scores, weather

Parameters:

- `game_id` (path, string, required)  e.g. NFL-20240908-KC-BAL

Returns one GameDetail.

GameDetail fields:

- `game_id` string
- `sport` enum — Sport. `ncaaf` is FBS college football. [mlb | nfl | ncaaf | nba | nhl]
- `season` integer
- `season_type` string | null — regular | postseason
- `week` integer | null — NFL/NCAAF week
- `game_date` string — YYYY-MM-DD in America/New_York.
- `start_time` string — ISO-8601 UTC kickoff / first pitch / tip.
- `status` enum [final]
- `home` object
- `home.team_id` integer | null — Team id
- `home.abbreviation` string | null — Abbreviation as in /v1/teams
- `home.name` string | null — Team name
- `home.score` integer | null — Final score
- `away` object
- `away.team_id` integer | null — Team id
- `away.abbreviation` string | null — Abbreviation as in /v1/teams
- `away.name` string | null — Team name
- `away.score` integer | null — Final score
- `neutral_site` boolean | null — Neutral site
- `overtime` boolean | null — Went to OT / extra innings / shootout
- `venue` object | null
- `venue.venue_id` integer | null — Venue id
- `venue.name` string | null — Venue name
- `venue.city` string | null — City
- `venue.indoor` boolean | null — Indoor / dome
- `referee` string | null — Lead official / home-plate umpire name
- `result` object
- `result.margin_home` integer | null — home score − away score
- `result.home_covered` boolean | null — Home covered the consensus closing spread (null on push or no line).
- `result.went_over` boolean | null — Final total went over the consensus closing total (null on push or no line).
- `result.favorite` enum | null — Consensus closing favorite. [home | away]
- `consensus` object — Market-wide consensus: the modal line across US books and the mean implied-probability price of the books on that line. Same numbers the web builder grades against. Null when no US book posted the market.
- `consensus.moneyline` object
- `consensus.moneyline.home_open` integer | null — Home ML open. American odds (e.g. -110).
- `consensus.moneyline.home_close` integer | null — Home ML close. American odds (e.g. -110).
- `consensus.moneyline.away_open` integer | null — Away ML open. American odds (e.g. -110).
- `consensus.moneyline.away_close` integer | null — Away ML close. American odds (e.g. -110).
- `consensus.spread` object
- `consensus.spread.open` number | null — Home spread open (negative = home favored). Decimal line (e.g. -3.5, 47.5).
- `consensus.spread.close` number | null — Home spread close. Decimal line (e.g. -3.5, 47.5).
- `consensus.spread.home_juice_open` integer | null — Home spread price at open. American odds (e.g. -110).
- `consensus.spread.home_juice_close` integer | null — Home spread price at close. American odds (e.g. -110).
- `consensus.spread.away_juice_open` integer | null — Away spread price at open. American odds (e.g. -110).
- `consensus.spread.away_juice_close` integer | null — Away spread price at close. American odds (e.g. -110).
- `consensus.total` object
- `consensus.total.open` number | null — Total open. Decimal line (e.g. -3.5, 47.5).
- `consensus.total.close` number | null — Total close. Decimal line (e.g. -3.5, 47.5).
- `consensus.total.over_juice_close` integer | null — Over price at close. American odds (e.g. -110).
- `consensus.total.under_juice_close` integer | null — Under price at close. American odds (e.g. -110).
- `period_scores` map<object> — Per-period scores keyed by period (`q1`…`q4`, `1h`, `2h`, `p1`…`p3`, `inn1`, `inn5`, `ot`). Only periods we have.
- `period_scores.*.home` integer | null
- `period_scores.*.away` integer | null
- `weather` object | null
- `weather.temp_f` number | null — °F
- `weather.wind_mph` number | null — mph
- `weather.wind_direction` string | null
- `weather.condition` string | null — Text condition; `Indoor` for domes
- `books` BookOdds[] — Per-book full-game odds. US books only.
- `books[].book_id` integer
- `books[].book` string — Book slug, e.g. `draftkings`.
- `books[].book_name` string
- `books[].moneyline` object
- `books[].moneyline.home_open` integer | null — Home ML open. American odds (e.g. -110).
- `books[].moneyline.home_close` integer | null — Home ML close. American odds (e.g. -110).
- `books[].moneyline.away_open` integer | null — Away ML open. American odds (e.g. -110).
- `books[].moneyline.away_close` integer | null — Away ML close. American odds (e.g. -110).
- `books[].spread` object
- `books[].spread.open` number | null — Home spread open. Decimal line (e.g. -3.5, 47.5).
- `books[].spread.close` number | null — Home spread close. Decimal line (e.g. -3.5, 47.5).
- `books[].spread.home_juice_open` integer | null —  American odds (e.g. -110).
- `books[].spread.home_juice_close` integer | null —  American odds (e.g. -110).
- `books[].spread.away_juice_open` integer | null —  American odds (e.g. -110).
- `books[].spread.away_juice_close` integer | null —  American odds (e.g. -110).
- `books[].total` object
- `books[].total.open` number | null — Total open. Decimal line (e.g. -3.5, 47.5).
- `books[].total.close` number | null — Total close. Decimal line (e.g. -3.5, 47.5).
- `books[].total.over_juice_open` integer | null —  American odds (e.g. -110).
- `books[].total.over_juice_close` integer | null —  American odds (e.g. -110).
- `books[].total.under_juice_open` integer | null —  American odds (e.g. -110).
- `books[].total.under_juice_close` integer | null —  American odds (e.g. -110).
- `books[].team_total` object
- `books[].team_total.home_open` number | null —  Decimal line (e.g. -3.5, 47.5).
- `books[].team_total.home_close` number | null —  Decimal line (e.g. -3.5, 47.5).
- `books[].team_total.away_open` number | null —  Decimal line (e.g. -3.5, 47.5).
- `books[].team_total.away_close` number | null —  Decimal line (e.g. -3.5, 47.5).
- `period_odds` PeriodOdds[]
- `period_odds[].period` string — `1h`, `2h`, `q1`–`q4`, `p1`, `inn1`, `inn5`.
- `period_odds[].book_id` integer
- `period_odds[].book` string
- `period_odds[].moneyline` object
- `period_odds[].moneyline.home_open` integer | null —  American odds (e.g. -110).
- `period_odds[].moneyline.home_close` integer | null —  American odds (e.g. -110).
- `period_odds[].moneyline.away_open` integer | null —  American odds (e.g. -110).
- `period_odds[].moneyline.away_close` integer | null —  American odds (e.g. -110).
- `period_odds[].spread` object
- `period_odds[].spread.open` number | null —  Decimal line (e.g. -3.5, 47.5).
- `period_odds[].spread.close` number | null —  Decimal line (e.g. -3.5, 47.5).
- `period_odds[].spread.home_juice_close` integer | null —  American odds (e.g. -110).
- `period_odds[].spread.away_juice_close` integer | null —  American odds (e.g. -110).
- `period_odds[].total` object
- `period_odds[].total.open` number | null —  Decimal line (e.g. -3.5, 47.5).
- `period_odds[].total.close` number | null —  Decimal line (e.g. -3.5, 47.5).
- `period_odds[].total.over_juice_close` integer | null —  American odds (e.g. -110).
- `period_odds[].total.under_juice_close` integer | null —  American odds (e.g. -110).

Example (captured 2026-10-13, trimmed): `GET /v1/games/NFL-20240905-BAL-KC`

```json
{
 "data": {
  "game_id": "NFL-20240905-BAL-KC",
  "sport": "nfl",
  "season": 2024,
  "season_type": "regular",
  "week": 1,
  "game_date": "2024-09-05",
  "start_time": "2024-09-06T00:40:00.000Z",
  "status": "final",
  "home": {
   "team_id": 139,
   "abbreviation": "KC",
   "name": "Kansas City Chiefs",
   "score": 27
  },
  "away": {
   "team_id": 126,
   "abbreviation": "BAL",
   "name": "Baltimore Ravens",
   "score": 20
  },
  "neutral_site": false,
  "overtime": null,
  "venue": {
   "venue_id": 2300,
   "name": "GEHA Field at Arrowhead Stadium",
   "city": "Kansas City",
   "indoor": false
  },
  "referee": "Shawn Hochuli",
  "result": {
   "margin_home": 7,
   "home_covered": true,
   "went_over": null,
   "favorite": "home"
  },
  "consensus": {
   "moneyline": {
    "home_open": -148,
    "home_close": -148,
    "away_open": 124,
    "away_close": 124
   },
   "spread": {
    "open": -3,
    "close": -3,
    "home_juice_open": null,
    "home_juice_close": -105,
    "away_juice_open": null,
    "away_juice_close": -115
   },
   "total": {
    "open": 46.5,
    "close": 47,
    "over_juice_close": -112,
    "under_juice_close": -108
   }
  },
  "period_scores": {
   "q1": {
    "home": 7,
    "away": 7
   },
   "q2": {
    "home": 6,
    "away": 3
   },
   "q3": {
    "home": 7,
    "away": 0
   },
   "q4": {
    "home": 7,
    "away": 10
   },
   "1h": {
    "home": 13,
    "away": 10
   },
   "2h": {
    "home": 14,
    "away": 10
   }
  },
  "weather": {
   "temp_f": 71,
   "wind_mph": 16,
   "wind_direction": null,
   "condition": "Clear"
  },
  "books": [
   {
    "book_id": 1002,
    "book": "betmgm",
    "book_name": "BetMGM",
    "moneyline": {
     "home_open": -155,
     "home_close": -150,
     "away_open": 130,
     "away_close": 125
    },
    "spread": {
     "open": -3,
     "close": -3,
     "home_juice_open": null,
     "home_juice_close": -105,
     "away_juice_open": null,
     "away_juice_close": -115
    },
    "total": {
     "open": 46.5,
     "close": 46.5,
     "over_juice_open": null,
     "over_juice_close": -115,
     "under_juice_open": null,
     "under_juice_close": -105
    },
    "team_total": {
     "home_open": 23.5,
     "home_close": 23.5,
     "away_open": 23.5,
     "away_close": 23.5
    }
   },
   {
    "book_id": 1001,
    "book": "betonlineag",
    "book_name": "BetOnline.ag",
    "moneyline": {
     "home_open": -145,
     "home_close": -145,
     "away_open": 125,
     "away_close": 125
    },
    "spread": {
     "open": -3,
     "close": -2.5,
     "home_juice_open": null,
     "home_juice_close": -115,
     "away_juice_open": null,
     "away_juice_close": -105
    },
    "total": {
     "open": 46.5,
     "close": 46.5,
     "over_juice_open": null,
     "over_juice_close": -108,
     "under_juice_open": null,
     "under_juice_close": -112
    },
    "team_total": {
     "home_open": 24.5,
     "home_close": 24.5,
     "away_open": 21.5,
     "away_close": 21.5
    }
   }
  ],
  "period_odds": [
   {
    "period": "1h",
    "book_id": 1002,
    "book": "betmgm",
    "moneyline": {
     "home_open": -140,
     "home_close": -145,
     "away_open": 115,
     "away_close": 120
    },
    "spread": {
     "open": -1,
     "close": -1.5,
     "home_juice_close": -110,
     "away_juice_close": -110
    },
    "total": {
     "open": 23,
     "close": 23,
     "over_juice_close": -105,
     "under_juice_close": -115
    }
   },
   {
    "period": "1h",
    "book_id": 1001,
    "book": "betonlineag",
    "moneyline": {
     "home_open": -140,
     "home_close": -145,
     "away_open": 120,
     "away_close": 125
    },
    "spread": {
     "open": -1.5,
     "close": -1.5,
     "home_juice_close": 100,
     "away_juice_close": -120
    },
    "total": {
     "open": 23,
     "close": 23,
     "over_juice_close": 100,
     "under_juice_close": -120
    }
   },
   {
    "period": "1h",
    "book_id": 1003,
    "book": "betrivers",
    "moneyline": {
     "home_open": null,
     "home_close": null,
     "away_open": null,
     "away_close": null
    },
    "spread": {
     "open": -1.5,
     "close": -1.5,
     "home_juice_close": -105,
     "away_juice_close": -120
    },
    "total": {
     "open": 22.5,
     "close": 22.5,
     "over_juice_close": -118,
     "under_juice_close": -106
    }
   }
  ]
 },
 "meta": {
  "request_id": "7db797d3-c65a-4959-92f7-edab4eddcf96"
 }
}
```

### GET /v1/odds

Per-book open/close for completed games One row per (game, book). US sportsbooks only. Requires at least one of `season`, `from`/`to`, `team`, or `game_id`.

Parameters:

- `sport` (query, enum, required) Sport. `ncaaf` is FBS college football. one of: mlb, nfl, ncaaf, nba, nhl. e.g. nfl
- `from` (query, string) Game date (America/New_York), inclusive. `YYYY-MM-DD`. e.g. 2024-09-08
- `to` (query, string) Game date (America/New_York), inclusive. `YYYY-MM-DD`. e.g. 2024-09-08
- `season` (query, integer) Season label. MLB: calendar year. NFL/NCAAF: year the season started. NBA/NHL: year the season ENDED (2026 = 2025-26). e.g. 2024
- `team` (query, string) Team id or abbreviation (`KC`, `kansas-city` not accepted — use the abbreviation from /v1/teams). e.g. KC
- `game_id` (query, string) Restrict to one game.
- `book` (query, string) Comma-separated book slugs (`draftkings,fanduel`) or ids. Default: all US books.
- `market` (query, enum) Only rows where this market was priced. one of: moneyline, spread, total, team_total.
- `limit` (query, integer) Page size, 1–1000. e.g. 100
- `cursor` (query, string) Opaque cursor from `next_cursor`.
- `format` (query, enum) Response format. CSV also via `Accept: text/csv`. one of: json, csv.

Returns a paginated list of OddsRow; CSV available.

OddsRow fields:

- `game_id` string
- `sport` enum — Sport. `ncaaf` is FBS college football. [mlb | nfl | ncaaf | nba | nhl]
- `game_date` string
- `start_time` string
- `home` string | null — Home abbreviation
- `away` string | null — Away abbreviation
- `book_id` integer
- `book` string
- `book_name` string
- `moneyline` object
- `moneyline.home_open` integer | null —  American odds (e.g. -110).
- `moneyline.home_close` integer | null —  American odds (e.g. -110).
- `moneyline.away_open` integer | null —  American odds (e.g. -110).
- `moneyline.away_close` integer | null —  American odds (e.g. -110).
- `spread` object
- `spread.open` number | null — Home spread. Decimal line (e.g. -3.5, 47.5).
- `spread.close` number | null — Home spread. Decimal line (e.g. -3.5, 47.5).
- `spread.home_juice_open` integer | null —  American odds (e.g. -110).
- `spread.home_juice_close` integer | null —  American odds (e.g. -110).
- `spread.away_juice_open` integer | null —  American odds (e.g. -110).
- `spread.away_juice_close` integer | null —  American odds (e.g. -110).
- `total` object
- `total.open` number | null —  Decimal line (e.g. -3.5, 47.5).
- `total.close` number | null —  Decimal line (e.g. -3.5, 47.5).
- `total.over_juice_open` integer | null —  American odds (e.g. -110).
- `total.over_juice_close` integer | null —  American odds (e.g. -110).
- `total.under_juice_open` integer | null —  American odds (e.g. -110).
- `total.under_juice_close` integer | null —  American odds (e.g. -110).
- `team_total` object
- `team_total.home_open` number | null —  Decimal line (e.g. -3.5, 47.5).
- `team_total.home_close` number | null —  Decimal line (e.g. -3.5, 47.5).
- `team_total.home_over_juice_close` integer | null —  American odds (e.g. -110).
- `team_total.home_under_juice_close` integer | null —  American odds (e.g. -110).
- `team_total.away_open` number | null —  Decimal line (e.g. -3.5, 47.5).
- `team_total.away_close` number | null —  Decimal line (e.g. -3.5, 47.5).
- `team_total.away_over_juice_close` integer | null —  American odds (e.g. -110).
- `team_total.away_under_juice_close` integer | null —  American odds (e.g. -110).
- `result` object — Graded against THIS book's closing number.
- `result.home_covered` boolean | null
- `result.spread_push` boolean | null
- `result.went_over` boolean | null
- `result.total_push` boolean | null

Example (captured 2026-10-13, trimmed): `GET /v1/odds?sport=nba&game_id=NBA-20231024-LAL-DEN&market=spread`

```json
{
 "data": [
  {
   "game_id": "NBA-20231024-LAL-DEN",
   "sport": "nba",
   "game_date": "2023-10-24",
   "start_time": "2023-10-24T23:30:00.000Z",
   "home": "DEN",
   "away": "LAL",
   "book_id": 1001,
   "book": "betonlineag",
   "book_name": "BetOnline.ag",
   "moneyline": {
    "home_open": -189,
    "home_close": -179,
    "away_open": 165,
    "away_close": 160
   },
   "spread": {
    "open": -5,
    "close": -5,
    "home_juice_open": null,
    "home_juice_close": -110,
    "away_juice_open": null,
    "away_juice_close": -110
   },
   "total": {
    "open": 227.5,
    "close": 229,
    "over_juice_open": null,
    "over_juice_close": -110,
    "under_juice_open": null,
    "under_juice_close": -110
   },
   "team_total": {
    "home_open": 116.5,
    "home_close": 116.5,
    "home_over_juice_close": -110,
    "home_under_juice_close": -110,
    "away_open": 111.5,
    "away_close": 112,
    "away_over_juice_close": -110,
    "away_under_juice_close": -110
   },
   "result": {
    "home_covered": true,
    "spread_push": false,
    "went_over": false,
    "total_push": false
   }
  },
  {
   "game_id": "NBA-20231024-LAL-DEN",
   "sport": "nba",
   "game_date": "2023-10-24",
   "start_time": "2023-10-24T23:30:00.000Z",
   "home": "DEN",
   "away": "LAL",
   "book_id": 1002,
   "book": "betmgm",
   "book_name": "BetMGM",
   "moneyline": {
    "home_open": -210,
    "home_close": -200,
    "away_open": 170,
    "away_close": 165
   },
   "spread": {
    "open": -4.5,
    "close": -4.5,
    "home_juice_open": null,
    "home_juice_close": -115,
    "away_juice_open": null,
    "away_juice_close": -105
   },
   "total": {
    "open": 227.5,
    "close": 228.5,
    "over_juice_open": null,
    "over_juice_close": -110,
    "under_juice_open": null,
    "under_juice_close": -110
   },
   "team_total": {
    "home_open": null,
    "home_close": null,
    "home_over_juice_close": null,
    "home_under_juice_close": null,
    "away_open": null,
    "away_close": null,
    "away_over_juice_close": null,
    "away_under_juice_close": null
   },
   "result": {
    "home_covered": true,
    "spread_push": false,
    "went_over": false,
    "total_push": false
   }
  }
 ],
 "next_cursor": null,
 "meta": {
  "count": 10,
  "request_id": "d6d11e80-b753-41fc-b6eb-3e24ccfb6a0b"
 }
}
```

## Example error bodies

401 (401): `{"error":{"code":"unauthorized","message":"missing API key — send `Authorization: Bearer tbi_live_…`"}}`

404 (404): `{"error":{"code":"not_found","message":"game not found"}}`

400_unknown_param (400): `{"error":{"code":"invalid_param","message":"unknown parameter: seasn","param":"seasn"}}`

400_bad_value (400): `{"error":{"code":"invalid_param","message":"sport: must be one of: mlb, nfl, ncaaf, nba, nhl","param":"sport"}}`

400_odds_filter (400): `{"error":{"code":"invalid_param","message":"provide at least one of season, from/to, team, or game_id","param":"season"}}`

## Versioning

Additive changes only within /v1 (new endpoints, optional params, fields). Breaking changes get a new prefix with 90 days notice. Ignore unknown fields.
