APIHorseRacing

Documentation / Guides

Getting started

From nothing to a working request in about two minutes.

Two minutes, honestly

There is no application form, no sales call and no card. Sign in with Google, take the key that appears, and make a request. If the request works, everything else in this API will work the same way, because there is only one authentication scheme and one response shape.

1. Get a key

Sign in with Google. Your key appears on your account page immediately, on the free plan. It reads real races on a delayed window: nothing newer than 24 hours, nothing older than seven days. It is the same database a paying customer reads, not a sandbox.

2. Make a request

Every request needs your key in the X-API-Key header. That is the whole of authentication.

curl https://api.apihorseracing.com/v1/meta/coverage \
  -H "X-API-Key: $AHR_KEY"

Start with /v1/meta/coverage rather than something more interesting. It needs no parameters, it works on every plan, and it tells you what exists and what your own key can reach in one response. If it returns, your integration is sound.

3. Read the envelope

Every response has the same two keys. meta carries the request id, the freshness stamp, your plan and your window. data carries the answer.

{
  "meta": {
    "request_id": "979954038cb35c98",
    "data_as_of": "2026-09-09T08:50:32+00:00",
    "plan": "free",
    "window": "24 hours to 7 days"
  },
  "data": { ... }
}

Quote request_id if you ever need to ask us about a response. It lets us find your exact request rather than guess at a reproduction.

4. Fetch a real race

Take a date inside your window and ask for the card, then follow one of the race identifiers.

curl "https://api.apihorseracing.com/v1/racecards/2026-09-05" \
  -H "X-API-Key: $AHR_KEY"

curl "https://api.apihorseracing.com/v1/races/rc_1WNM9X2" \
  -H "X-API-Key: $AHR_KEY"

The three things to read next

In this order, because each saves you a category of mistake.

GuideWhat it saves you from
Coverage by country Building against a field that does not exist in the jurisdiction you care about. Nine times in ten a null is the source, not us.
Errors Treating a plan refusal as a network failure and retrying it forever.
Timezones and dates Assuming a race time is UTC. It is not; it is local to the course, and everything else is UTC.

What to do when something is empty

An empty array and a refusal mean very different things here, and we never blur them.

  • An empty array means we looked and there was nothing. No racing that day, no runs in that window, no dividends published. It is an answer.
  • A 403 means your plan could not look. The body names the window or the plans that reach the endpoint. It is not an answer and retrying will not change it.
  • A null field means the jurisdiction did not publish it. Data coverage says which fields exist where, counted rather than claimed.

Before you go to production

  • Read meta.data_as_of and show it, or at least log it. Racing data has a freshness and pretending otherwise is how a stale result gets displayed as live.
  • Handle 429 by respecting Retry-After rather than backing off blindly. X-RateLimit-Remaining is on every successful response too.
  • If you are polling for results, stop, and take a webhook instead. It is faster and costs nothing against your quota.
  • Cache. There is no requirement to re-fetch anything, and a settled race never changes.