Historical data · Practical guide
Historical betting odds: audit the data before the backtest
A historical price is useful only if you know when it was observed, when it reached your system and which market it describes. A backtest can look better simply because it uses information that arrived too late.
Vultax Research Team · 8 min read
Published · Sources checked
Working on a decision like this?
Check a dataset before using its backtest →
Choose between snapshots, closing lines and a quote path
Start with the documented odds API comparison for sources and historical windows. A feed of event closing prices, periodic odds snapshots and a complete sequence of source updates support different tests. Ask for the product you need, not simply 'historical NBA odds'.
This page provides two different worked examples: a real provider-published historical snapshot audited below, and an invented two-record timing fixture. It also includes blank working files. Vultax does not sell a comprehensive historical sportsbook database. Obtain any further source data under terms that cover your intended use.
Reproducible audit: a provider-published EPL snapshot
We downloaded The Odds API’s published historical EPL example on 30 October 2026 and ran the downloadable audit script. Its snapshot timestamp is 29 October 2021 at 23:55 UTC. This is an independent demonstration on a provider example, with no client commission, provider endorsement or live-feed benchmark.
The file contains 20 events, 13 distinct bookmaker keys and 240 bookmaker-event records. Across those records are 270 markets; this audit’s completeness, source-age and overround calculations cover the 240 head-to-head markets and their 720 outcome rows. The test requires exactly the event’s home team, away team and Draw, with unique outcome names and finite decimal odds above 1.
| Check | Result in the supplied example | Meaning and limit |
|---|---|---|
| Event identifiers | 20 present; no duplicates detected. | Tests identifiers inside this file; no independent event universe was supplied. |
| Three-way head-to-head markets | 240 of 240 contain the expected home, away and draw outcomes. | Completeness inside supplied markets; not evidence that every expected market or event was collected. |
| Decimal odds | No invalid prices detected in 720 h2h outcome rows. | A syntactic price check; not verification against the bookmaker or evidence of execution. |
| Source timestamps | No missing h2h source times or source times after the snapshot. | Use market.last_update when present, otherwise bookmaker.last_update. |
| Source age at the snapshot | Median 7.29 minutes; 95th percentile 11.98 minutes. | Snapshot time minus source-update time, not network latency or a present-day service measure. |
| Three-way overround | Median 4.80 percentage points. | Sum of the three implied probabilities minus one, in percentage points; not realised bookmaker profit. |
- Reproduction: download the source JSON from the provider and the audit script below, then run: node odds-sample-audit.mjs historical-epl.json. The script reads a local file, makes no network request and writes only aggregate findings to standard output.
- Match the input SHA-256 in the downloadable results before expecting the same figures. A provider can revise the example in place; a different hash is a different input.
- The 30-minute flag in the code is an illustrative review threshold. It is not a provider commitment or an agreed client acceptance criterion.
- One snapshot supplies no season-wide expected event list, quote path, original delivery logs, betting-account eligibility or execution evidence. The clean checks do not establish those properties.
node odds-sample-audit.mjs historical-epl.json > my-audit.jsonKeep three clocks with every quote
Retain the provider snapshot timestamp, the source or bookmaker update time when available, and the time your own system received the record. Store the requested time too: the timestamp you asked for can differ from the snapshot returned. The Odds API's historical documentation explicitly returns the closest snapshot at or before the requested time.
For a replay of your own system, a record must have arrived by the decision cutoff. For a historical reconstruction from a vendor archive, today's download time is not a claim that your model possessed the record then. State the archive-availability assumption and distinguish it from measured delivery. Never replace a missing timestamp with an event start time.
- Use UTC with an explicit timezone and retain original precision.
- Preserve missing source times as missing; report their frequency.
- Record event start-time changes, suspended prices, corrections and ingestion failures.
- Keep the raw response hash and the transformation version so a derived row can be traced.
Worked example: a quote that arrived five seconds too late
All event names, prices and timestamps in this example are invented. The decision cutoff is 18:00:00 UTC on 1 October 2026. Both records refer to a fictional full-game, two-way moneyline market with overtime included. A separate away outcome would still be required to calculate the whole market's overround.
Record B has the fresher source timestamp, but it reaches the replayed system after the cutoff. A nearest-timestamp join that chooses B gives the model information it did not yet possess. Under the stated replay rule, A is eligible and B is excluded. A's eligibility does not establish that a bet was available at that price.
| Record | Source updated (UTC) | Received (UTC) | Decimal odds | At the decision cutoff |
|---|---|---|---|---|
| A | 17:59:40 | 17:59:50 | 1.91 | Eligible by arrival time; 20-second source age. |
| B | 17:59:55 | 18:00:05 | 2.05 | Exclude: received 5 seconds after the cutoff. |
Define an as-of join that cannot look ahead
First restrict the candidate rows to the exact event, bookmaker, market, line, period and settlement convention. Apply the availability cutoff before choosing a quote. For this replay example, require received_at ≤ decision_at and source_updated_at ≤ decision_at, then select the newest eligible source observation. Break ties deterministically and retain the chosen row's identifier.
Set a maximum source age before evaluating a strategy. If the latest eligible quote is too old, mark the decision missing instead of reaching forward to the next snapshot. Audit the missing decisions as well as the filled ones. A vendor archive may need a different availability rule; document it and test the effect of plausible delay assumptions.
Audit coverage and settlement before performance
Build an expected event universe independently of which rows happen to survive the download. Report the events and bookmaker-market combinations present, missing, duplicated and corrected. A dataset restricted to successfully matched final results can conceal failed collections, postponed games and settlement disputes.
Keep full-game and regulation-only prices separate. Totals and spreads need their numerical line; player props need player identity and participation rules. Record pushes, voids and dead heats explicitly. Compare outcome definitions before comparing model results across providers.
- Count expected versus observed events for each league, season and provider.
- Inspect duplicate identifiers and cross-provider event matches manually on a sample.
- Verify whether each outcome and line existed at the same observation time.
- Reconcile final outcomes, refunds and voids without deleting unresolved records.
- Calculate costs, stake limits and execution assumptions separately from quoted-price returns.
Keep the audit receipt beside the result
Save the extraction request, download date, provider terms, raw-file hashes, parsing version, expected universe and exclusion counts. Include a small sample of accepted and rejected joins. That gives a reviewer a way to distinguish an edge in a model from an edge created by the dataset.
The free CSV below contains the two synthetic records and their expected verdicts. Use it as a simple acceptance fixture for your own join logic, then expand it with genuinely observed samples you are entitled to process. The overround calculator can check a complete market once matching and timing are established.
Sources and verification
Read on 31 October 2026. Official records and provider documentation can change. Recheck the linked source for a decision after this cutoff.
- 1. The Odds API: historical odds and timestamp semantics
Primary documentation for the distinction between requested time and returned historical snapshot time. The audit rules and synthetic example on this page are Vultax's illustrative method.
- 2. SportsGameOdds: historical data documentation
Provider account of historical events and opening/closing data; see the separate API comparison for the dated feature summary.
- 3. The Odds API: public historical EPL example
Provider-published source file downloaded on 30 October 2026; exact input hash and aggregate audit findings are in the results JSON. Source rows remain with the provider.
- 4. The Odds API: terms and conditions
Terms dated 31 August 2026, rechecked 31 October. They describe research and derived-value uses and restrict standalone raw-data resale. Recheck your own use and access terms.
Questions this guide answers
- Can I download a real historical odds database from this page?
- The page offers a link to the provider’s own real historical example, Vultax’s aggregate audit results and code, a synthetic timing fixture and blank checklists. It does not redistribute or sell a comprehensive sportsbook database. The API comparison identifies documented sources to investigate under their terms.
- Is a historical odds snapshot proof I could have placed a bet?
- No. A snapshot documents a recorded price under the provider's timestamp model. Availability, account eligibility, stake limits, suspensions, delays and settlement terms need separate evidence.