odds channel page — this page assumes you have read it.
What you get per price
Every outcome carries the market-making primitives in one object — no secondary lookup:
The four-stamp chain (
bookmakerChangedAt → changedAt → envelope ts → your receipt) lets you measure delay per venue yourself rather than take it on trust. See Latency and freshness for the published per-book figures.
Deriving a tradeable view
meta.back and meta.lay are uniform across every exchange and prediction market: arrays of { price, size }, best price first, with identical semantics everywhere. The first back entry is top of book and matches the outcome’s price. See Advanced metadata for the exact shape.
Because the shape does not vary, cross-venue comparison is direct — no per-venue parsing branch, and no normalization layer of your own to keep in sync as venues change their APIs.
Best bid and best offer across venues. Group every quote by selection, then take the highest back price and the lowest lay price across the venues you are permissioned for. Both sides matter even if you only ever take one: the tightest spread tells you where the consensus actually sits, and a venue quoting far outside it is either carrying information or carrying a stale price — which of the two you decide with the timestamps, not the price.
Crossed and locked books are a signal, not an opportunity. If the best back on one venue exceeds the best lay on another, check changedAt on both sides before you treat it as arbitrage. A genuine cross closes in milliseconds; a persistent one almost always means one side has stopped updating. Gate on the staleness checks below before sizing into it.
Depth is per venue, not aggregate. Summing ladders across venues gives you a number you cannot trade against — each venue’s queue is separate, and you cannot fill one venue’s order against another’s resting liquidity. Model per-venue executable size, then aggregate the outcomes, not the books.
Sizes carry the venue’s own denomination. size is in the venue’s base currency. For crypto venues, convert through the currencies channel before you compare depth or set position limits, otherwise a large number on one venue and a small number on another can mean the same exposure.
Sizing: limit and depth do different jobs
Two independent size signals arrive on the same row, and confusing them is a common source of over-sizing:
limitis the maximum stake the venue will accept at the current price. It is normalized across books and carried for close to every sharp book — including traditional bookmakers that publish no ladder at all. See Limits.- The ladder is resting liquidity behind the top of book. It tells you what you can clear before you move the price, and at what average cost.
limit bounds a single order, the ladder bounds the fill. For a traditional book you usually have only limit, and that is the whole answer. Walk the ladder to a target size and price the fill at the volume-weighted average rather than at top of book — for anything beyond the first level, the touch price is not the price you get.
Limit changes are prices too. They arrive as ordinary odds updates on the same oddsId. A book cutting its limit while holding its price is telling you something the price alone does not, and it is often the earlier of the two signals. Log limit history alongside price history if you want to see it.
None of this is specific to exchanges — the same distinction governs sizing on traditional sharp books, where limit is the only size signal you get. See sharp-line trading & CLV.
Knowing when a quote is tradeable
Price freshness is a separate question from price value, and for automated trading it is the one that costs money when you get it wrong. Four independent gates, each of which can veto: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. Treat everything from that venue as unvalidated until it flips back to false, and pull your quotes rather than re-price them. Detail in Critical fields for automated trading.
Add a fifth gate of your own: an age check against the published bound. Each book publishes maxDelayLiveInSec / maxDelayPregameInSec on GET /bookmakers. If now − changedAt exceeds that book’s bound by a comfortable margin, the quote is out of contract even though no flag has fired — a quiet venue and a broken venue look identical for the first few seconds. See Status signals.
Wire all five into a single “can I quote against this right now” predicate, evaluated on every update rather than on a timer. One predicate, one place to fix.
For fixture markets, participantsRotated on the bookmakers channel is a sixth check of a different kind: when true, that venue’s home/away assignment is flipped relative to ours, which reverses the meaning of side-based outcomes and flips the sign of handicaps. It affects correctness rather than freshness, and it is silent if you map outcomes by position instead of by ID.
Delay budgeting
Your reaction time is the sum of four segments, and you control exactly one of them:- Venue → OddsPapi observation (
changedAt − bookmakerChangedAt) — published per book, measured where the venue gives usable timestamps. - Conflation — a fixed ~10 ms batching window; the newest value always lands, so this bounds staleness rather than dropping data.
- Gateway → you (
your receipt − ts) — the delivery hop, the one segment you can shorten. See Server location. - Your own decision and order round-trip back to the venue.
Surviving gaps
A market maker’s worst state is quoting from a book it believes is current and is not. The correct disconnect behavior is therefore ordered: pull quotes first, reconcile second.- Server-initiated reconnect. Before a deploy or rebalance we push a
reconnectcontrol frame and keep streaming for a short grace window. Act on the frame, not on the eventual socket error — you get a clean handover instead of a gap. - 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. This is a designed path with a bounded worst case, not an error. - On a changed
serverEpoch, your old cursors are meaningless — re-snapshot rather than resume.
Routing orders back to the venue
bookmakerMarketId and bookmakerOutcomeId travel on every price, on the live stream. When your strategy picks a selection, you already hold the venue’s own identifiers — no mapping table to maintain, no second call before you can send. This matters most exactly when it is hardest: the moment you want to act is the moment you least want a lookup in the path.
At fixture level, GET /fixtures/mapping resolves in both directions — pass fixtureIds to get the venue’s IDs, or bookmakerFixtureIds to go from a venue ID back to the OddsPapi fixture. The bookmakers channel also carries bookmakerFixtureId and fixturePath per venue per fixture, so a manual desk can open the venue’s own page for the same market.
Pricing across venues
Group bymarketId before you price anything. All outcomes under one marketId form a complete probability space, which is what overround, normalization, and completeness checks require — and each distinct line value is its own marketId, so a market never silently mixes lines.
From there the usual construction applies: normalize each venue’s quotes within the market, then combine across venues. Two weightings are worth carrying explicitly, because the data supports both:
- By size — a venue showing real depth and a high
limitis a stronger observation than one quoting a token amount at the same price. - By delay — a venue with a measured sub-100 ms bound and one with a multi-second bound are not equally current, and the published per-book figures let you weight that rather than guess it.
oddsId key as the live stream, so live decisions and their audit trail join without a translation step.
Non-sports markets are futures
Politics, elections, finance, crypto, and weather are modeled as sports withsportId 69+ and exist as futures only — one future per outright market:
futures and oddsFutures channels, which follow the same delivery semantics and carry the same orderbook meta. Everything above about gating, sizing, and depth applies unchanged. See How Futures Work for the model and Coverage for the sportId bands.
Which question a future asks is market.marketId on the futures payload — the 5-digit futureMarketId also carried in the futureId tail, allocated per sport (69002 = Politics Prediction).
Which selection a price is on is the last two segments of the key, and which one carries it depends on the market’s shape. Participant-keyed markets (winner, topscorer, relegation, mvp) own no outcome rows: the selection is the entity you back, and futureOutcomeId is the sentinel 0. Decomposed markets (Yes / No, Over / Under) carry a real futureOutcomeId per side per line — one future market spans many lines, and unlike fixture markets the handicap sits on the outcome rather than on the market. Read both from the explicit fields on the odds row rather than splitting oddsId; see Future odds IDs.
Wiring it up
1
Snapshot
Pull current state over REST (
/fixtures/odds or /futures/odds) to hydrate your book.2
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.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. See Resume & Replay.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 channel 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 across every exchange and prediction market, so you write one parser rather than a per-venue adapter set. Unknownmetakeys are additive; theback/laycore will not be repurposed. - Venue-native IDs ride on the price itself.
bookmakerMarketIdandbookmakerOutcomeIdare on the live odds row, so routing an order back to the venue 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, so what you can actually get filled for is in the same payload as the price. - Loss of contact is reported, not hidden.
staleOddstells you when we can no longer vouch for a venue’s prices, which is the difference between pulling quotes and unknowingly trading against a frozen book.