# OddsIntel Data API > Football consensus closing prices and fair prices (bookmaker margin removed) as a JSON API, for businesses. Public beta, v1. Every figure carries the number of independent sources behind it (n_sources; brands sharing one trading desk count once) and the age of its newest input price (price_age_s). Football only. Markets: 1x2 (match result), ou (over/under goals), btts (both teams to score), ah (Asian handicap, per exact line). Derived figures only: no single bookmaker's price, no bookmaker names. Businesses only, not for betting operators or their affiliates. 18+ · for businesses · not betting advice. ## Base URL and authentication - Base URL: https://data.oddsintel.app/v1 - Send the key in the request header `X-Api-Key: oid_...` on /v1/fair, /v1/close, /v1/close.csv, /v1/clv and /v1/fixtures. /v1/status needs no key. - Free Sample key, sent automatically by email (no review, no card): https://data.oddsintel.app/request-key - Keep the key on a server; never put it in a web page or a downloadable app. ## Endpoints (JSON; all GET except POST /v1/clv) - /v1/fair?date=YYYY-MM-DD[&market=1x2|ou|btts][&competition=text] — latest fair prices for matches kicking off on that UTC date. competition: case-insensitive "contains" match, up to 80 characters. - /v1/close?date=YYYY-MM-DD[&market=1x2|ou|btts] — same rows plus consensus_close (fair price at kick-off). null before kick-off; close_is_final is true once the match has kicked off. Ask for a past date for final closes. No competition filter. - /v1/close.csv?date=YYYY-MM-DD[&market=1x2|ou|btts] — the same close rows for one kick-off day as a CSV file (header row = the row fields below, same order; empty cell = null). Same key, limits and plan history depth; one file = one request. Errors are JSON. - POST /v1/clv with body {"bets": [{"ref"?, "fixture_id", "market", "line" (ou only), "selection", "odds", "taken_at"}, ...]} (taken_at required) — grades each price against the consensus close: clv = odds × (1 / consensus_close) − 1, the same close /v1/close serves. One call = one request; up to 10 bets on Sample, 50 on Starter, 500 on Pro and Business. Per-bet status: graded | not_closed_yet | no_consensus | unknown_fixture | unknown_selection | taken_after_kickoff | plan_scope | invalid (with message, e.g. taken_at missing) | implausible_price (more than 25 % above the margin-free close). A price taken less than 60 seconds before kick-off, or later, is never graded. Each graded bet also carries fair_at_taken_at (our fair price then, as /v1/fair?as_of= serves it; null when no figure stood then or we cannot show it was still being re-checked) and edge_vs_fair_at_taken_at_pct. Takeable grading (Pro and Business; takeable: seen (seen at our sources: one of them had an open price at or above yours) | near (near our sources' best price: at most 4 % above it) | not_seen (not at our sources) | unknown (incl. thin_coverage: fewer than 3 of our sources known and the price not seen), against the best open price among our sources, never served; at most 2 different prices checked per selection per account, then takeable_reason probe_limit; takeable_basis {n_sources, max_read_age_s}; no source named; our sources are the European bookmakers our consensus is built from: a bet at a book we don't collect can show not at our sources) is being finalised: until then takeable is "unchecked" and takeable_basis / takeable_reason null. Method: https://data.oddsintel.app/clv-grading - /v1/fixtures?kickoff=ISO8601&home=text&away=text — find our fixture_id from your feed's kick-off time (± 15 min) and team names. Up to 5 rows, best first: {fixture_id, kickoff, competition, home, away, confidence (0–1), kickoff_delta_s, teams_reversed}. Join at confidence ≥ 0.9; 0.6–0.9 check by hand; lower = not found. teams_reversed true = your home is our away (swap 1X2 home/away). Kick-off day must be inside your plan's window. One call = one request. - /v1/status — {"status": "ok" | "stale", "data_as_of": UTC time, "max_age_s": seconds, "history_from": first kick-off date we hold}. No key. Response shape for /v1/fair and /v1/close: {"data": [row, ...], "meta": {date, count, data_as_of, contract_version: "v1", plan, delay_minutes?, as_of?, max_age?}}. One date per call, no paging, responses cached up to 60 seconds, unknown parameters ignored. Row fields: fixture_id, kickoff, competition, home, away, market, line (ou goal line, else null), selection (home/draw/away, over/under, yes/no), fair_p (0-1), fair_odds (1/fair_p), consensus_close, close_ts, close_is_final, n_sources, price_age_s, status (provisional | confirmed_before_ko | withheld), quality_flag (ok | stale | thin), computed_at, checked_at (our last re-check of the figure), read_age_s (seconds since the newest input was last confirmed by a read, as of checked_at; price_age_s is the value age: seconds since the figure last changed), oldest_input_age_s (age of the oldest input behind the figure), dispersion_pp (spread of the independent sources' fair probabilities, percentage points; no source named), n_sources_band ("3" | "4+"; n_sources is capped at 4). fair_p has 3 decimals; before kick-off a figure changes at most once every 5 minutes. Optional parameters: max_age (seconds; leave out pre-kick-off rows whose oldest input is older) and, on /v1/fair, as_of (UTC time; the rows as they stood then, rounded down to 5 minutes). Under 3 independent sources, or when the newest input was not read within 45 minutes, the row is withheld and its figures are null; a high price_age_s is not a sign of freshness. ## Errors JSON body {"error": code, "message": text}. 400 bad_request · 401 missing_key / invalid_key · 403 revoked / plan_scope (date outside the key's plan history depth) · 429 rate_limited (wait Retry-After seconds) · 503 data_stale (data older than 10 minutes; we refuse rather than serve it) / busy (Retry-After) / not_open_yet. Rate headers: X-RateLimit-Limit, X-RateLimit-Remaining. ## Plans (planned prices; free during the beta, no card taken) - Sample (free): 200 requests a day; 5 requests a minute; up to 300 rows per answer; 1 key · internal testing and evaluation only. Today + 2 days: 2 h delayed. - Starter (€49 / month, or €490 / year): history since 10 Oct 2026, capped at 90 days; 5,000 requests a day; 60 requests a minute; internal business use. Today + 2 days: no added delay. - Pro (€149 / month): full history since 10 Oct 2026; 50,000 requests a day; 120 requests a minute; show individual figures to your users inside your own product (no resale or bulk redistribution). Today + 2 days: no added delay. - Business (from €399 / month): bulk history since 10 Oct 2026; custom limits. Today + 2 days: no added delay. History depth: Sample yesterday only, Starter 90 days back, Pro and Business no cap — and no plan reaches before 2026-10-10, the first kick-off date we hold. Sample keys read yesterday, plus today and the next 2 days as the figures stood 2 hours ago (meta.delay_minutes: 120), at most 300 rows per answer (meta.truncated). Daily limits reset at midnight UTC. Beta terms: no service level agreement; data provided as is; no resale or redistribution. ## Links - [Docs](https://data.oddsintel.app/docs): quickstart, every field, method, errors, changelog - [OpenAPI 3.1 spec](https://data.oddsintel.app/openapi.json): machine-readable, for code generators - [Coverage](https://data.oddsintel.app/coverage): competitions in our data, with recent match counts - [Status](https://data.oddsintel.app/status): is the data fresh right now - [Pricing](https://data.oddsintel.app/pricing) - [Beta terms](https://data.oddsintel.app/terms) - Contact: data@oddsintel.app