Quick start

Build an NBA product with Odds API

Find NBA games, load every available sportsbook market, follow price changes, and read game results. One event_id connects each request.

01 / Discover

Find NBA games

Request NBA events and keep each event_id for follow-up calls.

02 / Snapshot

Load the odds

Page through the game snapshot to collect every available market.

03 / Stream

Follow changes

Apply odds deltas and read the result when the game finishes.

curl -sS -G 'https://api.odds-api.net/v1/events' \
  -H "X-API-Key: $ODDS_API_KEY" \
  --data-urlencode 'sport=basketball' \
  --data-urlencode 'league=NBA'
Base URLhttps://api.odds-api.net/v1 AuthX-API-Key: $ODDS_API_KEY League filtersport=basketball&league=NBA

Build from the available NBA data

Start with GET /v1/coverage?sport=basketball&league=NBA to build sportsbook and market filters. The event and odds responses determine what your product can display for a particular game.

Endpoints

Use the right NBA endpoint

The public API separates game discovery, game records, odds, and results. A stream sends changes after an initial snapshot.

NeedEndpointWhat it returns
Current coverageGET /v1/coverage?sport=basketball&league=NBANBA league, sportsbook, and approximate market coverage.
GamesGET /v1/events?league=NBATeam names, tip-off times, canonical IDs, and pages of games.
Game recordGET /v1/events/{event_id}The source game record under data.
Game resultGET /v1/events/{event_id}/resultspending or available, with the result record when present.
All game oddsGET /v1/events/{event_id}/odds/snapshotCurrent rows across available sportsbooks, markets, and periods. Follow every next_cursor.
All game odds changesGET /v1/events/{event_id}/odds/streamSSE changes for that game's odds. WebSocket: /odds/ws.
Line movementGET /v1/events/{event_id}/odds/historyRetained points for one selection_key and time range when history access is enabled.
NBA main linesGET /v1/odds/main-lines/stream?leagues=NBAA league-wide feed of main lines. It is a smaller view than every game market.

Coverage

Find NBA coverage and sportsbooks

Request GET /v1/coverage?sport=basketball&league=NBA to build your league and sportsbook filters. The coverage response contains bookmakers, leagues, and approximate markets with an as_of timestamp. Use the canonical keys returned by GET /v1/bookmakers when filtering odds.

curl -sS -G 'https://api.odds-api.net/v1/coverage' \
  --data-urlencode 'sport=basketball' \
  --data-urlencode 'league=NBA'

The bookmakers field on an NBA event shows which sportsbooks are attached to that game. The event odds snapshot shows their current selections. For sportsbook-specific context, see DraftKings, FanDuel, BetMGM, and Bet365.

Events and results

Discover games and read results

Page /v1/events with sport=basketball and league=NBA. Set start_from and start_to as Unix seconds when you need live games as well as upcoming ones. Without start_from, the endpoint starts at the current time.

curl -sS -G 'https://api.odds-api.net/v1/events' \
  -H "X-API-Key: $ODDS_API_KEY" \
  --data-urlencode 'sport=basketball' \
  --data-urlencode 'league=NBA' \
  --data-urlencode 'limit=200'

Illustrative response. Use the returned IDs and times; these values are examples.

{
  "count": 1,
  "items": [
    {
      "away_team": "New York Knicks",
      "bookmakers": {
        "draftkings": null,
        "fanduel": null
      },
      "event_id": "nba-example-001",
      "home_team": "Boston Celtics",
      "league": "NBA",
      "sport": "basketball",
      "start_time": 1792875600
    }
  ],
  "next_cursor": null
}
Follow the cursor

Pass next_cursor back as cursor with the same time filters until it is null. Use event_id for every follow-up request.

Game record

GET /v1/events/{event_id}

The response contains event_id and a data object. Read the fields actually supplied for that game; the record can carry more source detail than the event list.

Result

GET /v1/events/{event_id}/results

The result endpoint returns status as pending or available. An available response contains the result record. Poll it for scores; this endpoint is not an odds stream.

A game without a result record returns:

{
  "event_id": "nba-example-001",
  "result": null,
  "status": "pending"
}

Odds snapshots

Request every available odds market

Leave bookmakers, types, market_keys, and periods out of the request. That returns all odds rows available to your account for the chosen game. price_fields=all includes the supported price fields; include_unavailable=true also returns suspended rows.

curl -sS -G 'https://api.odds-api.net/v1/events/$EVENT_ID/odds/snapshot' \
  -H "X-API-Key: $ODDS_API_KEY" \
  --data-urlencode 'limit=10000' \
  --data-urlencode 'price_fields=all' \
  --data-urlencode 'include_unavailable=true' \
  --data-urlencode 'include_source=true'

Continue with the snapshot's next_cursor until it is null. Save the resume token from the first page before opening the stream, so changes during later pages can be replayed. Match stream changes by the odds-row id.

Illustrative snapshot with a moneyline and a player prop. API prices are decimal: 1.91 is about American -110; 2.05 is about +105.

{
  "as_of_ts_ms": 1792875300000,
  "event_id": "nba-example-001",
  "items": [
    {
      "bet_type": "moneyline",
      "bookmaker": "draftkings",
      "event_id": "nba-example-001",
      "fair_odds": 1.95,
      "id": "line-example-001",
      "is_available": true,
      "market_key": "moneyline",
      "odds": 1.91,
      "odds_no_vig": 1.96,
      "period": "full game",
      "selection_key": "selection-example-home",
      "selection_name": "Boston Celtics",
      "side": "home"
    },
    {
      "bet_type": "player prop",
      "bookmaker": "draftkings",
      "event_id": "nba-example-001",
      "id": "line-example-002",
      "is_available": true,
      "line": "24.5",
      "market_key": "player points",
      "odds": 2.05,
      "period": "full game",
      "player_name": "Example Player",
      "selection_key": "selection-example-points-over",
      "side": "over"
    }
  ],
  "next_cursor": null,
  "resume": "1792875300000-0",
  "ttl_seconds": 120
}

Markets

Read NBA market keys

market_key identifies the normalized market in an odds row. Use market_keys in a query only when your product needs a subset. Keep period, line, side, and player_name when comparing two prices.

American market nameAPI market keysMeaning
Moneyline moneyline Straight-up prices for each team to win the game.
Point spread handicap Spread prices for both teams, including alternate lines where supplied.
Game total total Over and under prices for the combined game score.
Team total team total Over and under prices for each team's score.
Player scoring props player points
player threes
player field goals
Player points, made three-pointers and field-goal lines for covered NBA players.
Player stat props player assists
player rebounds
player blocks
player steals
player turnovers
Assists, rebounds, blocks, steals and turnovers for covered NBA players.
Combined player props player pra
player pr
player pa
player ra
pickem player points
Points-rebounds-assists combinations and pick'em player points when supplied.

For example, a point spread uses the API key handicap. A game total uses total. Player points and player rebounds use their literal prop keys. The Python player props tutorial shows how to match exact prop selections across sportsbooks.

SSE and WebSocket

Follow NBA odds updates

Open one odds stream for each NBA game you are tracking. Pass the snapshot's resume token as since, with the same price and availability options used for the snapshot. The stream returns changes, not another full odds list.

curl -N -G 'https://api.odds-api.net/v1/events/$EVENT_ID/odds/stream' \
  -H "X-API-Key: $ODDS_API_KEY" \
  --data-urlencode "since=$RESUME" \
  --data-urlencode 'catchup=true' \
  --data-urlencode 'price_fields=all' \
  --data-urlencode 'include_unavailable=true' \
  --data-urlencode 'include_source=true'

Illustrative payload for an SSE delta event. The odd.id identifies the row to replace.

{
  "changes": [
    {
      "odd": {
        "bet_type": "moneyline",
        "bookmaker": "draftkings",
        "event_id": "nba-example-001",
        "id": "line-example-001",
        "is_available": true,
        "market_key": "moneyline",
        "odds": 1.95,
        "period": "full game",
        "selection_key": "selection-example-home",
        "selection_name": "Boston Celtics",
        "side": "home"
      },
      "op": "upsert"
    }
  ],
  "event_id": "nba-example-001",
  "resume": "1792875315000-0"
}
Full NBA market feed

Discover games with /v1/events, then snapshot and stream each game. Repeat discovery to add newly scheduled games. This covers all markets offered through each event's odds endpoint.

League-wide main lines

Use /v1/odds/main-lines/snapshot?leagues=NBA and /v1/odds/main-lines/stream?leagues=NBA for a lighter NBA scoreboard. Main lines are a subset of the per-game markets.

  1. delta: apply each change to the local odds row keyed by odd.id. Save the latest resume token.
  2. heartbeat: keep the connection open. It has no odds changes.
  3. resync: discard the local odds view, reload every snapshot page, then reopen the stream with the new token.
  4. Disconnect: reconnect with since=<last_resume> and catchup=true. Back off after errors.

The WebSocket route is /v1/events/{event_id}/odds/ws. It accepts the same query filters and sends JSON envelopes shaped as {"event":"delta","data":{...}}. Keep API keys on your server. Game discovery and results still use their REST endpoints.

For a line chart or audit, request /v1/events/{event_id}/odds/history with a selection_key from the snapshot and bounded ISO 8601 from_ts and to_ts values. The history stream follows one selection. Both history routes require history access; use the game snapshot and odds stream for current state.

Field dictionary

Understand the NBA data

These fields are the pieces an NBA product needs to join games, compare the same bet, show current prices, and recover a live connection. Optional fields can be absent or null.

Game identity and timing

FieldWhat it means
event_idThe canonical game ID. Use it in the game, result, snapshot, and stream paths.
sport / leagueUse basketball and NBA to identify the league in event and coverage responses.
home_team / away_teamThe teams assigned to the home and away sides of this game.
start_timeScheduled tip-off as Unix seconds. Convert it to the viewer's time zone in your app.
bookmakersSportsbooks attached to the event. The event list is a discovery hint; the odds snapshot contains the actual selections.

Markets and selections

FieldWhat it means
idAn odds-row ID. Use it to update a row when a stream delta arrives; treat its format as opaque.
bookmakerThe canonical sportsbook key. Keep it when comparing prices across books.
market_keyThe exact normalized market key in an odds row. The market_keys query filter matches this value.
bet_typeThe normalized market family, such as moneyline, handicap, total, or player prop.
period / period_strWhich game period the bet covers. Keep the period when matching selections.
metricThe measured stat or scoring quantity when the market supplies one.
lineThe spread, total, or player-stat threshold. It is nullable and may be a string.
sideThe side of a market, such as home, away, over, or under.
player_nameThe player for a prop market, when present.
selection_nameThe display name of the offered selection, when present.
selection_keyAn optional normalized selection identity. Treat it as opaque and also compare period, line, player, and side.

Prices and state

FieldWhat it means
oddsThe sportsbook's decimal price. A value of 1.91 is about American -110; 2.05 is about +105.
odds_no_vigA nullable price with the sportsbook margin removed.
fair_oddsA nullable composite fair-odds estimate. Request price_fields=all to include every supported price field.
is_availableWhether the row is currently offered. Request include_unavailable=true when your product must track suspensions.
as_of_ts_msSnapshot freshness time in Unix milliseconds. It applies to the snapshot, not the game's tip-off.
ttl_secondsThe snapshot cache lifetime when supplied. Show age rather than presenting stale odds as current.
next_cursorThe next page token. Continue until it is null for both event lists and odds snapshots.
resume / sincePass the snapshot resume token as the stream's since parameter, then persist the latest delta resume token.

The API provides sportsbook player-prop lines, including points and rebounds. It does not expose a separate player box-score statistics feed.

Production

Handle normal production states

Response or stateWhat to do
No NBA eventsKeep the selected time window visible. An empty page means no games matched that request; widen the window or wait for another slate.
No odds rowsKeep the game record. Odds can be posted later, and a specific sportsbook or prop may have no rows.
status=pendingThe result record is not ready. Poll the game result endpoint separately from the odds stream.
Old as_of_ts_msShow the snapshot age in your product and reload rather than presenting a stale price as current.
401 or 403Check the server-side API key and whether the account has event odds access. See plans.
429 or stream disconnectBack off and reconnect with the last resume token. Respect rate-limit headers when present.
resyncReload all snapshot pages before opening a new stream. The older changes cannot safely be replayed.

Keep ODDS_API_KEY on your server. Use the API reference for current response codes and parameter limits, and status and support when an endpoint is unavailable.

Python example

Run the complete NBA example

The Python example pages a rolling NBA schedule, requests game records and results, loads every odds snapshot page, and opens a stream for each game. It writes JSON Lines, so another process can consume the complete records. Set a suitable discovery window and connection budget for your product.

python -m pip install httpx
export ODDS_API_KEY='your_api_key'
python nba_full_feed.py

Save nba_full_feed.py and run it from your server. Set NBA_PAST_HOURS and NBA_FUTURE_HOURS to change the rolling game window. Set ODDS_API_BASE_URL for a different API environment.

Read the full Python example
"""Discover NBA games, load every available odds row, and follow each game.

Install: python -m pip install httpx
Run:     ODDS_API_KEY=your_key python nba_full_feed.py

The program writes JSON Lines to stdout. Set NBA_PAST_HOURS and
NBA_FUTURE_HOURS to change the rolling discovery window.
"""

from __future__ import annotations

import asyncio
import json
import os
import random
import sys
import time
from typing import Any, AsyncIterator

import httpx


BASE_URL = os.getenv("ODDS_API_BASE_URL", "https://api.odds-api.net/v1").rstrip("/") + "/"
API_KEY = os.getenv("ODDS_API_KEY", "").strip()
PAST_HOURS = int(os.getenv("NBA_PAST_HOURS", "8"))
FUTURE_HOURS = int(os.getenv("NBA_FUTURE_HOURS", "48"))
DISCOVERY_SECONDS = int(os.getenv("NBA_DISCOVERY_SECONDS", "300"))
ODDS_SHAPE = {
    "price_fields": "all",
    "include_unavailable": "true",
    "include_source": "true",
}


def emit(kind: str, **payload: Any) -> None:
    print(json.dumps({"kind": kind, **payload}, separators=(",", ":")), flush=True)


async def get_json(client: httpx.AsyncClient, path: str, params: dict | None = None) -> dict:
    response = await client.get(path, params=params)
    response.raise_for_status()
    return response.json()


async def nba_events(client: httpx.AsyncClient) -> list[dict]:
    now = int(time.time())
    params = {
        "sport": "basketball",
        "league": "NBA",
        "start_from": now - PAST_HOURS * 3600,
        "start_to": now + FUTURE_HOURS * 3600,
        "limit": 1000,
    }
    events: list[dict] = []
    while True:
        page = await get_json(client, "events", params)
        events.extend(page.get("items") or [])
        cursor = page.get("next_cursor")
        if not cursor:
            return events
        params["cursor"] = cursor


async def complete_snapshot(client: httpx.AsyncClient, event_id: str) -> tuple[dict, dict[str, dict]]:
    params = {"limit": 10000, **ODDS_SHAPE}
    rows: dict[str, dict] = {}
    first_page: dict | None = None
    while True:
        page = await get_json(client, f"events/{event_id}/odds/snapshot", params)
        if first_page is None:
            first_page = page
        for odd in page.get("items") or []:
            if odd.get("id"):
                rows[str(odd["id"])] = odd
        cursor = page.get("next_cursor")
        if not cursor:
            break
        params["cursor"] = cursor

    # Start the stream at the first page's resume token. Changes that arrive
    # while later snapshot pages load can then be replayed by the stream.
    return first_page or {}, rows


async def sse_messages(response: httpx.Response) -> AsyncIterator[tuple[str, dict]]:
    event = "message"
    data: list[str] = []
    async for line in response.aiter_lines():
        if line == "":
            if data:
                yield event, json.loads("\n".join(data))
            event, data = "message", []
        elif line.startswith("event:"):
            event = line[6:].strip()
        elif line.startswith("data:"):
            data.append(line[5:].lstrip())
    if data:
        yield event, json.loads("\n".join(data))


def apply_delta(rows: dict[str, dict], payload: dict) -> None:
    for change in payload.get("changes") or []:
        odd = change.get("odd") or {}
        row_id = str(odd.get("id") or "")
        if not row_id:
            continue
        if change.get("op") in {"remove", "delete"}:
            rows.pop(row_id, None)
        elif change.get("op") == "upsert":
            # Keep is_available=false rows so the product can show suspensions.
            rows[row_id] = odd


async def follow_event(client: httpx.AsyncClient, event_id: str) -> None:
    resume = ""
    rows: dict[str, dict] = {}
    needs_snapshot = True
    retry_seconds = 1.0

    while True:
        try:
            if needs_snapshot:
                snapshot, rows = await complete_snapshot(client, event_id)
                resume = str(snapshot.get("resume") or "$")
                emit(
                    "odds_snapshot",
                    event_id=event_id,
                    as_of_ts_ms=snapshot.get("as_of_ts_ms"),
                    ttl_seconds=snapshot.get("ttl_seconds"),
                    resume=resume,
                    items=list(rows.values()),
                )
                needs_snapshot = False

            params = {"since": resume, "catchup": "true", "max_batch": 2000, **ODDS_SHAPE}
            async with client.stream(
                "GET", f"events/{event_id}/odds/stream", params=params,
                headers={"Accept": "text/event-stream"},
            ) as response:
                response.raise_for_status()
                retry_seconds = 1.0
                async for event, payload in sse_messages(response):
                    if event == "delta":
                        apply_delta(rows, payload)
                        resume = str(payload.get("resume") or resume)
                        emit("odds_delta", event_id=event_id, resume=resume, changes=payload.get("changes") or [])
                    elif event == "resync":
                        emit("odds_resync", event_id=event_id, reason=payload.get("reason"))
                        needs_snapshot = True
                        break
                    # A heartbeat contains no price change.

        except asyncio.CancelledError:
            raise
        except (httpx.HTTPError, ValueError, json.JSONDecodeError) as exc:
            emit("stream_error", event_id=event_id, error=str(exc))

        if needs_snapshot:
            continue
        await asyncio.sleep(retry_seconds + random.uniform(0, 0.5))
        retry_seconds = min(retry_seconds * 2, 30.0)


async def game_record(client: httpx.AsyncClient, event_id: str) -> None:
    try:
        detail, result = await asyncio.gather(
            get_json(client, f"events/{event_id}"),
            get_json(client, f"events/{event_id}/results"),
        )
        emit("event", event_id=event_id, data=detail)
        emit("result", event_id=event_id, data=result)
    except httpx.HTTPError as exc:
        emit("record_error", event_id=event_id, error=str(exc))


async def bounded_game_record(client: httpx.AsyncClient, semaphore: asyncio.Semaphore, event_id: str) -> None:
    async with semaphore:
        await game_record(client, event_id)


async def main() -> None:
    if not API_KEY:
        raise SystemExit("Set ODDS_API_KEY before running this example.")
    timeout = httpx.Timeout(connect=10.0, read=None, write=10.0, pool=10.0)
    tasks: dict[str, asyncio.Task] = {}
    record_limit = asyncio.Semaphore(6)
    async with httpx.AsyncClient(
        base_url=BASE_URL,
        headers={"X-API-Key": API_KEY, "Accept": "application/json"},
        timeout=timeout,
    ) as client:
        try:
            while True:
                try:
                    events = await nba_events(client)
                    ids = {str(item["event_id"]) for item in events if item.get("event_id")}
                    emit("event_list", count=len(events), items=events)

                    for event_id in ids:
                        if event_id not in tasks or tasks[event_id].done():
                            tasks[event_id] = asyncio.create_task(follow_event(client, event_id))
                    for event_id in set(tasks) - ids:
                        tasks.pop(event_id).cancel()

                    await asyncio.gather(
                        *(bounded_game_record(client, record_limit, event_id) for event_id in ids)
                    )
                except httpx.HTTPError as exc:
                    emit("discovery_error", error=str(exc))
                await asyncio.sleep(DISCOVERY_SECONDS)
        finally:
            for task in tasks.values():
                task.cancel()
            await asyncio.gather(*tasks.values(), return_exceptions=True)


if __name__ == "__main__":
    try:
        asyncio.run(main())
    except KeyboardInterrupt:
        print("Stopped.", file=sys.stderr)

Reference

NBA endpoint parameters

The parameter lists come from the current public OpenAPI document. The full API reference contains response schemas and other sports routes.

GETCoverage/v1/coverage

Returns public bookmaker, sport, league, and recently observed market coverage. Market records are approximate and based on normalized odds lines seen in the configured lookback window, not a guarantee that every market is available for every event at request time.

Query parameters

bookmaker
string

Canonical bookmaker filter. Use `/bookmakers` or `/coverage` to discover supported keys.

sport
string

Sport filter. Use `/sports` to discover supported values.

league
string

League filter. Use `/leagues?sport=...` to discover supported values.

country_code
string

Comma-separated country code filter, for example `AU` or `AU,UK`.

lookback_days
integer

Number of days of recently observed approximate market coverage to include. Maximum is 90.

Open full response schema
GETBookmakers/v1/bookmakers

Lists active bookmakers accepted by bookmaker filters across odds and betting endpoints. Each item includes the country codes where that bookmaker is available. Pass `country_code=AU` or `country_code=AU,UK` to filter the catalog.

Query parameters

country_code
string

Comma-separated country code filter, for example `AU` or `AU,UK`.

Open full response schema
GETSearch/v1/events

Searches sports events by sport, league, team, time window, status, bookmaker coverage, and pagination cursor. For production polling, use bounded time windows, keep filters stable across pages, and pass `next_cursor` back as `cursor` until there is no next cursor. For live discovery start with `/v1/events/live`; `event_states=in_play` returns only events with a fresh live observation and adds a `hint` when started but unconfirmed events were excluded. Every item has a plain `live_status`.

Query parameters

sport
string

Sport filter. Use `/sports` to discover supported values.

league
string

League filter. Use `/leagues?sport=...` to discover supported values.

start_from
integer

Unix seconds lower bound for event start time. Use bounded windows in production polling.

start_to
integer

Unix seconds upper bound for event start time. Keep windows narrow for hot sync jobs.

cursor
string

Pagination cursor from the previous `next_cursor`. Keep filters identical between pages.

limit
integer

Maximum items to return. Respect the caps returned by `/limits`.

include_bookmaker_ids
boolean

When true, include bookmaker-to-odds-data IDs and preview ID maps on event responses.

include_source
boolean

When true, include per-line/per-bookmaker provenance and capture metadata. Top-level freshness fields are always kept.

include_opportunity_counts
boolean
not_started_only
boolean
not_started_buffer_seconds
integer
event_states
string

Lifecycle filter. Requesting in_play automatically applies the live lookback window.

live_candidates
boolean

Include already-started events that remain candidates for live play; this is not confirmation of current play.

Open full response schema
GETEvent details/v1/events/{event_id}

Returns the current event record for a canonical sports event ID.

Path parameters

event_idrequired
string

Canonical event or race identifier from an event list response.

Query parameters

include_links
boolean

When true, include bookmaker/deep-link fields such as match links and racing links.

include_raw_payload
boolean

When true, include raw stored payload/data objects where the endpoint exposes them.

include_source
boolean

When true, include per-line/per-bookmaker provenance and capture metadata. Top-level freshness fields are always kept.

include_bookmaker_ids
boolean

When true, include bookmaker-to-odds-data IDs and preview ID maps on event responses.

include_debug_ids
boolean

When true, include internal/subgroup/opposing IDs useful for reconciliation.

Open full response schema
GETEvent result/v1/events/{event_id}/results

Returns the latest known result for a sports event, or `pending` until settled. Poll every 1-5 minutes after start, then back off once the event is final.

Path parameters

event_idrequired
string

Canonical event or race identifier from an event list response.

Open full response schema
GETSnapshot/v1/events/{event_id}/odds/snapshot

Returns the current odds lines for one event. Use filters to narrow bookmakers, market types, market keys, and periods. An explicit `bookmakers` filter returns every matching line for those bookmakers in one response, up to the 25,000-item safety cap. Without a bookmaker filter, pages contain whole bookmakers, so a bookmaker's matching markets are never split across pages. Follow the opaque `next_cursor` until `complete=true`. Cache by request shape. `as_of_ts_ms` is when the API accepted the latest successful event snapshot or authoritative bookmaker subset; use `bookmaker_as_of_ts_ms` for bookmaker-specific freshness and compare it with `target_refresh_interval_seconds`. Respect `ttl_seconds` when present, and persist `resume` when you plan to subscribe to updates. Pass `price_fields=odds,fair` to include nullable composite fair odds alongside bookmaker odds. Exchange orderbooks are excluded from this sportsbook-style odds surface.

Path parameters

event_idrequired
string

Canonical event or race identifier from an event list response.

Query parameters

limit
integer

Soft item target when `bookmakers` is omitted. Pages contain whole bookmakers and may exceed this target. With an explicit bookmaker filter, all matching lines are returned up to the 25,000-item safety cap.

cursor
string

Opaque bookmaker-page cursor from the previous `next_cursor`. Use only when `bookmakers` is omitted and keep all filters identical between pages.

bookmakers
string

Comma-separated bookmaker allow-list. Explicitly requested bookmakers are returned complete in one response, up to the 25,000-item safety cap. Use `/bookmakers` to discover supported keys.

types
string

Comma-separated market type allow-list for odds filters.

market_keys
string

Comma-separated market key allow-list for odds filters.

periods
string

Comma-separated period allow-list for odds filters.

price_fields
string

`odds`, `odds,novig`, `odds,fair`, or `all`. Compact clients default to `odds`; existing clients default to current full pricing fields.

include_source
boolean

When true, include per-line/per-bookmaker provenance and capture metadata. Top-level freshness fields are always kept.

include_debug_ids
boolean

When true, include internal/subgroup/opposing IDs useful for reconciliation.

include_unavailable
boolean

When true, include unavailable or suspended rows where the endpoint supports them.

Open full response schema
GETStream (SSE)/v1/events/{event_id}/odds/stream

Server-Sent Events feed for odds changes on one event. Subscribe after reading the snapshot and pass the snapshot `resume` value as `since` to receive catch-up changes when available. Handle ordered semantic `delta` batches idempotently and persist each batch's `resume`; a `heartbeat` carries current freshness even when prices did not change. Reload the snapshot after `resync`. Exchange orderbook changes are served only from the exchange orderbook stream.

Path parameters

event_idrequired
string

Canonical event or race identifier from an event list response.

Query parameters

bookmakers
string

Comma-separated bookmaker allow-list. Use `/bookmakers` to discover supported keys.

types
string

Comma-separated market type allow-list for odds filters.

market_keys
string

Comma-separated market key allow-list for odds filters.

periods
string

Comma-separated period allow-list for odds filters.

price_fields
string

`odds`, `odds,novig`, `odds,fair`, or `all`. Compact clients default to `odds`; existing clients default to current full pricing fields.

include_source
boolean

When true, include per-line/per-bookmaker provenance and capture metadata. Top-level freshness fields are always kept.

include_debug_ids
boolean

When true, include internal/subgroup/opposing IDs useful for reconciliation.

include_unavailable
boolean

When true, include unavailable or suspended rows where the endpoint supports them.

since
string

Resume token from a previous snapshot or stream message. Pass it after reconnecting.

catchup
boolean

When true, return available missed stream events after `since` before waiting for new events.

heartbeat_sec
integer

Heartbeat interval in seconds for stream liveness. Valid range is 5-120.

max_batch
integer

Maximum stream messages to read per batch. Default is 500; use smaller batches for low-latency clients.

Open full response schema
GETStream (WebSocket)/v1/events/{event_id}/odds/ws

WebSocket feed for odds changes on one event. Messages use the same `delta`, `heartbeat`, and `resync` payloads as the SSE stream. Reconnect with jittered exponential backoff and `since=<last_resume>`.

Path parameters

event_idrequired
string

Canonical event or race identifier from an event list response.

Query parameters

bookmakers
string

Comma-separated bookmaker allow-list. Use `/bookmakers` to discover supported keys.

types
string

Comma-separated market type allow-list for odds filters.

market_keys
string

Comma-separated market key allow-list for odds filters.

periods
string

Comma-separated period allow-list for odds filters.

price_fields
string

`odds`, `odds,novig`, `odds,fair`, or `all`. Compact clients default to `odds`; existing clients default to current full pricing fields.

include_source
boolean

When true, include per-line/per-bookmaker provenance and capture metadata. Top-level freshness fields are always kept.

include_debug_ids
boolean

When true, include internal/subgroup/opposing IDs useful for reconciliation.

include_unavailable
boolean

When true, include unavailable or suspended rows where the endpoint supports them.

since
string

Resume token from a previous snapshot or stream message. Pass it after reconnecting.

catchup
boolean

When true, return available missed stream events after `since` before waiting for new events.

heartbeat_sec
integer

Heartbeat interval in seconds for stream liveness. Valid range is 5-120.

max_batch
integer

Maximum stream messages to read per batch. Default is 500; use smaller batches for low-latency clients.

Open full response schema
GETSnapshot/v1/events/{event_id}/odds/history

Returns line movement for a single selection across bookmakers and a time range. Use `selection_key` from an odds snapshot response, bound queries with `from_ts` and `to_ts`, and narrow by bookmaker or market when building charts or backtests.

Path parameters

event_idrequired
string

Canonical event or race identifier from an event list response.

Query parameters

selection_keyrequired
string

Stable selection identifier from an odds snapshot line, used for history and line movement.

market_group_id
string

Optional market grouping filter for history queries.

bookmakers
string

Comma-separated bookmaker allow-list. Use `/bookmakers` to discover supported keys.

from_ts
string

ISO8601 UTC start timestamp for a bounded history query.

to_ts
string

ISO8601 UTC end timestamp for a bounded history query.

price_type
string

History price type to return, for example odds.

price_fields
string

`odds`, `odds,novig`, `odds,fair`, or `all`. Compact clients default to `odds`; existing clients default to current full pricing fields.

include_source
boolean

When true, include per-line/per-bookmaker provenance and capture metadata. Top-level freshness fields are always kept.

include_debug_ids
boolean

When true, include internal/subgroup/opposing IDs useful for reconciliation.

include_unavailable
boolean

When true, include unavailable or suspended rows where the endpoint supports them.

limit_points_per_bookmaker
integer

Maximum history points per bookmaker. Use this to keep chart/backtest payloads bounded.

Open full response schema
GETStream (SSE)/v1/events/{event_id}/odds/history/stream

Server-Sent Events feed for line movement on one selection. This is useful for charts that should update while an event market is moving.

Path parameters

event_idrequired
string

Canonical event or race identifier from an event list response.

Query parameters

selection_keyrequired
string

Stable selection identifier from an odds snapshot line, used for history and line movement.

market_group_id
string

Optional market grouping filter for history queries.

bookmakers
string

Comma-separated bookmaker allow-list. Use `/bookmakers` to discover supported keys.

price_type
string

History price type to return, for example odds.

price_fields
string

`odds`, `odds,novig`, `odds,fair`, or `all`. Compact clients default to `odds`; existing clients default to current full pricing fields.

include_source
boolean

When true, include per-line/per-bookmaker provenance and capture metadata. Top-level freshness fields are always kept.

include_debug_ids
boolean

When true, include internal/subgroup/opposing IDs useful for reconciliation.

include_unavailable
boolean

When true, include unavailable or suspended rows where the endpoint supports them.

since
string

Resume token from a previous snapshot or stream message. Pass it after reconnecting.

catchup
boolean

When true, return available missed stream events after `since` before waiting for new events.

heartbeat_sec
integer

Heartbeat interval in seconds for stream liveness. Valid range is 5-120.

max_batch
integer

Maximum stream messages to read per batch. Default is 500; use smaller batches for low-latency clients.

Open full response schema

Need a narrower build? Start with the NBA product overview or the player props tutorial.