Football odds data, explained
Football odds data is every bookmaker's price for every outcome of a fixture, filed under a sport key, a market key and an outcome name.
In the OddsRelay feed, football sits under soccer_ sport keys, and the matched boards set each back price beside the exchange lay for the same outcome.
What are the main football betting markets?
The main football betting markets are the match result, over/under goals, both teams to score and correct score. A single fixture carries dozens of markets, and they differ in shape. Some have two outcomes, some three, some one per scoreline. The common ones:
- Match result (
h2h): three outcomes, the home side,Drawand the away side. - Over/under goals (
totals): an over and an under on a line, with the line itself in the outcome'spointfield. One venue often prices several lines on the same fixture, so the line is part of the outcome's identity. - Both teams to score: two outcomes, yes and no.
- Correct score (
correct_): one outcome per scoreline, so it is the widest of the common markets.score - The long tail: draw no bet, double chance, half-time/full-time and the Asian handicap lines.
Don't hardcode that list. Every row of GET / carries markets, the market keys seen under that competition on the matched boards, and the markets filter takes the same keys. Name a key the catalogue doesn't know and the reply is a free 400 unknown_ with the valid keys listed.
Every competition has a competition key
Each football event carries two keys: sport_ and competition_. sport_ starts soccer_ and follows the league name its venues give it, with the readable name in sport_. Venues name leagues their own way, so one competition's matches can carry different sport_ values. competition_ names the competition itself, also under soccer_: one key per competition, the same on every live board and never changed once served. On the live boards it is null only when no venue's league name identifies one competition. Past boards in historical odds always carry it as null.
The sports filter takes either key, or a prefix: sports= asks for every football competition on the board, and an event's competition_ returns its whole competition. GET / lists both. The matched-board filters guide covers the rest of the grammar.
One place uses the prefix instead of the full key. meta.last_, the time each venue's prices were last read, is keyed by the sport's key base, so every football competition shares the soccer entry. The freshness guide shows the lookup.
Where football sits on the boards
| Product | What it carries for football |
|---|---|
standard | Bookmaker back prices beside exchange lay prices, market by market. |
2up | Each bookmaker's own 2Up price on the match result, paired to the exchange lay for the same team. |
dutching | Back prices only, one leg per outcome at different venues, grouped by dutch_. |
raw | Each venue's markets and outcomes as that venue labels them, with a last_ per market. |
All six matched boards and raw are on every plan, so one key reads every board in the table above. On a self-serve plan the same key reads historical odds too: the bookmakers' and exchanges' past football prices, and the bookmakers' closing lines, since 17 October 2026. The 2Up board is the one built for football alone: its back side is the bookmaker's 2Up price, which can differ from the same bookmaker's ordinary match odds. The dutching walkthrough covers that board.
What a matched football row looks like
Here is one Premier League fixture on the standard board, asked for with sports= and trimmed to the match result, with sport_, sport_ and competition_ left out (GET / lists the keys):
{
"meta": {
"feed_type": "standard",
"region": "uk",
"odds_format": "decimal",
"processed_at": "2026-09-20T12:20:57.271Z",
"last_seen": {
"bet365": { "soccer": "2026-09-20T12:20:51Z" },
"william_hill": { "soccer": "2026-09-20T12:20:55Z" },
"matchbook": { "soccer": "2026-09-20T12:20:56Z" },
"smarkets": { "soccer": "2026-09-20T12:20:54Z" }
},
"count": 1,
"version": "v2",
"next_cursor": null
},
"data": [{
"event_id": "or_evt_917dd44bce05",
"commence_time": "2026-09-20T14:00:00Z",
"home_team": "Arsenal",
"away_team": "Chelsea",
"markets": [{
"key": "h2h",
"outcomes": [
{
"name": "Arsenal",
"back": [
{ "bookmaker": "bet365", "price": 2.9, "link": null },
{ "bookmaker": "william_hill", "price": 2.8, "link": null }
],
"lay": [
{ "exchange": "matchbook", "price": 3.0, "available": 175, "link": null }
]
},
{
"name": "Draw",
"back": [{ "bookmaker": "william_hill", "price": 3.5, "link": null }],
"lay": [{ "exchange": "smarkets", "price": 3.6, "available": 42, "link": null }]
}
]
}]
}]
}Each outcome carries back offers, best price first, and lay offers, lowest first. A lay offer's available is the money on offer at that price. No offer is timestamped on its own, so read its age from the envelope: now minus the venue's meta.last_ entry is an upper bound on it, and processed_ is the board's time.
A back price reaches the board only when an exchange lay beside it has money behind it. The oddsmatcher widget works out the rating and the qualifying loss on screen. In your own code it takes a few lines, at your own commission rate:
const back = 2.9, lay = 3.0, commission = 0.02, stake = 10; const layStake = (back * stake) / (lay - commission); // 9.73 const ifBackWins = stake * (back - 1) - layStake * (lay - 1); // -0.46 const ifLayWins = layStake * (1 - commission) - stake; // -0.46, the qualifying loss const rating = (back / lay) * 100; // 96.7
The ratings explainer walks the formulas, including free-bet stakes.
Pairing is the hard part of football data
Prices are only half of a football feed. The other half is deciding that two venues are quoting the same fixture and the same outcome, when bookmakers spell teams their own way. One venue lists a club by its short name, another by its full name, and a third adds a suffix, on every fixture of every round. A football feed for matched betting should do that reconciliation once, for everyone, because every customer who does it alone rebuilds the same alias tables.
On the matched boards one fixture is one event with one event_ on every route. Treat that id as opaque. It never changes once served, so you can store it as the fixture's key. Raw keeps each venue's own outcome labels, which is the right trade when you want to run the matching yourself. What an odds API returns sets raw and matched side by side. The lay side matters as much as the pairing, since a lay with little money behind it cannot hedge a real stake. Why lay liquidity matters covers that part.
Which football the feed covers
The feed serves one region, the UK & Ireland: 140+ bookmakers, 60+ of them live in the UK & Ireland, bet365 included, with the lay side from Betfair, Smarkets and Matchbook. GET / lists every football competition on the boards now, and keyless GET / gives the live status per product, venue and sport.
The prices are pre-match unless you ask for in-play ones. Since 2 November 2026, inPlay= on the standard and dutching boards, or include and only on raw, returns football in play, each event marked in_, with no scores or match clock. Fewer bookmakers price football in play than before kick-off, and neither bet365 nor William Hill has in-play prices on the feed. Live vs pre-match odds data sets out the difference.
Price one competition first
Pick one competition key and one market, and add quote= to the standard board call to see its token price before you spend any. The quickstart walks that call, and the coverage page lists the bookmakers and sports the feed covers.
Related posts
- The anatomy of an odds API responseEvery field in a /v2 odds response, from the meta envelope down to each back and lay offer.
- Why lay liquidity matters in a matched odds feedA lay price with no money behind it cannot be taken. How to check a pair against your stake.
- Dutching data explainedThe stake arithmetic behind dutching, and a feed that sends the legs already grouped.
18+ · OddsRelay supplies odds data only and takes no bets. Please gamble responsibly.