BurnerOdds API — Documentation

DashboardTest Console Base URL:

Introduction

The BurnerOdds API delivers real-time sports odds through a single, unified REST + streaming interface. Its structure mirrors the OpticOdds v3 API, so existing OpticOdds integrations work with minimal changes.

All endpoints live under /api/v3. Every response wraps results in a top-level data array.

Base URL

…/api/v3

Authentication

Every request needs your API key, sent either as a header (recommended) or a query parameter:

# Header
curl "/api/v3/sports" -H "X-Api-Key: YOUR_KEY"

# Query param
curl "/api/v3/sports?key=YOUR_KEY"

Your key is licensed to specific sportsbooks. Requesting odds for a sportsbook your key isn't licensed for returns 403.

Quickstart

The typical flow: discover what's available, then pull odds.

# 1. list sports
curl "/api/v3/sports?key=YOUR_KEY"

# 2. leagues for a sport
curl "/api/v3/leagues?sport=soccer&key=YOUR_KEY"

# 3. active fixtures
curl "/api/v3/fixtures/active?sport=soccer&key=YOUR_KEY"

# 4. odds for a fixture (Pinnacle = ps3838)
curl "/api/v3/fixtures/odds?sportsbook=ps3838&fixture_id=FIXTURE_ID&key=YOUR_KEY"

# 5. for real-time updates, stream instead of polling
curl -N "/api/v3/stream/odds/soccer?sportsbook=ps3838&key=YOUR_KEY"

Odds formats

Pass odds_format on odds and streaming endpoints. Default is AMERICAN.

ValueExample (from +150)
AMERICAN150
DECIMAL2.50
PROBABILITY0.4
HONG_KONG1.5
INDONESIAN1.5
MALAY-0.6667

Sportsbooks

Sportsbooks are referenced by id. Currently available:

Brandid
Pinnacleps3838

More sportsbooks are added over time — call /api/v3/sportsbooks for the live list.

GET/api/v3/sports

All sports (and their main markets). /api/v3/sports/active returns only sports with live odds.

{ "data": [ { "id": "soccer", "name": "Soccer", "numerical_id": null,
  "main_markets": [ { "id": "moneyline", "name": "Moneyline" } ] } ] }

GET/api/v3/leagues

Leagues, optionally filtered by sport. /leagues/active = only leagues with live odds.

Param
sportfilter by sport id (e.g. soccer)
{ "data": [ { "id": "england_-_premier_league", "name": "England - Premier League",
  "sport": { "id":"soccer", "name":"Soccer" }, "region":"ENGLAND" } ] }

GET/api/v3/sportsbooks

All sportsbooks; is_active flags those currently serving odds.

{ "data": [ { "id":"ps3838", "name":"PS3838", "logo":"…", "is_active":true } ] }

GET/api/v3/markets

Markets available for a sport — pass ?sport= to get the live list (moneyline, spreads, totals, period markets, player props, specials…). /market-types lists market-type metadata.

curl "/api/v3/markets?sport=baseball&key=YOUR_KEY"
{ "data": [ { "id":"player_strikeouts", "name":"Player Strikeouts" }, … ] }

GET /api/v3/markets/active — markets currently offered for specific fixtures on specific books (requires fixture_id + sportsbook). GET /api/v3/sportsbooks/active returns only live books.

GET/api/v3/teams

Teams/participants. Requires one of sport, league, or id. Paginated.

GET/api/v3/fixtures

Fixtures (games/events). /fixtures/active returns only non-completed fixtures with odds — the recommended starting point. Requires one of sport, league, id.

Param
sport, league, idfilters (repeatable)
status, is_livefilter by fixture status / live flag
team_idfixtures involving a team (repeatable)
start_date_after, start_date_beforetime window (ISO 8601 or YYYY-MM-DD)
pagepage number (50/page)
updated_sinceonly fixtures changed since a timestamp (ISO 8601)
{ "data": [ { "id":"PIN123", "game_id":"123", "start_date":"2026-08-22T14:00:00Z",
  "home_team_display":"Arsenal", "away_team_display":"Chelsea",
  "status":"unplayed", "is_live":false,
  "sport":{"id":"soccer","name":"Soccer"}, "league":{"id":"england_-_premier_league","name":"England - Premier League"},
  "has_odds":true } ], "page":1, "has_more":true }

GET/api/v3/fixtures/odds

Current odds. If an odd isn't returned it is, for all intents, suspended.

Param
sportsbookrequired; repeatable (max 5), e.g. ps3838
fixture_idone of fixture_id / team_id / player_id; repeatable (max 5)
marketfilter by market id (e.g. moneyline, player_strikeouts)
is_maintrue = main lines only (drop alternates)
odds_formatsee Odds formats
{ "data": [ { …fixture fields…, "odds": [
  { "id":"123:ps3838:moneyline:arsenal", "sportsbook":"PS3838",
    "market":"Moneyline", "market_id":"moneyline", "name":"Arsenal", "is_main":true,
    "selection":"Arsenal", "selection_line":null, "points":null, "price":1.95,
    "player_id":null, "team_id":"T_arsenal", "timestamp":1784900000.12,
    "grouping_key":"default", "limits":{"max":5000} } ] } ] }

GET/api/v3/fixtures/odds/historical

Recorded line movement for a single fixture. Params: fixture_id (single), sportsbook, market, include_timeseries=true, include_locked, odds_format.

{ "data":[ { …fixture…, "odds":[ { …descriptor…,
  "entries":[ {"price":1.90,"timestamp":1784800000,"points":2.5,"locked":false} ],
  "olv":{"price":1.90,"points":2.5}, "clv":{"price":1.95,"points":2.5} } ] } ] }

SSE/api/v3/stream/odds/{sport}

Server-Sent Events — a long-lived GET that pushes odds changes within milliseconds. Use this instead of polling for real-time freshness. {sport} is a path param.

Param
sportsbookrequired; repeatable (≤5)
league, fixture_id, marketfilters (repeatable)
is_main, is_liveboolean filters
odds_formatsee Odds formats
last_entry_idreconnect: replays only events after this id (exactly-once). Omit on a fresh connect to receive the full current-odds snapshot instead.

On a fresh connect (no last_entry_id) the stream first sends the current odds as a snapshot — one or more odds events (chunked) with the present state of every odd matching your filters — then switches to live deltas. On reconnect, pass last_entry_id to skip the snapshot and replay only what you missed.

curl -N "/api/v3/stream/odds/soccer?sportsbook=ps3838&odds_format=DECIMAL&key=YOUR_KEY"

event: connected
data: ok go

event: odds          # ← initial snapshot (current state), then live deltas
id: 1730079534820-2
data: {"data":[ {…odd…} ],"entry_id":"1730079534820-2","type":"odds"}

WS/api/v3/ws/odds/{sport}

The same stream over WebSocket (convenient for browsers & most languages). Same query params as SSE. Each message is a JSON object with a type.

const ws = new WebSocket("/api/v3/ws/odds/soccer?sportsbook=ps3838&key=YOUR_KEY");
ws.onmessage = e => {
  const m = JSON.parse(e.data);
  if (m.type === "odds") console.log(m.data);
};

Streaming event model

EventMeaning
connectedhandshake (data: ok go)
pingheartbeat every 5s (data = ISO timestamp)
oddsinitial snapshot on connect + every new/updated price afterwards — upsert by the odd id
locked-oddssuspended/removed — mark unavailable
fixture-statusopt-in via include_fixture_updates=true — fixture status / start-time changes (old_statusnew_status)

Every delta carries an entry_id (<millis>-<seq>). Track the last one; on reconnect pass it as last_entry_id to replay missed events (exactly-once). Wrap the connection in a retry loop with backoff.

Pagination

List endpoints return page and has_more. Request ?page=1, 2, … until has_more is false.

Errors & rate limits

Code
401missing / invalid / revoked key
403key not licensed for the requested sportsbook
400bad request (missing required param, invalid odds_format, >5 sportsbooks)
429rate limit exceeded — see Retry-After header

Rate limits (per API key, per 15-second window): standard endpoints 2,500, new stream/ws connections 250, historical odds 10. A long-lived stream connection doesn't keep consuming quota. Over-limit returns 429 with Retry-After.

Errors return { "error": "…" }. For sub-minute freshness use streaming, not tight polling.