BlogOdds API basics

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, Draw and the away side.
  • Over/under goals (totals): an over and an under on a line, with the line itself in the outcome's point field. 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_score): one outcome per scoreline, so it is the widest of the common markets.
  • 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 /v2/sports 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_market with the valid keys listed.

Every competition has a competition key

Each football event carries two keys: sport_key and competition_key. sport_key starts soccer_ and follows the league name its venues give it, with the readable name in sport_title. Venues name leagues their own way, so one competition's matches can carry different sport_key values. competition_key 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=soccer asks for every football competition on the board, and an event's competition_key returns its whole competition. GET /v2/sports 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_seen, 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

ProductWhat it carries for football
standardBookmaker back prices beside exchange lay prices, market by market.
2upEach bookmaker's own 2Up price on the match result, paired to the exchange lay for the same team.
dutchingBack prices only, one leg per outcome at different venues, grouped by dutch_id.
rawEach venue's markets and outcomes as that venue labels them, with a last_update 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=soccer&markets=h2h and trimmed to the match result, with sport_key, sport_title and competition_key left out (GET /v2/sports lists the keys):

GET /v2/odds/standard (trimmed) · example

{
  "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_seen entry is an upper bound on it, and processed_at 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:

Your arithmetic on the Arsenal pair above · example

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_id 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 /v2/sports lists every football competition on the boards now, and keyless GET /v2/coverage 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=only on the standard and dutching boards, or include and only on raw, returns football in play, each event marked in_play, 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=true 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

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