A feed gives you current prices; the history is what you persist from them. Here's a simple, durable model: timestamped snapshots keyed by event, market and selection, plus the retention trade-offs to decide up front.
James··6 min read
Store odds data as timestamped snapshots keyed by event, market and selection, so you can reconstruct what any price was at any moment. A feed hands you the current state; the history is something you build by writing each observation down and never throwing the old one away. That history is what results tracking, closing line value and line-movement analysis all depend on. The single most important decision is to make it before you write your first row: how granular the snapshots are, and how long you keep them.
What does it mean to store odds data?
It means recording each price as it was at a specific instant, not just the latest value. An odds feed answers one question well: what is the price right now? A stored history answers a different one: what was the price then? To get from the first to the second, you append a new row every time you read the feed, stamped with the moment you read it. The current price is simply the most recent snapshot; every earlier snapshot is your record of the past.
The distinction matters because a price is not a fact that holds still. A bet365 match_odds selection can drift from 2.10 to 1.95 over an afternoon. If you only ever hold the latest number, you have the destination and none of the journey. The journey is where the analysis lives.
What does a simple storage model look like?
One append-only table of snapshot rows covers most needs. Each row identifies a selection with a stable key, carries the prices you care about, and records when you observed them. Nothing is ever updated in place; a new price is a new row.
The stable key is the part worth getting right early. Identify a selection by its event, market and selection together, not by a display string that changes. A good composite key survives a bookmaker renaming a team or reordering a market, and it lets you join every snapshot of one selection into a clean time series. Here is the shape of a single stored snapshot row (illustrative, not live data):
Notice that the row keeps the whole matched shape, not just the back price. Persisting the paired lay block, the rating and the qualifying_loss alongside back means you can later ask how a full opportunity moved, not merely how one bookmaker's number moved. The fields mirror the live response envelope, so storing is mostly a matter of adding observed_at and inserting. Store what you already receive; do not reshape it.
Why do timestamped snapshots beat overwriting?
Because the questions you will want to answer are all questions about time, and you cannot ask them of data you have already discarded. Overwriting the latest price is cheap today and expensive the first time someone needs the history you never kept. Three common needs all require the snapshots:
Results tracking: to show what a user could have got, you need the price as it stood when they saw it, not the price now. That is a stored snapshot, retrieved by timestamp.
Closing line value: comparing the price you took against the price at kick-off needs both points on record. The closing line is just the last snapshot before the event started.
Line-movement analysis: the shape of a drift, the speed of a move, the moment a market turned, all come from a series of snapshots read in order. We go deeper on reading those moves in line movement.
None of these are exotic. They are the ordinary things a matched-betting, arbitrage or odds-comparison product is asked to do once it has real users. Every one of them is trivial with snapshots and impossible without them.
How granular should snapshots be, and how long should you keep them?
Decide both up front, because they set your storage cost and your analytical ceiling together. Granularity is how often you write a row; retention is how long you keep it. Neither is free, and the sensible answer is rarely the maximum of both.
On granularity, your ceiling is the feed cadence: our posture is pre-match polling on roughly a few-second cycle, so you cannot record change faster than you observe it. You can, however, record it slower. Storing every read gives the finest history and the largest table; storing on a fixed interval, or only when a price actually changes, keeps the signal and drops the noise. Change-only writes are a strong default: an unchanged price adds no information, and skipping it shrinks the table sharply without losing a single move. Reading the feed on a sensible cadence in the first place is covered in polling efficiently.
On retention, match the horizon to the use. A rough guide:
Need
Granularity
Retention
Results tracking
Change-only or per-read
Long: users query weeks or months back
Closing line value
Denser near event start
Keep the closing snapshot indefinitely; thin the rest
Line-movement analysis
Per-read for fidelity
Medium: enough history to model typical moves
Live display only
Latest snapshot suffices
Short: history is optional
Granularity and retention follow the question you're answering, not a single global setting.
A common pattern is tiered retention: keep full-resolution snapshots for recent events, then downsample older ones to a coarser interval or to just the opening and closing prices. You keep the analytical value of history without the table growing without bound. Whatever you choose, choose it deliberately, because backfilling detail you never stored is impossible.
Where the feed ends and your storage begins
Be clear about the division of labour: the feed delivers current, matched prices; the history is yours to persist. OddsRelay covers 60+ UK books with bet365 included, each back price matched against three exchanges (Betfair, Smarkets and Matchbook) for the lay side, and delivers that as the current state on each read. What it does not do is remember, on your behalf, what last Tuesday looked like. That is by design, and it is why storage is a decision you own.
The good news is that persisting it is simple once the model is right. Each read returns rows in a predictable shape; you stamp them with observed_at and append. Because the matched structure is already computed, your stored history carries the rating and qualifying_loss too, so your archive is as analysable as your live view. The full field list is in the API docs.
The short version
Write each observation as a timestamped snapshot keyed by event, market and selection, never overwrite, and decide granularity and retention before you start. That gives you results tracking, closing line value and line-movement analysis for the cost of an INSERT. The feed keeps the current picture accurate and maintained; you keep the history. If you want to see the shape of the data you would be storing, a free trial gives you the full UK feed, bet365 included, matched against exchange lay prices, and you can check what is live right now on the coverage dashboard before you commit.
James is the founder of OddsRelay — the odds-data feed behind matched betting, arbitrage and odds-comparison products: 60+ UK bookmakers with bet365 included, matched against exchange lay prices and delivered as one clean, documented API. He writes here about how that data layer actually behaves — coverage, matching, freshness and the trade-offs — from the side that builds and runs it. The same feed powers a leading UK matched-betting platform today.