Deciding whether to upgrade
by=endpoint. If your refusals are concentrated on statistics endpoints, Analyst is the answer. If they are rate limits, a plan with a higher ceiling is.
Documentation / Webhooks & account
/v1/account/usage
Requests, errors and every refusal, by endpoint.
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 |
|---|---|---|---|---|
| days | query | integer | no | 1 to 90. Defaults to 30. |
| by | query | string | no | day or endpoint. Defaults to day. |
curl https://api.apihorseracing.com/v1/account/usage \ -H "X-API-Key: $AHR_KEY"
$ch = curl_init('https://api.apihorseracing.com/v1/account/usage'); 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/account/usage', { 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/account/usage", headers={"X-API-Key": os.environ["AHR_KEY"]}, ).json()
curl https://api.apihorseracing.com/v1/account/usage?key=$AHR_KEY
$url = 'https://api.apihorseracing.com/v1/account/usage' . '?key=$AHR_KEY'; $data = json_decode(file_get_contents($url), true)['data'];
const res = await fetch( 'https://api.apihorseracing.com/v1/account/usage?key=$AHR_KEY' ); const { meta, data } = await res.json();
import os, requests r = requests.get( "https://api.apihorseracing.com/v1/account/usage?key=$AHR_KEY", ).json()
{ "meta": { "request_id": "e8e1dfc44aa2684e", "data_as_of": "2026-09-12T05:10:27+00:00", "plan": "analyst", "window": "2017 → upcoming" }, "data": { "plan": { "name": "analyst", "window": "2017 → upcoming", "statistics": true, "rate_limit": "240 a minute" }, "this_month": { "from": "2026-09-01", "requests": 368, "quota": 1000000, "remaining": 999632, "used_pct": 0.0004 }, "today": { "date": "2026-09-13", "requests": 64 }, "by_day": [ { "date": "2026-09-08", "requests": 22 }, { "date": "2026-09-09", "requests": 215 }, "… 2 more" ], "by_endpoint": [ { "endpoint": "races", "requests": 20, "errors_4xx": 11, "errors_5xx": 0, "empty": 0 }, { "endpoint": "meta-coverage", "requests": 17, "errors_4xx": 0, "errors_5xx": 0, "empty": 0 }, "… 38 more" ], "refused": [], "note": null } }
A real response from /v1/account/usage,
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 |
|---|---|---|
| plan | string | The plan your key resolves to right now. |
| name | string | Race title as published, sponsor and all. |
| window | string | How far back and forward that plan can read. |
| date | date | Race date, YYYY-MM-DD. |
| 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
What your key has done: requests, errors and every refusal, by day or by endpoint, alongside your plan, your limits and how much of your monthly quota is gone.
The refusals are the part worth reading. Every time your key asked for something its plan could not reach, we recorded which endpoint and which reason, so this endpoint can tell you precisely what a plan change would buy you rather than making you guess. It is the same data behind the prompts on your account page.
It is also the honest answer to "am I being rate limited". The counts here are what we actually served and refused, taken from the same records that enforce the limits, so it cannot disagree with what happened.
by=endpoint. If your refusals are concentrated on statistics endpoints, Analyst is the answer. If they are rate limits, a plan with a higher ceiling is.
Call it daily and warn when the quota used passes whatever threshold you are comfortable with. Far better than discovering it at month end.
by=endpoint over thirty days. A single endpoint dominating the count is usually a poller that should be a webhook.
Counts are per key, and there is one key per account, so this is your whole usage. It does not count against your own quota.