# Pairing back and lay odds across UK bookmakers

> A matched odds outcome holds a list of back offers and a list of lays. How an arbitrage tool turns them into rows with each user's books, stake and commission.

Published 31 October 2026

**To pair back and lay odds across UK bookmakers, take one outcome from a matched board, keep the back offers from the books your user holds, and give each one the lowest lay whose liquidity covers the lay stake.** The board has already agreed the fixture, market and line. What it can't choose is the pair, because that depends on your user.

This post is for teams building [arbitrage](https://oddsrelay.io/glossary/arbitrage) and trading tools, who already know a [back bet](https://oddsrelay.io/glossary/back-bet) from a [lay price](https://oddsrelay.io/glossary/lay-odds). [Matching bookmaker and exchange odds](https://oddsrelay.io/blog/matching-bookmaker-and-exchange-odds) covers whether two prices are the same bet. This post starts one step later, with which of those prices share a row in your tool.

## How do you pair a bookmaker back price with an exchange lay price?

Take both from the same outcome, because each outcome on OddsRelay's standard board carries a `back` list and a `lay` list. On a line market, each `point` has its own outcome, so `Over` at 2.5 goals never shares a list with `Over` at 3.5. Three back offers and two lay offers make six possible pairs:

GET /v2/odds/standard (one h2h outcome) · example:

```json
{
  "name": "Arsenal",
  "back": [
    { "bookmaker": "sky_bet", "price": 2.95, "link": null },
    { "bookmaker": "william_hill", "price": 2.9, "link": null },
    { "bookmaker": "paddy_power", "price": 2.88, "link": null }
  ],
  "lay": [
    { "exchange": "matchbook", "price": 2.94, "available": 38.5, "link": null },
    { "exchange": "smarkets", "price": 2.98, "available": 412, "link": null }
  ]
}
```

`back` comes best price first and `lay` lowest price first, so `back[0]` and `lay[0]` look like the answer. Often they're the wrong pair. Here, a user with no Sky Bet account can't take the first back offer, and a back stake of 100 asks more than the 38.5 behind the first lay can take.

## Which lay do you pair when several exchanges price the same outcome?

Pair the lowest lay that can take the lay stake, and drop the back offer if none can. The board draws its lays from Betfair, Smarkets and Matchbook, and every lay offer carries `available`. The list is already in price order, so the first offer that fits is the one you want. The lay stake changes with the back price and each exchange's commission, so work it out per offer:

Your code: rows from one outcome · example:

```ts
// Customer-side code. The books, stake and commission rates are your user's own.
// One rate per exchange: a missing rate gives NaN and skips that offer.
const books = new Set(["william_hill", "paddy_power"]);
const commission = { smarkets: 0.02, matchbook: 0.02 };
const backStake = 100;

const layStake = (back, lay) =>
  (backStake * back.price) / (lay.price - commission[lay.exchange]);

const rows = outcome.back
  .filter((back) => books.has(back.bookmaker))
  .flatMap((back) => {
    const lay = outcome.lay.find(
      (l) => l.available !== null && l.available >= layStake(back, l),
    );
    return lay ? [{ back, lay, layStake: layStake(back, lay) }] : [];
  });
```

Run that over the outcome above and the `books` check drops Sky Bet, the liquidity check drops Matchbook's lay, and two rows are left:

| Back offer | Lay stake on Matchbook | Pairs with | Lay stake there |
| --- | --- | --- | --- |
| William Hill at 2.9 | 99.32, too much for its liquidity | Smarkets at 2.98 | 97.97 |
| Paddy Power at 2.88 | 98.63, too much for its liquidity | Smarkets at 2.98 | 97.30 |

Drop the back stake to 25 and both rows move to Matchbook, because a lay stake near 25 fits inside Matchbook's 38.5, and Matchbook's price is lower. Nothing in the reply changed, which is why these rows belong in your code and not in a cache of the board. [Why lay liquidity matters](https://oddsrelay.io/blog/why-lay-liquidity-matters) sets out what a scanner can do with a short lay besides dropping it.

Commission stays in your code too, because each user pays their own rate and that rate can change which offer fits.

## One best row hides the rest

Keep a row for every back offer that passes, instead of one best row per outcome. The best price across the market tells a user little if the next-best is the one at a book they can still bet with. Rows also make your screen easy to filter, and when a user loses an account, that book drops out of `books` with no change to anything else.

The `bookmakers` and `exchanges` filters narrow the books on the way in, and on the standard board a filtered call costs less. They still can't replace the check in your code when one reply serves users who hold different accounts. A filter also drops any outcome left with no back offer or no lay offer, as the [matched-board filters guide](https://oddsrelay.io/docs/guides/matched-board-filters) explains.

## What does a scanner do with a paired row?

A scanner runs its own sums on each row and decides what to show. The board flags nothing and computes nothing, so each of these is your code:

- The crossing check. Whether the back price beats the lay once commission comes off. [Arbitrage data](https://oddsrelay.io/blog/betting-arbitrage-data) has the line of code, and neither row above crosses.
- The pair's age. `meta.last_seen` gives the last read of each venue and sport. A row is at most as old as the older of its two reads, so check both before an alert goes out.
- The way to each leg. Each offer's `link` opens that venue's page, the event's own where we have one and otherwise a sport or home page, and it can be `null`, so check it before you send a user there.
- The back side's limit. Back offers carry no size. What a bookmaker accepts is set per account, so only your user's own history can tell you.

A value tool swaps the crossing check for a fair price of its own. [Value betting vs arbitrage](https://oddsrelay.io/blog/value-betting-vs-arbitrage-data) sets the two side by side.

## Pair one market on your users' books

Price a call for one sport and market with `quote=true`, which costs nothing, then run the loop above with your users' books, stakes and rates. Count how many rows survive at each stake. [Odds data for trading tools](https://oddsrelay.io/for/trading-tools) sets out the calls. A scanner other people use runs on [Enterprise](https://oddsrelay.io/enterprise), and so does one a business keeps for itself.

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

Source: https://oddsrelay.io/blog/pairing-back-and-lay-odds
