Language prefix
All endpoints are prefixed with a language code:/en/.../de/.../fr/...
sportId, fixtureId, etc.) are language-independent.
Sports, tournaments, seasons
sportId
- Integer.
- The first two digits embedded into several downstream IDs.
- Discover via
/sports.
tournamentId
- Integer.
- Belongs to exactly one sport.
- Discover via
/tournaments.
seasonId
- Integer.
- Belongs to exactly one tournament.
- Discover via
/seasons.
See the full Sports Coverage table for every sportId and whether it’s available as fixtures, futures, or both.
Entity hierarchy
Everything in the API hangs off one tree:How Fixtures Work (Sports)
A fixture is a single match between two participants (teams or players):- The fixture object carries the schedule and state:
startTime(epoch seconds),statusId,participant1Id/participant2Id,seasonId, venue — see thefixtureschannel and REST reference for full schemas. - Scores are per period, keyed
result,p1,p2, … — theresultrow is the authoritative final/current score; period rows let you settle period markets. See thescoreschannel. - Odds attach to fixtures via the odds identifiers below — one price per
{fixtureId}:{bookmaker}:{outcomeId}:{playerId}.
How Futures Work (Prediction Markets)
A future is an outright market on a whole season or event rather than a single match — “Who wins the Premier League?” — and the same model covers non-sports prediction markets (“Who wins the 2028 US election?”, “Will Bitcoin hit $100k?”). For sports, a future hangs off the exact same season its fixtures do — only the leaf differs:Arsenal vs Chelsea is one match inside season 130281; the Winner future is a market on that season as a whole. Same tree, same seasonId — a fixture resolves in 90 minutes, a future resolves when the season does.
Non-sports prediction markets reuse the identical model — the recurring topic is the tournament, the edition is the season:
- One future = one outright market within a season. The trailing
futureMarketIdin thefutureIdsays which kind. It is allocated per sport, so the same question is a distinct market in each sport:
So soccer’s Winner market is
10001 and the Politics Prediction market is 69002. Position 002 is topscorer for sports and prediction for the non-sports bands — the two never collide, because no sport carries both.
- Futures odds are keyed
{futureId}:{bookmaker}:{futureOutcomeId}:{participantId}— see Future odds IDs. - Non-sports topics (politics, elections, finance, crypto, weather, …) are modeled as sports with
sportId69+ and exist as futures only; racing codes (79–81) are ante-post futures. See Sports Coverage for the exact bands.
Markets and outcomes
TL;DR — Every selection follows a fixed hierarchy:
marketType → period → handicap → side, all baked into the outcomeId — so the same bet always resolves to the same marketId / outcomeId, addressed by coordinates rather than bookmaker name strings. The one part not in outcomeId is the player, carried by playerId. All outcomes of a market share its marketId, which equals the market’s first outcomeId.How markets decompose (the deterministic hierarchy)
EveryoutcomeId comes from a single, deterministic decomposition — OddsPapi splits each market into a fixed hierarchy:
marketId / outcomeId across bookmakers and fixtures.
Key rule: because every line value gets its ownmarketId, “Over 2.5” and “Over 3.5” are different markets — not two outcomes of one market. Within a singlemarketId, the outcomes are only the sides of that exact line (e.g.Over 2.5+Under 2.5).
Worked example — soccer (sportId 10)
Worked example — soccer (sportId 10)
A representative slice of the real soccer market map. Each
marketId equals its first outcomeId, and each line value is its own market:Reading the first row:
marketType=1x2, period=fulltime, handicap=0.0, outcomeName=1 — the home side of the full-time match-result market.Relationship between marketId and outcomeId
Markets and outcomes are tightly coupled by design:
- A market represents a complete betting market (for example moneyline, 1x2, totals).
- A market contains multiple outcomes.
- All outcomes that belong to the same market share the same
marketId. - The first
outcomeIdof a market is always equal to themarketId.
marketLength
marketLength is the number of outcomes under a marketId — the count of sides that can be bet on, which together form the complete probability space (their implied probabilities sum to ≈ 1 before margin removal). Examples: moneyline → 2, 1x2 → 3, winningmargin → the number of margin bands offered.
ID structure
BothmarketId and outcomeId are integers constructed as:
11xxxx→ basketball market or outcome14xxxx→ American football market or outcome
- Unlimited markets and outcomes per sport
- Fast grouping by sport
- Immediate visibility of market relationships
Why marketId matters (arbitrage & modeling)
All outcomes under a single marketId together represent a complete probability space.
This makes marketId especially useful for:
- Arbitrage detection
- Overround / margin calculations
- Probability normalization
- Market completeness checks
marketId.
Participants and players
participantId
- Integer.
- Represents teams or competitors in fixtures.
- Discover via
/participants.
playerId
- Integer.
- Used for player proposition markets.
playerId = 0typically represents a non-player market.- Discover via
/players.
Fixture IDs
fixtureId structure
fixtureId is a string that encodes multiple pieces of information:
id→ internal namespace prefix — treat as opaque11→ sportId (2 digits)000132→ tournamentId (6 digits)62926199→ native fixture ID within that namespace
fixtureId alone you can infer the sport and tournament — handy for logging and cross-system correlation.
To correlate with a fixture ID from another system, use the externalProviders block on the fixtures channel or the mapping endpoints — see running OddsPapi as a second feed.
Future IDs
futureId structure
futureId follows the same principle as fixtureId:
prefix→ internal namespace prefix (e.g.id,pm,ks) — treat as opaquesportId→ 2 digitsseasonId→ the season / event instancefutureMarketId→ the outright market this future asks (5 digits — see below)
tournament.tournamentId on the futures payload; keeping it out of the ID is what lets the futureId stay stable when a provider re-parents a season under a different tournament.
Future markets and outcomes
A future market is a question kind — “Who wins the Premier League?”, “Who is top scorer?”, “Will X happen by Y?” — not a stat template. One market is reused by every future that asks it, and is identified bymarket.marketId on the futures payload: the same 5-digit futureMarketId carried in the futureId tail.
Markets come in two shapes:
Two deliberate differences from fixture markets:
handicapsits on the outcome, not the market. For fixtures each line is its ownmarketId. A future is minted per (season, question) before anyone knows which ladder a book will offer, so one future market carries many lines.- There is no
period. A future resolves when its season or question does.
Odds identifiers
TL;DR — A single price is uniquely keyed by
{fixtureId}:{bookmaker}:{outcomeId}:{playerId} (fixtures) or {futureId}:{bookmaker}:{futureOutcomeId}:{participantId} (futures). Use these oddsId strings as primary keys in your storage for clean dedup, updates, and reconciliation.Fixture odds keys
For fixtures, a single price is uniquely identified by:bookmaker and you have the selection itself — {fixtureId}:{outcomeId}:{playerId} — the bookmaker-independent key for grading / settlement, since a selection wins or loses the same way everywhere. No marketId is needed; the outcomeId already encodes the market.
Selection keys are what make cross-book and cross-feed comparison cheap — see running OddsPapi as a second feed for joining to an existing feed on them.
Future odds IDs
Future odds are identified by four segments, mirroring fixture odds keys:0 is a sentinel exactly as playerId is 0 for non-player fixture odds — a real, stable key value, not a null. The two ID spaces never overlap by construction: futureMarketId is at most 5 digits, futureOutcomeId is always 6 (≥ 100001), so a value can never be read as the wrong kind of ID.
Treat oddsId as an opaque string. Key your storage on it, but read bookmaker, futureOutcomeId, participantId, and the rest from the explicit fields on the odds row rather than splitting the segments.
Timestamps (seconds vs milliseconds)
This API intentionally uses both epoch seconds and epoch milliseconds, depending on context. The rule is consistent across fixtures and futures:Epoch seconds (UTC)
All start times are integer epoch seconds:startTime/endTimestartTimeFromstartTimeTo
Epoch milliseconds (UTC)
All odds-update times are integer epoch milliseconds — for fixtures and futures alike:changedAt/bookmakerChangedAt- Odds
sincefilters (/fixtures/odds,/fixtures/odds/main,/futures/odds) - WebSocket envelope
tsand the timestamp part ofentryId
Recommendation
- Store timestamps as integers.
- Rule of thumb: schedule times are seconds, odds-update times are milliseconds.
- Normalize internally only if needed.
Operating tips
Server Location Matters
To achieve the lowest latency for realtime updates:- Our main infrastructure runs in Germany and US East — the fastest delivery regions.
- For best performance, deploy your backend in the same datacenters we use: Hetzner, central Germany.
- On AWS, use:
- eu-central-1 (Frankfurt) — preferred, or
- eu-west-1 (Ireland) — e.g. for prediction-market work near Polymarket
⚡ Servers colocated near our streaming infrastructure receive updates faster and with fewer hops.
Snapshot + realtime pattern
A reliable integration pattern:1
Subscribe to WebSocket
Connect, log in with your filters, and start applying updates keyed per row as soon as
login_ok arrives.2
Fetch HTTP snapshot
Pull the current state (fixtures, odds, or futures) over REST and merge it in — per key, the newer
changedAt wins.3
Re-snapshot on signal
If the WebSocket signals
snapshot_required, re-fetch the HTTP snapshot with the same merge rule.Efficient backfills
- Use
sinceparameters where available - Avoid full historical fetches unless required
- Store odds keyed by their odds identifiers for deduplication
Rate limits
See Rate Limits for headers, per-endpoint limits, and 429 backoff handling.Historical odds and CLV
The realtime WebSocketodds and oddsFutures channels deliver latest state on a fixed ~6 ms batching window — ticks superseded within a window are collapsed to the newest value (no state is lost), so the stream is not a tick ledger. See delivery semantics.
Use the live stream to trade, and the REST history endpoints to measure — CLV models, fill auditing, and backtests.When you need the full price movement of an outcome, or its opening vs closing line, use the REST history endpoints. They share the same
oddsId format ({fixtureId}:{bookmaker}:{outcomeId}:{playerId}) as the live odds channel, so you can join realtime fills directly to their historical and closing-line records.
Trading use cases:
- CLV modelling — compare your fill price against the closing line to measure edge.
- Fill auditing — reconcile traded prices with the recorded timeline to detect slippage.
- Backtesting — replay the full historical timeline to test strategies against real line movement.
Glossary
Quick definitions of the core terms used throughout the API. Identifiers & data model- sportId — Numeric sport identifier; the first two digits of downstream
marketId,outcomeId, andfixtureId. - fixtureId — String key for a single match/event, encoding sport, tournament, and a native fixture ID.
- futureId — String key for a futures / outright market, encoding sport, tournament, season, and market.
- marketType — The kind of market (
1x2,totals,spreads,bothteamsscore, …). - period — The segment a market applies to (
fulltime,p1,p2). - handicap — The line value / specifier of a market; each distinct line value is its own
marketId. - marketId — Integer grouping all outcomes of one market; equals the market’s first
outcomeId. - marketLength — Number of outcomes under a
marketId: the sides that can be bet on, together summing to a probability of ≈ 1. - outcomeId — Integer identifying one fully-decomposed selection:
marketType+period+handicap(line) + side. It encodes everything except the player — that’s whatplayerIdadds. - playerId — Ties an outcome to a player for player markets;
playerId = 0is a non-player market. - participantId — Integer identifying a team or competitor in a fixture.
- oddsId — Composite price key. Fixtures:
{fixtureId}:{bookmaker}:{outcomeId}:{playerId}. Futures:{futureId}:{bookmaker}:{futureOutcomeId}:{participantId}.
- OLV — Opening line value: the outcome’s first recorded price.
- CLV — Closing line value: the outcome’s last price before settlement; the reference for grading execution.
- staleOdds — Flag indicating a bookmaker’s connectivity is degraded, so its odds may not be current.
- settlement — Post-event endpoints returning per-outcome results (won / lost), final scores, and margins.
- serverEpoch + entryId — The per-channel cursor; a change in
serverEpochmeans the client must re-snapshot. - resume / replay — Reconnect and replay missed messages within
resumeWindowMs(default60000). - snapshot_required — Signal that the cursor is outside the resume window and a fresh REST snapshot is needed.