Calibrating a model
Compare your selections' A/E against the band they fall in, not against 1.00. Picking winners at 20/1 is easy; beating 0.72 there is not.
Documentation / Market & analysis
/v1/market/sp-performance
What each price band has actually returned.
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 |
|---|---|---|---|---|
| region | query | string | no | Restrict to one jurisdiction. |
| run_type | query | string | no | Restrict to Flat, Hurdle, Chase or NH Flat. |
| field_size | query | string | no | A field size band. |
| course_id | query | string | no | Restrict to one course. |
| 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/sp-performance \ -H "X-API-Key: $AHR_KEY"
$ch = curl_init('https://api.apihorseracing.com/v1/market/sp-performance'); 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/sp-performance', { 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/sp-performance", headers={"X-API-Key": os.environ["AHR_KEY"]}, ).json()
curl https://api.apihorseracing.com/v1/market/sp-performance?key=$AHR_KEY
$url = 'https://api.apihorseracing.com/v1/market/sp-performance' . '?key=$AHR_KEY'; $data = json_decode(file_get_contents($url), true)['data'];
const res = await fetch( 'https://api.apihorseracing.com/v1/market/sp-performance?key=$AHR_KEY' ); const { meta, data } = await res.json();
import os, requests r = requests.get( "https://api.apihorseracing.com/v1/market/sp-performance?key=$AHR_KEY", ).json()
No sample has been captured for this endpoint yet. Use the console below against your own key.
| 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
What each price band has actually returned, across every settled race in the archive. Runners, wins, strike rate, A/E with its interval, and level stakes profit, banded from odds-on out to 100/1 and beyond.
This is the single most useful call in the API for calibrating expectations, because it answers a question every other endpoint depends on: is the market right, and where is it least right.
Our own archive says it is close to right in aggregate and clearly wrong at one end. Across all settled races, actual wins and market-implied wins agree to within a fifth of one percent, which is the market working. But the band at 16/1 and longer returns an A/E of 0.72 with an interval of 0.71 to 0.74 over roughly 495,000 runners, and loses about half of every unit staked. The curve is monotonic: the longer the price, the worse the return.
That is the favourite-longshot bias, and it is the most durable finding in racing. It is not an edge, because it tells you what loses rather than what wins. What it is good for is calibration: any model that likes long shots needs to beat a base rate that is considerably worse than the headline book margin.
Compare your selections' A/E against the band they fall in, not against 1.00. Picking winners at 20/1 is easy; beating 0.72 there is not.
A strategy that appears profitable on long shots should be looked at very hard indeed, because the population says it is swimming upstream.
Split by run_type and field_size. It is not uniform, and where it is shallower there is more room for a model to work.
Bands are by starting price. Level stakes assumes a one unit win bet at that price, with no commission and no place terms.