APIHorseRacing

Documentation / Guides

The envelope

meta and data, and what is always in meta.

Every response from every endpoint has the same two keys. Once you have written the code that reads one, you have written the code that reads all sixty.

{
  "meta": { ... },
  "data": { ... }
}

What is always in meta

FieldWhat it is
request_id Identifies this exact request. Also sent as the X-Request-Id header. Quote it when reporting anything and we can find the request rather than guess at a reproduction.
data_as_of When the serving database was last written, not when you asked. If it stops advancing during a racing afternoon, something upstream has stopped.
plan The plan this request was served under. Read from your account at request time, which is why an upgrade takes effect without a new key.
window The date range that plan can read, in words.

What appears when it applies

FieldOnWhat it is
countAnything returning a list Items in data, so you are not calling length on something that might be an object.
next_cursorPaginated endpoints Pass it back to get the next page. Absent on the last one.
has_morePaginated endpoints The thing to loop on. Do not infer it from the item count.
searchedSearch endpoints The date range actually used after clamping to your window. Read this rather than assuming your parameters were honoured verbatim.
withheldForm endpoints How many runs your window hid. It is how you tell a short career from a clipped one.

data is an object or an array, never both

A single thing returns an object. A list returns an array. It does not depend on how many results there happen to be, so a search returning one row still returns an array of one and your code does not need a special case.

# object
GET /v1/races/rc_1WNM9X2      → data: { race_id: ..., runners: [...] }

# array
GET /v1/races/search          → data: [ { ... }, { ... } ]

Errors replace data, they do not join it

A failed response has error and no data. There is never a response carrying both, so a truthy check on data is a safe test for success.

{
  "error": {
    "code": "outside_window",
    "message": "That date is outside the 24h – 7d your plan reads.",
    "doc_url": "https://apihorseracing.com/documentation/errors#outside_window",
    "request_id": "17d0af45d676676d",
    "your_plan": "free",
    "window": "24h – 7d",
    "requested": "2021-02-28"
  }
}

Branch on error.code, never on the message. Codes are part of the contract; messages are wording and may be improved. Full list in Errors.

Version one only ever gains fields

We will not remove or rename a documented field that is in use. New fields will appear, so parse permissively: ignore what you do not recognise rather than rejecting the response. A field can become null where a source stops publishing it, and the changelog will say so.