odds channel page.
What arrives on every price
No secondary lookup, and no per-venue parsing branch: the ladders have identical semantics on every exchange and prediction market, and the first
back entry is top of book and matches price. Shape detail in Advanced metadata.
Building a cross-venue view
Group by selection — the same selection at two venues differs only in the venue segment of the key:back and the lowest lay across the venues you are permissioned for. Carry both sides even if you only ever take one — the tightest spread shows where consensus sits, and a venue far outside it is either carrying information or carrying a stale price. The timestamps tell you which; the price does not.
back always equals price. Here the lay side sits nowhere near it — 15 units of interest at 100.0 is a one-sided book, not a spread you can quote inside.
Three things that bite:
- A crossed book is a signal, not an opportunity. If best
backon one venue exceeds bestlayon another, checkchangedAton both sides. A genuine cross closes in milliseconds; a persistent one means a side stopped updating. - Depth is per venue, never aggregate. You cannot fill one venue’s order against another’s queue. Model executable size per venue, then aggregate the outcomes, not the books.
sizeis in the venue’s base currency. Convert through thecurrencieschannel before comparing depth across crypto venues, or the same exposure reads as two different numbers.
Sizing: limit and depth do different jobs
limitbounds a single order — the most the venue accepts at this price. See Limits.- The ladder bounds the fill — what rests behind the touch, and what it costs to clear a size.
limit constrains something else entirely: no single order above 50, however deep the book is.
Limit cuts arrive as ordinary odds updates on the same oddsId and are often the earlier signal. Full treatment, including traditional books where limit is the only size signal there is, in sharp-line trading & CLV.
Gating: is this quote tradeable
Freshness is a separate question from value, and the expensive one to get wrong. Four flags can veto, each independently:
Two of them arrive on a different channel than the price, which is what makes the failure mode look like this — a quote that is immaculate and untradeable at the same time:
staleOdds is the one people miss, because nothing about the last price you received looks wrong — the prices you hold may have moved at the venue with no way for us to tell you. Pull your quotes rather than re-price them, until it flips back to false. See Critical fields for automated trading.
Add a fifth gate of your own — an age check against the bound the book publishes in maxDelayLiveInSec / maxDelayPregameInSec on GET /bookmakers. A quiet venue and a broken venue look identical for the first few seconds. See Status signals. Evaluate all five as one predicate on every update, not on a timer.
On fixture markets,
participantsRotated is a sixth check of a different kind: when true, that venue’s home/away assignment is flipped relative to ours, reversing side-based outcomes and flipping handicap signs. It affects correctness, not freshness — and it is silent if you map outcomes by position instead of by ID.Delay budget
Your reaction time is four segments, and you control one:
Every message carries the first three stamps, so the split is measurable per venue rather than something you take on trust:
Surviving gaps
The worst state is quoting from a book you believe is current and is not. So: pull quotes first, reconcile second.- Server-initiated reconnect — we push a
reconnectcontrol frame before deploys and keep streaming through a grace window. Act on the frame, not on the eventual socket error. - Within the replay window — reconnect with your per-channel cursor (
serverEpoch+entryId) and missed updates replay with no gaps. - Outside it — the gateway emits
snapshot_required; restore with one REST snapshot. A designed path with a bounded worst case, not an error. - Changed
serverEpoch— old cursors are meaningless; re-snapshot instead of resuming.
Routing orders back
Venue-native IDs ride on the price itself, so the moment you decide is not the moment you need a lookup:bookmakerOutcomeId at price, staked at no more than limit.
At fixture level, GET /fixtures/mapping resolves both directions — fixtureIds in for venue IDs, bookmakerFixtureIds in to go back. The bookmakers channel also carries bookmakerFixtureId and fixturePath, so a manual desk can open the venue’s own page.
Pricing across venues
Group bymarketId before pricing anything: all outcomes under one marketId form a complete probability space, which is what overround and completeness checks need — and each distinct line value is its own marketId, so a market never silently mixes lines.
Normalize within each venue, then combine across them. Two weightings the data supports explicitly:
- By size — real depth and a high
limitis a stronger observation than a token amount at the same price. - By delay — a measured sub-100 ms venue and a multi-second one are not equally current.
oddsId key as the live stream, so decisions and their audit trail join without a translation step.
Non-sports markets are futures
Politics, elections, finance, crypto, and weather are sports withsportId 69+ and exist as futures only — one future per outright market, on the futures and oddsFutures channels, with the same delivery semantics and the same orderbook meta. Everything above applies unchanged.
market.marketId — the 5-digit futureMarketId also carried in the futureId tail, allocated per sport (69002 = Politics Prediction). Which selection a price is on depends on the market’s shape:
One decomposed future market spans many lines, and unlike fixture markets the handicap sits on the outcome, not on the market. Read both from the explicit fields rather than splitting
oddsId — see Future odds IDs and How Futures Work.
Wiring it up
1
Stream
Connect to
wss://v5.oddspapi.io/ws and log in with the channels, sports, and venues you trade. Subscribe to bookmakers alongside odds — without it you have prices but no staleness signal.2
Snapshot
With updates already flowing, hydrate over REST (
/fixtures/odds or /futures/odds) and merge by newest changedAt per key. Connect-first is what makes hydration gapless — there is no REST-to-WebSocket cursor, and none is needed.3
Apply updates
Key by
oddsId and overwrite in place. Each message is state, not a tick.4
Gate
Evaluate the quoting predicate on every update, per venue, before anything downstream sees the price.
5
Recover
Handle
reconnect and snapshot_required as designed paths.sportIds, tournamentIds, and bookmakers cut volume far more effectively than client-side filtering — and prefer receiveType: "zstd" on odds at scale (Compression). Login shapes and a worked client are on the odds page and in the Quickstart.
What you can rely on
- One orderbook shape, not one per venue.
meta.back/meta.layare{ price, size }arrays with identical semantics everywhere, so you write one parser. Unknownmetakeys are additive; theback/laycore will not be repurposed. - Venue-native IDs ride on the price itself. Routing an order back costs no second lookup and no reverse-mapping table of your own.
- Size is a first-class field.
limitis normalized across books and carried for close to every sharp book. - Loss of contact is reported, not hidden.
staleOddsis the difference between pulling quotes and unknowingly trading against a frozen book.