# Switching odds data providers without downtime

> Switch odds data providers by running the new feed in parallel: map fields behind one adapter, diff coverage and freshness on your markets, then cut over.

Published 1 July 2026 · updated 5 November 2026

**To switch odds data providers without downtime, run the new feed beside the old one before you commit: map it into the shape you already consume, shadow it on your own markets, and cut over only when the diff holds up.**

Users see no downtime this way, because the old feed keeps serving until the new one has earned the traffic. If you are still shortlisting, [how to choose an odds data provider](https://oddsrelay.io/guides/how-to-choose-an-odds-data-provider) comes first, and [testing a feed before you buy](https://oddsrelay.io/blog/how-to-test-an-odds-feed-before-you-buy) covers the acceptance run. A migration is that acceptance run with your existing feed as the benchmark.

## Step 1: map the new feed's fields to yours

Write the mapping down before you write code. Your ingestion layer reads a fixed set of keys today, and the migration is easy or painful depending on how cleanly the new feed lines up with them. Put every provider-specific name in one adapter and let no other code know who supplies the data.

On an OddsRelay matched board, each event carries its teams and kick-off, then markets, then outcomes. Each outcome has `back` offers (a bookmaker's price) and `lay` offers (an exchange's price with `available`, the money on offer). The response wraps the rows in a `meta` envelope with the board time. Here is a trimmed example from `GET /v2/odds/standard`:

A standard matched board (trimmed) · example:

```json
{
  "meta": {
    "region": "uk",
    "odds_format": "decimal",
    "processed_at": "2026-09-17T02:20:57.271Z",
    "last_seen": { "william_hill": { "soccer": "2026-09-17T02:20:55Z" } },
    "count": 1,
    "version": "v2",
    "next_cursor": null
  },
  "data": [{
    "event_id": "or_evt_917dd44bce05",
    "sport_key": "soccer_premier_league",
    "sport_title": "Premier League",
    "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 }],
        "lay":  [{ "exchange": "smarkets", "price": 3.0, "available": 175, "link": null }]
      }]
    }]
  }]
}
```

| Field to map | What to check when you map it |
| --- | --- |
| `event_id` | Treat it as opaque. It never changes once served, so it can key your history tables. An id merged into another is listed in that event's `previous_ids` and still answers. |
| `commence_time`, `home_team`, `away_team` | These are what you join the two feeds on during the parallel run. Team names differ between providers, so normalise them first. You can narrow the join to a sport family, such as the `soccer` prefix, or to one competition with `competition_key`, but do not join on `sport_key`. It follows the league names the venues use, so one competition's matches can carry different ones. |
| `back[].bookmaker`, `lay[].exchange` | Snake_case venue slugs from `GET /v2/bookmakers`. Map each one to your own venue list once. |
| `lay[].available` | How much can be laid at that price. If your old feed had no liquidity figure, decide how your product will show it. |
| `meta.processed_at`, `meta.last_seen` | The board time and the last read per venue and sport. These replace whatever freshness field your old feed sent. |

The mapping for a matched board. The API reference lists every other field.

Two differences catch teams moving from a ratings-style feed. First, the feed sends the back and lay prices and the `available` money, and no derived figures: the rating and qualifying loss are yours to work out at your own commission rate, as the [oddsmatcher widget](https://oddsrelay.io/widgets/oddsmatcher) does. If your old provider sent a rating field, your adapter now computes it, for example `const rating = (outcome.back[0].price / outcome.lay[0].price) * 100`. Second, event ids from two providers never line up, so keep your own event key and a crosswalk table that records which provider id belongs to it.

## Step 2: run the new feed in parallel

Point the new feed at the markets you actually serve and write its output to a shadow table your users never see. A shadow run spends tokens like any other traffic, so keep it to those markets: [what drives the cost of an odds feed](https://oddsrelay.io/blog/what-drives-the-cost-of-an-odds-feed) shows where they go.

- **Same events, both feeds.** Request the sports and bookmakers you sell, filtered the way production will be. A feed can look complete on football and thin on the markets you depend on.
- **Polling.** OddsRelay is pull only, with no webhook or stream. If your old provider pushed updates, the adapter needs a poller. Send each reply's `ETag` back as `If-None-Match`: an unchanged board returns a `304` and costs no tokens.
- **Token cost.** A live-feed call's token price comes from the request: the board and its filters. Reply size does not change it. Add `?quote=true` to price a call without spending. The [filters and tokens guide](https://oddsrelay.io/docs/guides/filters-and-tokens) explains it, and [pricing](https://oddsrelay.io/pricing) lists each plan's tokens.
- **Peak-hour window.** A quiet afternoon tells you little. Freshness and completeness are hardest to hold when the markets are busiest.

A few integration details bite on day one. The key goes in `Authorization: Bearer <key>` or `x-api-key`. Send `region=uk` on every call. Make sure your HTTP client sends `Accept-Encoding: gzip`: without it replies come back as much larger plain bodies, and a client that explicitly refuses gzip gets a `400` from the standard, dutching and raw boards. The [quickstart](https://oddsrelay.io/docs/guides/quickstart) walks through a first call.

## Step 3: diff coverage and freshness on your real markets

This is the decision gate. Join the shadow rows to your live rows on your own event key and answer one question: does the new feed match or beat what you have today, on the books and markets you sell? [Evaluating coverage](https://oddsrelay.io/blog/how-to-test-an-odds-feed-before-you-buy) goes deeper. For a migration, these are the checks that matter.

- **Book breadth.** The new feed should carry every book you rely on, and ideally close a gap. OddsRelay lists 140+ bookmakers, 60+ of them live in the UK & Ireland, with bet365 on every plan for featured football and UK and Irish racing. Check the coverage, per product, venue and sport, on [the coverage page](https://oddsrelay.io/coverage), or the live status with a keyless `GET /v2/coverage`, before you ask for a key.
- **The lay side.** Each back price should sit beside an exchange lay price and the money available at it. OddsRelay's lay side comes from Betfair, Smarkets and Matchbook.
- **Freshness.** For each venue and sport, now minus `meta.last_seen` is an upper bound on a price's age. On the raw board, each market's `last_update` is when that market last changed, so a quiet market can carry an old time and still be current. Compare like with like on the same events. The [freshness guide](https://oddsrelay.io/docs/guides/freshness) defines each field, and every product on sale meets a maximum update time of 10–20 s for match odds, and for every market within six hours of kick-off. Further from kick-off, prices in the other markets can be older.
- **Completeness across the run.** Count the selections that dropped out when a source hiccupped. Each market should stay whole for the whole window.
- **In-play.** Since 2 November 2026 the feed serves in-play prices too, with `inPlay` on raw and on the standard and dutching boards, polled like the rest. It carries no scores or match clock, and bet365 and William Hill have no in-play prices, so diff your in-play markets book by book before you move them across.

## Step 4: cut over behind a flag

With the adapter written and the parallel run green, the cutover is a config change.

1. Point production ingestion at the new feed through the adapter from step one.
2. Keep the old provider warm briefly, so rollback is a flag flip rather than a re-integration. A clean parallel run can still miss a case that only shows at peak.
3. Retire the old feed when that window closes, and update your runbook and your crosswalk table.

## Start with what is live

Shadow your current feed with the new one on the bookmakers you sell, and keep the [API reference](https://oddsrelay.io/docs/reference) open while you write the adapter.

Serving your users from the new feed runs on [Enterprise](https://oddsrelay.io/enterprise), and the shadow run can start on your own Free key.

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

Source: https://oddsrelay.io/blog/how-to-switch-odds-data-providers
