A startup health check
Call it when your process boots. If data_as_of is hours old or races has not moved, something upstream is wrong and you will know before your users do.
Documentation / Reference & coverage
/v1/meta/coverage
Totals, the full-order split, and what your own plan can read.
Send your key in the X-API-Key header, or as
?key= if your tool cannot set one. A date outside your plan's window returns
403 outside_window naming the window, never an empty array.
None. This endpoint takes nothing beyond your key.
curl https://api.apihorseracing.com/v1/meta/coverage \ -H "X-API-Key: $AHR_KEY"
$ch = curl_init('https://api.apihorseracing.com/v1/meta/coverage'); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ['X-API-Key: ' . getenv('AHR_KEY')], ]); $data = json_decode(curl_exec($ch), true)['data'];
const res = await fetch( 'https://api.apihorseracing.com/v1/meta/coverage', { headers: { 'X-API-Key': process.env.AHR_KEY } } ); const { meta, data } = await res.json();
import os, requests r = requests.get( "https://api.apihorseracing.com/v1/meta/coverage", headers={"X-API-Key": os.environ["AHR_KEY"]}, ).json()
curl https://api.apihorseracing.com/v1/meta/coverage?key=$AHR_KEY
$url = 'https://api.apihorseracing.com/v1/meta/coverage' . '?key=$AHR_KEY'; $data = json_decode(file_get_contents($url), true)['data'];
const res = await fetch( 'https://api.apihorseracing.com/v1/meta/coverage?key=$AHR_KEY' ); const { meta, data } = await res.json();
import os, requests r = requests.get( "https://api.apihorseracing.com/v1/meta/coverage?key=$AHR_KEY", ).json()
{ "meta": { "request_id": "6dcabd0aee945a9e", "data_as_of": "2026-09-12T05:10:27+00:00", "plan": "analyst", "window": "2017 → upcoming" }, "data": { "first_race": "2017-01-01", "races": 473221, "runners": 5200000, "horses": 371904, "countries": 18, "result_order": { "full": 232032, "placings_only": 241189, "full_pct": 0.49 }, "not_covered": [ "Japan" ], "never_published": { "sectional_times": "No sectional or in-running times exist in this dataset.", "bookmaker_odds": "Starting price and the price path only. This is not an odds comparison feed." }, "your_plan": { "plan": "analyst", "window": "2017 → upcoming", "statistics": true } } }
A real response from /v1/meta/coverage,
captured by calling the live API rather than written by hand.
Each field is described once and rendered on every endpoint that returns it, so the wording cannot drift between pages.
| Field | Type | What it is |
|---|---|---|
| runners | integer | Runners who took part. |
| plan | string | The plan your key resolves to right now. |
| window | string | How far back and forward that plan can read. |
| Code | Status | When |
|---|---|---|
| unauthorized | 401 | No key, or one we do not recognise. Check the header name and that the key has not been rotated from your account page. |
| rate_limited | 429 | Past your per-minute rate. Retry-After is set, and X-RateLimit-Remaining tells you where you stand on every successful response too. |
| server_error | 500 | Ours, not yours. Every response carries a request_id; quote it and we can find the exact request. |
A real request from your browser straight to
api.apihorseracing.com with
your own key. Nothing is proxied and nothing is logged by this page.
Remembered in this browser so you do not paste it on every page. Forget it
The whole archive in one object: how many races, how many runners, how far back, and the split between races carrying a full finishing order and those publishing only the placings.
It also returns your own plan and window, which makes it the one call worth making at startup: it answers what exists and what you personally can reach, in the same response.
never_published is the part worth reading twice. Sectional times and bookmaker odds do not exist in this dataset and never will, and we would rather you learned that here than after a card payment.
Call it when your process boots. If data_as_of is hours old or races has not moved, something upstream is wrong and you will know before your users do.
The figures are ours to publish and yours to display. Several customers put races and the date span straight onto their own about page.
A race missing from a date inside your window is a real absence, not a transient failure. This endpoint tells you the archive is intact.
The backfill is still running, so races and the date span move upward daily. Nothing already loaded changes.