Understanding why level stakes loses
The overround is the answer, and it is bigger than most people assume in large fields.
Documentation / Market & analysis
/v1/market/overround
How fat the books have been, by field size and course.
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.
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| by | query | string | no | field_size, course, region, run_type, class or year. Defaults to field_size. |
| region | query | string | no | Restrict to one jurisdiction. |
| date_from | query | string | no | YYYY-MM-DD. Clamped to your plan's window. |
| date_to | query | string | no | YYYY-MM-DD. Clamped to your plan's window. |
curl https://api.apihorseracing.com/v1/market/overround \ -H "X-API-Key: $AHR_KEY"
$ch = curl_init('https://api.apihorseracing.com/v1/market/overround'); 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/market/overround', { 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/market/overround", headers={"X-API-Key": os.environ["AHR_KEY"]}, ).json()
curl https://api.apihorseracing.com/v1/market/overround?key=$AHR_KEY
$url = 'https://api.apihorseracing.com/v1/market/overround' . '?key=$AHR_KEY'; $data = json_decode(file_get_contents($url), true)['data'];
const res = await fetch( 'https://api.apihorseracing.com/v1/market/overround?key=$AHR_KEY' ); const { meta, data } = await res.json();
import os, requests r = requests.get( "https://api.apihorseracing.com/v1/market/overround?key=$AHR_KEY", ).json()
{ "meta": { "request_id": "ea1312c40f20db10", "data_as_of": "2026-09-12T05:10:27+00:00", "plan": "analyst", "window": "2017 → upcoming", "count": 4 }, "data": { "window": { "from": null, "to": null, "note": "The whole archive. Your plan sets no date limit." }, "by": "field_size", "field_size": [ { "field_size": "8-11", "races": 203671, "mean_book": 123.51, "margin_pct": 23.51, "tightest": 100, "fattest": 200 }, { "field_size": "2-7", "races": 128757, "mean_book": 117.33, "margin_pct": 17.33, "tightest": 100.51, "fattest": 186.12 }, "… 2 more" ], "note": "100 would be a fair book. The excess is the margin taken out of the field. Level stakes on every runner loses more than the margin, because equal stakes overweight the longshots.", "excluded": { "races": 144, "reason": "Book percentage outside 100 to 200, which is a partial field priced or a bad figure at source." } } }
A real response from /v1/market/overround,
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 |
|---|---|---|
| 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. |
| upgrade_required | 403 | The endpoint returns computed statistics and your plan does not include them. The body names every plan that does reach it, so you never have to look it up. |
| invalid_param | 422 | A date that is not YYYY-MM-DD, a by= dimension we do not have, or a cursor that did not come from us. The message names the parameter. |
| 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
How much margin the books have taken, sliced by field size, course, region, run type, class or year. The median and the percentiles rather than a mean, so one broken race cannot move a row.
The headline pattern is strong and consistent: margin grows with the size of the field. In our archive small fields run around 117% and fields of sixteen or more around 133%. That difference is the reason a strategy that looks marginally profitable in small fields can be hopeless in big ones without anything else changing.
Races with a book below 100% or above 200% are excluded as bad data rather than reported as remarkable. There are only a few dozen of them in the whole archive, and every one is a source error rather than an arbitrage opportunity.
The overround is the answer, and it is bigger than most people assume in large fields.
by=field_size, then confine a strategy to the bands where the margin is thinnest.
by=year. Margins move, and a backtest run across nine years is quietly averaging several different markets.