BlogBuilding with odds data

How to normalise odds across bookmakers

To normalise odds across bookmakers, you give one fixture, one market and one selection a single identity, then map every book's own labels onto it.

Until that is done, two prices for the same outcome cannot sit side by side, and every comparison or oddsmatcher built on them is guessing.

Why bookmakers' odds don't line up

Every book models the same match with its own names, IDs and market structures. The prices are the easy part: convert fractional or American odds to decimal (odds formats) and they compare cleanly. The layers around them drift independently between books.

  • Event names differ. One book lists Man Utd v Man City, another Manchester United vs Manchester City, a third Man. United - Man. City. Kick-off times can be a minute apart, and abbreviations, punctuation and word order all vary.
  • Selection names differ. The home team, the draw and Over 2.5 Goals each appear under slightly different labels. Draw against Tie, or a player's name spelled two ways, is enough to break a naive join.
  • Market structures differ. One book's match odds market is another's three-way 1X2. Totals lines, handicap notation and each-way terms are all modelled differently, and the same market can be split at different line values from book to book.
  • IDs are private. Every book has its own event and selection IDs, and no two share a scheme. There is no common key to join on.

For how a single response is laid out before any of this begins, see the anatomy of an odds API response.

Three matching problems, stacked

That single identity is the join key no book gives you, and you build it top down. Each layer rests on the one above it: a market match is meaningless if the events were wrongly joined.

  1. Event matching. Decide that book A's Man Utd v Man City and book B's Manchester United vs Manchester City are the same fixture, using team identity, competition and kick-off time together. In practice that means cleaning names first (case, punctuation, Utd to United), keeping an alias table per book for what cleaning cannot fix, and accepting a kick-off within a small window.
  2. Market matching. Decide that book A's match odds and book B's 1X2 are the same market, and align totals and handicap lines so the same line compares to the same line.
  3. Selection matching. Decide that the home-win price in each maps to the same canonical selection, and that Over 2.5 from one book pairs with Over 2.5 from the other, never with Under.

The output looks simple: every price carries a stable event, market and selection you can group on. That grouping is what a comparison table or an oddsmatcher is built on, and it is the core of how comparison sites use data.

False matches cost more than missed ones

A missed match costs you a row you never see. A false match shows your users a price comparison that is wrong, and your product acts on it. Tune for precision, and let a doubtful pair go unmatched.

A false match looks exactly like a good price

When two prices are wrongly aligned, the gap between them reads as a standout best price or an unusually close back and lay. Loose fuzzy matching produces these constantly.

The first pitfall is false positives: aligning two selections that are not the same. A same-name player in two different fixtures, a totals line read at the wrong value, or a two-way market matched against a three-way one all yield a comparison that is arithmetically valid and factually wrong.

The second is near-duplicate events: one fixture appearing twice with slightly different metadata, or two different fixtures that look almost identical, such as a first-team match and a reserve match. Merge the wrong pair and unrelated prices share a row. Split the right pair and one match is scattered across two IDs, so neither half shows the full market.

Neither is a one-off bug. Fixtures, team names and market layouts change all the time, so matching logic needs continuous maintenance to stay correct. That maintenance is the recurring cost of normalising in-house.

What a feed can join for you

How much it joins depends on the product. In OddsRelay's feed, one fixture is one id on every route: event_id on the six matched boards and id on raw carry the same value, whichever books price the match. The matched boards go further and join every layer. On standard, each outcome of a market lists the bookmakers' back prices with the exchange lay prices beside them.

A matched outcome with its back and lay offers · a single event from GET /v2/odds/standard, trimmed · example

{
  "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": "william_hill", "price": 2.9, "link": null },
        { "bookmaker": "paddy_power", "price": 2.88, "link": null }
      ],
      "lay": [
        { "exchange": "smarkets", "price": 3.0, "available": 175, "link": null }
      ]
    }]
  }]
}

Comparing William Hill to Paddy Power here is reading two entries in one array, and the Smarkets lay offer beside them carries available, the liquidity on offer at that price. Nothing in the reply is derived: a rating from this outcome is your own sum on the best back price and the lay, and the oddsmatcher widget does the same sum on screen. Written out:

Customer-side arithmetic on the example above · example

const outcome = event.markets[0].outcomes[0];
const best = outcome.back.reduce((a, b) => (b.price > a.price ? b : a));
const lay = outcome.lay[0];
const rating = (best.price / lay.price) * 100; // 96.7 before commission

Raw joins less. Each fixture is still one event with every book under its bookmakers list, but each book's markets and outcomes arrive as that book labels them. The event layer is done for you. The market and selection layers stay yours, which is the right trade if you want each book's markets exactly as it prices them. Walking raw covers paging through it.

One note about the ID. An event_id is opaque, one per fixture, the same on every board, and never changed once served, so it is a key you can store. If two ids turn out to be one fixture, the feed keeps the one it served first and lists the other in that event's previous_ids, and the other still answers on the event route. Storing and versioning odds data shows how an archive folds the two together.

See the joined rows yourself

The feed covers 140+ bookmakers, 60+ of them live in the UK & Ireland, and the matched boards pair back prices against Betfair, Smarkets and Matchbook. See coverage for every bookmaker and exchange on each feed, and the API reference for the shapes your join has to fit.

Related posts

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