BlogOdds API basics

Live vs pre-match odds data

A live odds API usually means in-play prices, the odds while an event runs, and that is a different kind of data from pre-match odds, the prices before it starts.

OddsRelay serves pre-match prices on every feed. Since 2 November 2026 it has also served in-play prices on raw and on the standard and dutching boards, polled, with no scores or match clock.

What's the difference between in-play and pre-match odds data?

The difference is how long a price stays true. Before kick-off, a price moves on team news, money and the bookmaker's own view, and it can hold for minutes or hours. Once play starts, every incident can re-price a market at once, and bookmakers suspend markets while they do it. A price that was right a moment ago can be gone.

That changes what an odds feed has to do. A pre-match feed is judged on breadth, correct matching and a price age you can reason about. An in-play feed is judged on how quickly each change reaches you and whether it keeps arriving during the busiest moments of a match. They can share a schema. The engineering problems differ.

Pre-matchIn-play
How long a price holdsOften minutes or hoursOften until the next incident
SuspensionsOccasionalRoutine, around every incident
What the buyer checksCoverage, matching, price ageDelivery delay under load
Typical deliveryPolling a boardA push stream

The same markets, judged on different things.

A matched pair needs a market that holds still

A matched board pairs a bookmaker's back price with an exchange's lay price for the same selection. Before the off, both sides usually hold still long enough for that pair to mean something, and the lay side carries available, the money on offer at that price. In-play, both sides move between incidents and exchange money can vanish while a market settles. A pair that was correct when it was read may not exist by the time a user acts on it.

OddsRelay's feed covers 140+ bookmakers, 60+ of them live in the UK & Ireland, and its matched boards pair each back price with a lay price from Betfair, Smarkets and Matchbook. Add inPlay=only and the standard and dutching boards serve the events in play instead, each marked in_play. The 2Up, each-way, extra place and Best odds guaranteed boards stay pre-match.

Our view is that a supplier should sell the product its freshness can support and name the boundary. Stretching the word "live" over a pre-match board sets up a user to act on a price that no longer exists. On this feed, bet365 and William Hill have no in-play prices, and horse racing is never in play.

What a feed should guarantee about freshness

Before it launches, every product OddsRelay sells must meet a maximum update time of 10–20 s for match odds, and for every market within six hours of kick-off. That figure bounds the age of a bookmaker's prices when you receive them, measured from outside the feed. It is a fact about the data in your hand. It says nothing about how often prices are collected, and we don't publish how they are collected. Further from kick-off, prices in the other markets can be older.

The bound is the promise. The fields in each response are how you check it:

  • meta.processed_at (and the X-Processed-At header) is the board's time, on every response.
  • meta.last_seen on a matched board gives, per venue and sport, the last time it was read. Now minus that time is an upper bound on a price's age.
  • last_update on each raw market is when that market last changed, so a market nobody re-prices keeps an old time while its venue is still being read.

A price's age from meta.last_seen · example

// groupOf maps each sport_key to its key base (soccer, horse_racing), built from GET /v2/sports
const seen = meta.last_seen[offer.bookmaker]?.[groupOf[event.sport_key]];
const ageSeconds = seen ? (Date.now() - Date.parse(seen)) / 1000 : null;

// your own threshold, your own decision
const usable = ageSeconds !== null && ageSeconds < myMaxAgeSeconds;

The feed drops no pre-match row for its age. An in-play price is served only while freshly read, so one that stops being read leaves the reply instead of ageing in it. If your product needs a tighter tolerance than ours, apply it on your side with the fields above. The freshness guide covers how last_seen is keyed, and the response anatomy post walks the rest of the envelope.

Both are polled, and an unchanged board costs no tokens

In-play products usually push each change down a stream, because a poll can land between incidents. The OddsRelay feed is pull only. There is no webhook, WebSocket or stream. You poll a board and send back the ETag from your last reply as If-None-Match. An unchanged board comes back as a 304, which costs no tokens.

For pre-match work that is the right trade: you choose your own polling rate and spend tokens only when the board changed. In play the same loop works, but an in-play board changes more often, so fewer polls come back as a 304. An in-play call is priced as the same call before kick-off. The polling post covers the loop, and when push is worth its extra moving parts.

Choosing between them

Work back from the moment your user acts. If they act before the event starts, you need pre-match data, and paying for in-play buys a capability nothing in your product touches. If the whole point of the product is reacting to a goal or a break of serve, you need in-play, and a pre-match feed will not serve it.

  • Pre-match fits an oddsmatcher, a matched-betting tool, an odds comparison page, and preview or tipping content. Every price they show is a price from before the off.
  • In-play is required for in-play trading, in-play hedging and any tool built around score changes. On OddsRelay that means raw with inPlay=include or only, or the standard and dutching boards with inPlay=only. The scores themselves come from another kind of API (odds API vs sports data API).
  • Mixed products can take both from one call: inPlay=include on raw serves pre-match and in-play events together, and an event keeps its id across kick-off, so a screen can follow it from one to the other.

A pre-match build can keep the default, inPlay=exclude, which leaves out every event its venues show in play. Hide any row whose commence_time has passed as well: once the event starts, its pre-match price is history. The matched boards also take a kick-off window (commenceTimeFrom and commenceTimeTo). The matched-board filters guide lists the rest.

See what the feed covers

The coverage page shows each bookmaker and sport the feed carries now. The in-play guide sets out what each inPlay value returns, and the quickstart walks your first request.

Related posts

18+ · OddsRelay supplies odds data only and takes no bets. Please gamble responsibly.