Analysis7 min read

Bookmaker odds feeds: validate before comparing

Compare bookmaker odds only after checking market identity, settlement rules, timestamps and status. Use a local validator to expose false comparisons.

On this page

Before comparing two bookmakers' prices, establish that both observations describe the same market and an acceptable state of that market. A larger decimal number is a poor signal when it belongs to a different line, includes extra time, or was retained after suspension.

This article is for developers building odds archives, comparison displays and data-quality alerts from feeds they are authorized to use. The practical output is a comparison contract: a normalized record, explicit rejection reasons and a small offline checker. It validates your supplied evidence; it does not establish that an offer is currently executable.

Define the market before comparing the number

Consider two fictional records: Over 2.5 goals at 1.91 and Over 3.5 goals at 2.05. Treating the latter as an improvement compares two different outcomes. The numbers can both be valid while the comparison is invalid.

Use an explicit identity key. The following is a proposed internal model, not a claim that every provider returns these field names:

FieldWhat to establishExample mistake it prevents
Event namespace and IDThe same event in one provider namespace, or a reviewed cross-provider mappingJoining two fixtures just because their team names match
PeriodFirst half, regulation time or another defined periodMixing a half-time market with a full-match market
Market and lineExact proposition and threshold, with a consistent unitMixing totals 2.5 and 3.5
Selection setThe same complete set of outcome identifiersTreating a missing outcome as a price disappearance
Settlement rulesA reviewed equivalence group for the rules that affect the resultMixing regulation-only and extra-time-inclusive outcomes
Event phaseA known pre-match or in-play stateComparing a retained pre-match snapshot with an in-play observation

A matching in-play flag does not establish equal score, match clock or game state. This example does not validate those fields; a workflow that requires synchronized game state needs additional evidence and gates.

Preserve the provider and bookmaker identifiers alongside that key. Identical raw IDs from different namespaces are not a cross-provider mapping. A settlement_rules label is only as reliable as the rule comparison behind it; copying the same label into both rows cannot establish equivalence.

These distinctions map to real feed structures. The Odds API v4 documents bookmaker and market objects, outcome prices, and point values for spread and totals outcomes. Its event-odds response also carries market-level update timestamps. Normalize the endpoint you actually use rather than copying a sample from a different operation. The Odds API v4

Rebuild state before judging completeness

A stream update is not necessarily a complete snapshot. Sportradar's Unified Odds Feed documentation says an odds_change can cover only some markets; markets omitted from that message remain unchanged. Clearing your entire market cache on each message would manufacture disappearing prices. Sportradar odds-change semantics

Separate two jobs:

  1. A provider adapter applies the documented snapshot, delta, recovery and status rules to a cache.
  2. A comparison gate evaluates the resulting normalized state.

The checker linked below performs the second job. It must receive a complete normalized observation. Setting complete_snapshot: true on a raw delta bypasses the very question the adapter must answer.

For a totals example, require both OVER and UNDER in the normalized selection set. A different market needs its own completeness definition. Preserve explicit suspension, closure and unknown state; do not fill a missing status with OPEN unless the source contract actually establishes that state. Some protocols define defaults, but a default from one protocol is not a rule for another.

When a connection is lost, retain the last observation for your archive and mark it ineligible for current comparison until the adapter establishes usable state again. Historical visibility and current eligibility are separate properties.

Keep collection time separate from price time

Record when your collector received the observation and what the source timestamp means. A fresh HTTP response may contain an old price, while an unchanged price may remain valid for a long time. An age threshold is an admission policy, not proof that a price is wrong.

Do not rewrite a price timestamp when the connection emits a heartbeat. Betfair distinguishes heartbeat messages from market changes; its stream pt is a message publish time. Connection activity alone does not establish that every cached selection was refreshed. Betfair Exchange Stream API

For a monitoring job, define and record these limits before admitting a pair:

  • Maximum source age at the declared evaluation time.
  • Maximum difference between the two collection timestamps.
  • How missing, ambiguous, future or timezone-free timestamps are handled.
  • Which source field supports the normalized price-update time.

Our fictional fixture policy uses a 30-second age limit and a two-second collection skew. Those values make the example reproducible; they are not bookmaker requirements or suitable defaults for every live feed. A production policy must fit the feed's documented timestamp semantics and the reader's decision deadline. A market-level update timestamp must remain identified as market-level evidence; it does not establish each outcome's last price change.

Evaluate stored records against the intended historical evaluation time when replaying an archive. Comparing yesterday's fixtures with your current wall clock answers a different question.

Use rejection reasons to locate the problem

These are illustrative diagnostic branches, not measured bookmaker failure rates:

ObservationComparison decisionInvestigate next
Same contract, complete open state, valid prices and acceptable timestampsEligible for this comparisonCompare the observations while preserving their timestamps
Same label, different line or settlement rulesReject this pairingCorrect the market mapping
One side suspended or closedHold current comparisonProcess the status transition; preserve history
Outcome missing from a supposedly complete snapshotHoldCheck adapter completeness and recovery
Old price time with a recent collection timeHold if outside your declared age policyInspect upstream update semantics and cache state
Healthy heartbeat but no established usable market stateHoldDiagnose stream state and recovery
Decimal price missing, non-finite or at most oneReject the normalized inputCheck parsing and odds-format conversion

The last branch matters when ingesting multiple odds formats. Convert using a defined adapter and validate the resulting decimal representation; do not silently treat an American-odds integer as decimal odds. The Odds API exposes an explicit odds-format choice. Odds API format options

Run the offline comparison gate

Save the Python checker and fictional input cases in the same directory, then run:

sh
python3 odds_compare.py fixtures.json

The supplied checker deliberately accepts only its fixture contract: regulation-time total goals at 2.5 with the documented fictional rule ID. Other markets require a reviewed extension of the contract and tests, not just replacement prices. Keep bookmaker/source provenance in your surrounding observation log.

In the executed local fixture, one of 14 pairs was eligible and 13 were held; 21 test methods passed. These are synthetic validator results, not bookmaker success or failure rates.

The input file declares a fixed evaluation time under now and the illustrative age/skew policy, so replay does not depend on when you run it. Each case contains two candidate normalized records, including deliberately incomplete or invalid records that should be held. The example contract is regulation-time total goals at 2.5, with OVER and UNDER selections and a fictional settlement-rule ID.

The checker returns eligible_for_comparison plus reasons for held pairs. Eligibility permits a data comparison under the supplied contract; it says nothing about staking, returns, liquidity or execution. Equal prices are not required.

The method and schema explain how to supply your own normalized records. The tests exercise valid comparisons and malformed or ambiguous evidence. The checker makes no network requests and includes no API adapter. Its timestamp field, price_updated_at, is supplied by your adapter; the checker cannot prove that the upstream field has the meaning you assigned to it.

Turn the contract into an operational check

An HTTP success counter belongs in transport monitoring. Keep a separate denominator for comparisons admitted by the data gate. Record attempted pairs, admitted pairs and rejection reasons, including observations you could not normalize. Otherwise, an apparently clean chart can simply be hiding the difficult inputs.

Store the mapping version, source identifiers, collection time, source timestamps, status and reasons with each decision. When an alert looks wrong, that record lets you distinguish a market-mapping defect from a stale cache or a transport failure. Do not replace unknown state with zero odds or erase the previous valid observation.

A proxy can change the transport route used by an authorized collector. It cannot establish market equivalence, fill in missing selections or prove a price is current. If the evidence is incomplete, fix the data contract before increasing request volume. For network-layer diagnosis, see proxy environment variables and timeout troubleshooting.

Method: this is an AI-assisted synthesis of the linked provider documentation and an explicitly synthetic local validation exercise. It reports no live bookmaker collection, provider performance, betting outcome or ipvolt service trial.

Join the ipvolt waitlist. Service access is not open yet. One email when access opens. Nothing else.

Sources

  1. Odds API Documentation V4
  2. List of API Betting Markets
  3. Exchange Stream API
  4. Betting Enums
  5. Odds Change

Tagged:ProxiesTroubleshooting