# HorseRacingAPI complete reference

The whole HorseRacingAPI API in one file: every endpoint, parameter, error, sample answer and guide.

- Base URL: https://api.horseracingapi.org/v1
- Authentication: your key in `X-API-Key: hra_live_…` or `Authorization: Bearer hra_live_…`. Never in the address.
- Site: https://horseracingapi.org
- Endpoints: 19, every one GET
- Generated: 2026-10-21 21:19 UTC
- This file: https://horseracingapi.org/documentation/manual.md

## Before anything else

- Factual data only: official results as published. No predictions, ratings, tips or betting advice on any endpoint.
- Five nations: the United States (USA), Great Britain (GB), Ireland with Northern Ireland (IRE), France (FR) and South Africa (RSA).
- From 1 January 2022 to now. Nothing earlier is served, on any plan.
- Not in the data, on any plan, at any price: odds or prices of any kind, sire, dam or breeding, prize money, official ratings or speed figures, sectional times, in-running comments, racecards or declarations, deep statistics and bulk downloads. A horse's form and a jockey's rides are the last 10 only.
- Where a nation does not publish a race class or a winning time, the field is null. Never guessed.
- Every answer is `{"data": …, "meta": {…}}`. `meta` carries `request_id`, `plan`, `window` (what your plan reaches), `data_as_of` (how fresh the data is, UTC) and, on lists, `count` with the page or cursor fields.
- Ids are our own, with a type prefix (`rc_` race, `mt_` meeting, `cr_` course, `hs_` horse, `jk_` jockey, `tr_` trainer, `ow_` owner). Stable and safe to store.
- Times are UTC, except race times, which are the course's own.

## Plans

| Plan | Price | Reads | Rate limit | Allowance |
| --- | --- | --- | --- | --- |
| Free key | Free | Yesterday's results; 4 endpoints | 10 a minute | 50 a day |
| Results plan | $29 a month or $299 a year | Everything since 1 January 2022; all 19 endpoints | 60 a minute | 30,000 a month |

Free-key endpoints: `/v1/results/yesterday`, `/v1/countries`, `/v1/courses`, `/v1/usage`. Pricing: https://horseracingapi.org/pricing

## Coverage

Every meeting in the five nations since 1 January 2022, counted from the data each hour on https://horseracingapi.org/coverage. `GET /v1/countries` returns each nation and whether it publishes race class and winning times.

## Endpoints

### Results

Days of results, the newest as they are declared, single races and race search.

#### `GET /v1/results/latest` (Results plan)

The newest results, as they are declared, each with its full finishing order.

Plans: Results plan. Page: https://horseracingapi.org/documentation/results-latest. MCP tool: `latest_results`

| Parameter | In | Type | Required | Description | Example |
| --- | --- | --- | --- | --- | --- |
| `country` | query | string | no | Only this nation: USA, GB, IRE, FR or RSA. |  |
| `per_page` | query | integer | no | How many to a page, 1 to 50. Default 25. | `2` |

| Field | Type | What it holds |
| --- | --- | --- |
| `race_id` | string | Our own id for the race. Stable, safe to store. |
| `meeting_id` | string | The meeting it was run at. |
| `course_id` | string | The course. |
| `course` | string | Where it was run. |
| `country` | string | USA, GB, IRE, FR or RSA. |
| `date` | string | The course's own calendar day, YYYY-MM-DD. |
| `time` | string | The advertised start, course time, HH:MM. |
| `off_time` | string | When it actually started, course time, HH:MM:SS. |
| `name` | string | The race title as run. |
| `race_type` | string | Flat, Hurdle, Chase and so on, where the nation publishes it. |
| `class` | string | Where the nation publishes one, else null. |
| `distance` | string | As the course states it. |
| `going` | string | The ground as declared on the day. |
| `surface` | string | Turf, Dirt or All Weather. |
| `handicap` | boolean | Whether it was a handicap. |
| `runners` | integer | How many started. |
| `result_available` | boolean | True once the result is declared. |
| `winning_time` | string | Where the nation publishes one, as M:SS.ss, else null. Never guessed. |
| `finishing_order` | array | Every runner in order: position, status, distance_beaten, cloth, draw, horse_id, horse, age, weight, jockey_id, jockey, trainer_id, trainer, owner_id, owner. |

Errors: 400 `bad_request`, 401 `unauthorized`, 403 `upgrade_required`, 422 `unknown_parameter`, 429 `rate_limited`, 429 `quota_exceeded`, 503 `server_error`

Sample answer:

```json
{
  "data": [
    {
      "race_id": "rc_3yh12h8j",
      "meeting_id": "mt_fhx5gesa",
      "course_id": "cr_rsqqeptz",
      "course": "Belmont Park",
      "country": "USA",
      "date": "2026-10-21",
      "time": "21:05",
      "off_time": "21:12:15",
      "name": "Race 8 - Maiden Claiming",
      "race_type": null,
      "class": null,
      "distance": "6f 110y",
      "going": "Firm",
      "surface": "Dirt",
      "handicap": false,
      "runners": 9,
      "result_available": true,
      "winning_time": null,
      "finishing_order": [
        {
          "position": 1,
          "status": "finished",
          "distance_beaten": null,
          "cloth": 7,
          "draw": 7,
          "horse_id": "hs_34xer2s6",
          "horse": "Richies Love",
          "age": 3,
          "weight": "8-11",
          "jockey_id": "jk_s4cw84ry",
          "jockey": "Kendrick Carmouche",
          "trainer_id": "tr_ah0dgtjt",
          "trainer": "Michael Miceli",
          "owner_id": "ow_1tzx1gq3",
          "owner": "Ruggeri Stable, Inc And Miceli, Michael"
        },
        {
          "position": 2,
          "status": "finished",
          "distance_beaten": null,
          "cloth": 6,
          "draw": 6,
          "horse_id": "hs_85bsv5vb",
          "horse": "Rocolino",
          "age": 3,
          "weight": "8-11",
          "jockey_id": "jk_jha0spxk",
          "jockey": "Dalila Rivera",
          "trainer_id": "tr_7wgf9v8z",
          "trainer": "Lolita Shivmangal",
          "owner_id": "ow_qkc7648q",
          "owner": "Bueti, Marie"
        },
        "… 7 more"
      ]
    },
    {
      "race_id": "rc_0hks9m2r",
      "meeting_id": "mt_gghvc8xt",
      "course_id": "cr_1e5waa5x",
      "course": "Horseshoe Indianapolis",
      "country": "USA",
      "date": "2026-10-21",
      "time": "20:53",
      "off_time": "20:57:27",
      "name": "Race 6 - Starter Optional Claiming",
      "race_type": null,
      "class": null,
      "distance": "1m",
      "going": "Firm",
      "surface": "Turf",
      "handicap": false,
      "runners": 7,
      "result_available": true,
      "winning_time": null,
      "finishing_order": [
        {
          "position": 1,
          "status": "finished",
          "distance_beaten": null,
          "cloth": 6,
          "draw": 6,
          "horse_id": "hs_nrpyv461",
          "horse": "Shankar",
          "age": 6,
          "weight": "8-10",
          "jockey_id": "jk_9seas2qn",
          "jockey": "Joseph Romero",
          "trainer_id": "tr_jb8vesce",
          "trainer": "Tracey Wisner",
          "owner_id": "ow_zg8dyabb",
          "owner": "Tracey Wisner Racing Llc"
        },
        {
          "position": 2,
          "status": "finished",
          "distance_beaten": null,
          "cloth": 5,
          "draw": 5,
          "horse_id": "hs_5sh540e3",
          "horse": "Baytown Parfait",
          "age": 5,
          "weight": "8-12",
          "jockey_id": "jk_202803rr",
          "jockey": "Orlando Mojica",
          "trainer_id": "tr_gfddj9tg",
          "trainer": "Michael Tomlinson",
          "owner_id": "ow_93s0r903",
          "owner": "Liberty Racing Stables Llp"
        },
        "… 5 more"
      ]
    }
  ],
  "meta": {
    "plan": "results",
    "window": "2022-01-01 to now",
    "data_as_of": "2026-10-21T21:19:06Z",
    "count": 2
  }
}
```

#### `GET /v1/results/yesterday`

Every result from yesterday, at each course's own day, meeting by meeting.

Plans: Free key, Results plan. Page: https://horseracingapi.org/documentation/results-yesterday. MCP tool: `yesterdays_results`

| Parameter | In | Type | Required | Description | Example |
| --- | --- | --- | --- | --- | --- |
| `country` | query | string | no | Only this nation: USA, GB, IRE, FR or RSA. | `FR` |
| `per_page` | query | integer | no | How many to a page, 1 to 25. Default 10. | `1` |
| `page` | query | integer | no | Which page, from 1. |  |

| Field | Type | What it holds |
| --- | --- | --- |
| `meeting_id` | string | Our own id for the meeting. |
| `course_id` | string | The course. |
| `course` | string | The course name. |
| `country` | string | USA, GB, IRE, FR or RSA. |
| `date` | string | The course's own calendar day. |
| `going` | string | The going for the meeting. |
| `surface` | string | Turf, Dirt or All Weather. |
| `races` | array | Every race at the meeting in full, with its finishing order. |

Errors: 400 `bad_request`, 401 `unauthorized`, 422 `unknown_parameter`, 429 `rate_limited`, 429 `quota_exceeded`, 503 `server_error`

Sample answer:

```json
{
  "data": [
    {
      "meeting_id": "mt_31tcke0x",
      "course_id": "cr_t6pv23df",
      "course": "Bordeaux Le Bouscat",
      "country": "FR",
      "date": "2026-10-20",
      "going": "Soft",
      "surface": "Turf",
      "races": [
        {
          "race_id": "rc_9c9cmvvp",
          "meeting_id": "mt_31tcke0x",
          "course_id": "cr_t6pv23df",
          "course": "Bordeaux Le Bouscat",
          "country": "FR",
          "date": "2026-10-20",
          "time": "09:20",
          "off_time": "09:20:42",
          "name": "Prix De Suffren Stakes",
          "race_type": "Flat",
          "class": null,
          "distance": "1m 1f 97y",
          "going": "Soft",
          "surface": "Turf",
          "handicap": false,
          "runners": 6,
          "result_available": true,
          "winning_time": "2:09.51",
          "finishing_order": [
            {
              "position": 1,
              "status": "finished",
              "distance_beaten": null,
              "cloth": 3,
              "draw": 1,
              "horse_id": "hs_yrjp3fkv",
              "horse": "Drumard",
              "age": 5,
              "weight": "9-2",
              "jockey_id": "jk_3k4knfq5",
              "jockey": "I Mendizabal",
              "trainer_id": "tr_aawg032s",
              "trainer": "E Libaud",
              "owner_id": "ow_8d2tqgw5",
              "owner": "Hyperion Sarl"
            },
            {
              "position": 2,
              "status": "finished",
              "distance_beaten": "Head",
              "cloth": 1,
              "draw": 4,
              "horse_id": "hs_gv4m13aa",
              "horse": "Reach The Sky",
              "age": 4,
              "weight": "9-6",
              "jockey_id": "jk_2k91gz18",
              "jockey": "H Besnier",
              "trainer_id": "tr_e9xaq4z5",
              "trainer": "P Cottier",
              "owner_id": "ow_wk8mjvgp",
              "owner": "Gousserie Racing/ec D'haspel"
            },
            "… 4 more"
          ]
        },
        "… 7 more"
      ]
    }
  ],
  "meta": {
    "plan": "free",
    "window": "yesterday",
    "data_as_of": "2026-10-21T21:19:06Z",
    "date": null,
    "count": 1,
    "page": 1,
    "per_page": 1,
    "total": 3,
    "total_pages": 3,
    "next_page": 2,
    "prev_page": null,
    "next_cursor": null
  }
}
```

#### `GET /v1/results/{date}` (Results plan)

Every result for any day since 1 January 2022, meeting by meeting.

Plans: Results plan. Page: https://horseracingapi.org/documentation/results-date. MCP tool: `results_on_date`

| Parameter | In | Type | Required | Description | Example |
| --- | --- | --- | --- | --- | --- |
| `date` | path | date | yes | The day, YYYY-MM-DD, from 2022-01-01. | `2026-10-20` |
| `country` | query | string | no | Only this nation: USA, GB, IRE, FR or RSA. | `FR` |
| `per_page` | query | integer | no | How many to a page, 1 to 25. Default 10. | `1` |
| `page` | query | integer | no | Which page, from 1. |  |

| Field | Type | What it holds |
| --- | --- | --- |
| `meeting_id` | string | Our own id for the meeting. |
| `course_id` | string | The course. |
| `course` | string | The course name. |
| `country` | string | USA, GB, IRE, FR or RSA. |
| `date` | string | The course's own calendar day. |
| `going` | string | The going for the meeting. |
| `surface` | string | Turf, Dirt or All Weather. |
| `races` | array | Every race at the meeting in full, with its finishing order. |

Errors: 400 `bad_request`, 401 `unauthorized`, 403 `upgrade_required`, 404 `not_found`, 422 `unknown_parameter`, 429 `rate_limited`, 429 `quota_exceeded`, 503 `server_error`

Sample answer:

```json
{
  "data": [
    {
      "meeting_id": "mt_31tcke0x",
      "course_id": "cr_t6pv23df",
      "course": "Bordeaux Le Bouscat",
      "country": "FR",
      "date": "2026-10-20",
      "going": "Soft",
      "surface": "Turf",
      "races": [
        {
          "race_id": "rc_9c9cmvvp",
          "meeting_id": "mt_31tcke0x",
          "course_id": "cr_t6pv23df",
          "course": "Bordeaux Le Bouscat",
          "country": "FR",
          "date": "2026-10-20",
          "time": "09:20",
          "off_time": "09:20:42",
          "name": "Prix De Suffren Stakes",
          "race_type": "Flat",
          "class": null,
          "distance": "1m 1f 97y",
          "going": "Soft",
          "surface": "Turf",
          "handicap": false,
          "runners": 6,
          "result_available": true,
          "winning_time": "2:09.51",
          "finishing_order": [
            {
              "position": 1,
              "status": "finished",
              "distance_beaten": null,
              "cloth": 3,
              "draw": 1,
              "horse_id": "hs_yrjp3fkv",
              "horse": "Drumard",
              "age": 5,
              "weight": "9-2",
              "jockey_id": "jk_3k4knfq5",
              "jockey": "I Mendizabal",
              "trainer_id": "tr_aawg032s",
              "trainer": "E Libaud",
              "owner_id": "ow_8d2tqgw5",
              "owner": "Hyperion Sarl"
            },
            {
              "position": 2,
              "status": "finished",
              "distance_beaten": "Head",
              "cloth": 1,
              "draw": 4,
              "horse_id": "hs_gv4m13aa",
              "horse": "Reach The Sky",
              "age": 4,
              "weight": "9-6",
              "jockey_id": "jk_2k91gz18",
              "jockey": "H Besnier",
              "trainer_id": "tr_e9xaq4z5",
              "trainer": "P Cottier",
              "owner_id": "ow_wk8mjvgp",
              "owner": "Gousserie Racing/ec D'haspel"
            },
            "… 4 more"
          ]
        },
        "… 7 more"
      ]
    }
  ],
  "meta": {
    "plan": "results",
    "window": "2022-01-01 to now",
    "data_as_of": "2026-10-21T21:19:06Z",
    "date": "2026-10-20",
    "count": 1,
    "page": 1,
    "per_page": 1,
    "total": 3,
    "total_pages": 3,
    "next_page": 2,
    "prev_page": null,
    "next_cursor": null
  }
}
```

#### `GET /v1/races/search` (Results plan)

Races by date, nation, course, race type, going or surface.

Plans: Results plan. Page: https://horseracingapi.org/documentation/races-search. MCP tool: `search_races`

| Parameter | In | Type | Required | Description | Example |
| --- | --- | --- | --- | --- | --- |
| `date_from` | query | date | no | From this day. The archive starts on 2022-01-01. | `2026-10-20` |
| `date_to` | query | date | no | Up to this day. Defaults to today. | `2026-10-20` |
| `country` | query | string | no | Only this nation: USA, GB, IRE, FR or RSA. | `FR` |
| `course_id` | query | string | no | Only this course. |  |
| `race_type` | query | string | no | Flat, Hurdle, Chase and so on. |  |
| `going` | query | string | no | The going, as published. |  |
| `surface` | query | string | no | Turf, Dirt or All Weather. |  |
| `handicap` | query | boolean | no | true for handicaps only, false for none. |  |
| `per_page` | query | integer | no | How many to a page, 1 to 100. Default 25. | `3` |
| `cursor` | query | string | no | meta.next_cursor from the previous answer, as it came. |  |

| Field | Type | What it holds |
| --- | --- | --- |
| `race_id` | string | Our own id for the race. Stable, safe to store. |
| `meeting_id` | string | The meeting it was run at. |
| `course_id` | string | The course. |
| `course` | string | Where it was run. |
| `country` | string | USA, GB, IRE, FR or RSA. |
| `date` | string | The course's own calendar day, YYYY-MM-DD. |
| `time` | string | The advertised start, course time, HH:MM. |
| `off_time` | string | When it actually started, course time, HH:MM:SS. |
| `name` | string | The race title as run. |
| `race_type` | string | Flat, Hurdle, Chase and so on, where the nation publishes it. |
| `class` | string | Where the nation publishes one, else null. |
| `distance` | string | As the course states it. |
| `going` | string | The ground as declared on the day. |
| `surface` | string | Turf, Dirt or All Weather. |
| `handicap` | boolean | Whether it was a handicap. |
| `runners` | integer | How many started. |
| `result_available` | boolean | True once the result is declared. |

Errors: 400 `bad_request`, 401 `unauthorized`, 403 `upgrade_required`, 422 `unknown_parameter`, 429 `rate_limited`, 429 `quota_exceeded`, 503 `server_error`

Sample answer:

```json
{
  "data": [
    {
      "race_id": "rc_9c9cmvvp",
      "meeting_id": "mt_31tcke0x",
      "course_id": "cr_t6pv23df",
      "course": "Bordeaux Le Bouscat",
      "country": "FR",
      "date": "2026-10-20",
      "time": "09:20",
      "off_time": "09:20:42",
      "name": "Prix De Suffren Stakes",
      "race_type": "Flat",
      "class": null,
      "distance": "1m 1f 97y",
      "going": "Soft",
      "surface": "Turf",
      "handicap": false,
      "runners": 6,
      "result_available": true
    },
    {
      "race_id": "rc_52zjchex",
      "meeting_id": "mt_31tcke0x",
      "course_id": "cr_t6pv23df",
      "course": "Bordeaux Le Bouscat",
      "country": "FR",
      "date": "2026-10-20",
      "time": "09:51",
      "off_time": "09:51:18",
      "name": "Prix Jean Des Roches De Chassay Chase",
      "race_type": "Chase",
      "class": null,
      "distance": "2m 4f 193y",
      "going": "Soft",
      "surface": "Turf",
      "handicap": false,
      "runners": 8,
      "result_available": true
    },
    "… 1 more"
  ],
  "meta": {
    "plan": "results",
    "window": "2022-01-01 to now",
    "data_as_of": "2026-10-21T21:19:06Z",
    "count": 3,
    "next_cursor": "f5EOuMvR7PUuN-sFdPEJiSxj4OIngspRG4i0QxeehqPj35gHQKXqaikPHZ_AtfOCAKxQe72sMjTK"
  }
}
```

#### `GET /v1/races/{race_id}` (Results plan)

One race in full: the conditions, the time and every runner in finishing order.

Plans: Results plan. Page: https://horseracingapi.org/documentation/races-race_id. MCP tool: `get_race`

| Parameter | In | Type | Required | Description | Example |
| --- | --- | --- | --- | --- | --- |
| `race_id` | path | string | yes | The race, as returned by any results or search call. | `rc_9c9cmvvp` |

| Field | Type | What it holds |
| --- | --- | --- |
| `race_id` | string | Our own id for the race. Stable, safe to store. |
| `meeting_id` | string | The meeting it was run at. |
| `course_id` | string | The course. |
| `course` | string | Where it was run. |
| `country` | string | USA, GB, IRE, FR or RSA. |
| `date` | string | The course's own calendar day, YYYY-MM-DD. |
| `time` | string | The advertised start, course time, HH:MM. |
| `off_time` | string | When it actually started, course time, HH:MM:SS. |
| `name` | string | The race title as run. |
| `race_type` | string | Flat, Hurdle, Chase and so on, where the nation publishes it. |
| `class` | string | Where the nation publishes one, else null. |
| `distance` | string | As the course states it. |
| `going` | string | The ground as declared on the day. |
| `surface` | string | Turf, Dirt or All Weather. |
| `handicap` | boolean | Whether it was a handicap. |
| `runners` | integer | How many started. |
| `result_available` | boolean | True once the result is declared. |
| `winning_time` | string | Where the nation publishes one, as M:SS.ss, else null. Never guessed. |
| `finishing_order` | array | Every runner in order: position, status, distance_beaten, cloth, draw, horse_id, horse, age, weight, jockey_id, jockey, trainer_id, trainer, owner_id, owner. |

Errors: 400 `bad_request`, 401 `unauthorized`, 403 `upgrade_required`, 404 `not_found`, 422 `unknown_parameter`, 429 `rate_limited`, 429 `quota_exceeded`, 503 `server_error`

Sample answer:

```json
{
  "data": {
    "race_id": "rc_9c9cmvvp",
    "meeting_id": "mt_31tcke0x",
    "course_id": "cr_t6pv23df",
    "course": "Bordeaux Le Bouscat",
    "country": "FR",
    "date": "2026-10-20",
    "time": "09:20",
    "off_time": "09:20:42",
    "name": "Prix De Suffren Stakes",
    "race_type": "Flat",
    "class": null,
    "distance": "1m 1f 97y",
    "going": "Soft",
    "surface": "Turf",
    "handicap": false,
    "runners": 6,
    "result_available": true,
    "winning_time": "2:09.51",
    "finishing_order": [
      {
        "position": 1,
        "status": "finished",
        "distance_beaten": null,
        "cloth": 3,
        "draw": 1,
        "horse_id": "hs_yrjp3fkv",
        "horse": "Drumard",
        "age": 5,
        "weight": "9-2",
        "jockey_id": "jk_3k4knfq5",
        "jockey": "I Mendizabal",
        "trainer_id": "tr_aawg032s",
        "trainer": "E Libaud",
        "owner_id": "ow_8d2tqgw5",
        "owner": "Hyperion Sarl"
      },
      {
        "position": 2,
        "status": "finished",
        "distance_beaten": "Head",
        "cloth": 1,
        "draw": 4,
        "horse_id": "hs_gv4m13aa",
        "horse": "Reach The Sky",
        "age": 4,
        "weight": "9-6",
        "jockey_id": "jk_2k91gz18",
        "jockey": "H Besnier",
        "trainer_id": "tr_e9xaq4z5",
        "trainer": "P Cottier",
        "owner_id": "ow_wk8mjvgp",
        "owner": "Gousserie Racing/ec D'haspel"
      },
      "… 4 more"
    ]
  },
  "meta": {
    "plan": "results",
    "window": "2022-01-01 to now",
    "data_as_of": "2026-10-21T21:19:06Z"
  }
}
```

### Meetings

Every meeting on a day, and one meeting with all its races.

#### `GET /v1/meetings/{date}` (Results plan)

Every meeting on a day, with its going and how many races it held.

Plans: Results plan. Page: https://horseracingapi.org/documentation/meetings-date

| Parameter | In | Type | Required | Description | Example |
| --- | --- | --- | --- | --- | --- |
| `date` | path | date | yes | The day, YYYY-MM-DD, from 2022-01-01 to today. | `2026-10-20` |
| `country` | query | string | no | Only this nation: USA, GB, IRE, FR or RSA. | `FR` |
| `per_page` | query | integer | no | How many to a page, 1 to 100. Default 50. |  |
| `page` | query | integer | no | Which page, from 1. |  |

| Field | Type | What it holds |
| --- | --- | --- |
| `meeting_id` | string | Our own id for the meeting. |
| `course_id` | string | The course. |
| `course` | string | The course name. |
| `country` | string | USA, GB, IRE, FR or RSA. |
| `date` | string | The course's own calendar day. |
| `going` | string | The going for the meeting. |
| `surface` | string | Turf, Dirt or All Weather. |
| `races` | integer | How many races it held. |

Errors: 400 `bad_request`, 401 `unauthorized`, 403 `upgrade_required`, 404 `not_found`, 422 `unknown_parameter`, 429 `rate_limited`, 429 `quota_exceeded`, 503 `server_error`

Sample answer:

```json
{
  "data": [
    {
      "meeting_id": "mt_31tcke0x",
      "course_id": "cr_t6pv23df",
      "course": "Bordeaux Le Bouscat",
      "country": "FR",
      "date": "2026-10-20",
      "going": "Soft",
      "surface": "Turf",
      "races": 8
    },
    {
      "meeting_id": "mt_mttc2aka",
      "course_id": "cr_p3dbgp5x",
      "course": "Clairefontaine",
      "country": "FR",
      "date": "2026-10-20",
      "going": "Heavy",
      "surface": "Turf",
      "races": 9
    },
    "… 1 more"
  ],
  "meta": {
    "plan": "results",
    "window": "2022-01-01 to now",
    "data_as_of": "2026-10-21T21:19:06Z",
    "date": "2026-10-20",
    "count": 3,
    "page": 1,
    "per_page": 50,
    "total": 3,
    "total_pages": 1,
    "next_page": null,
    "prev_page": null,
    "next_cursor": null
  }
}
```

#### `GET /v1/meetings/{meeting_id}` (Results plan)

A meeting and every race run at it.

Plans: Results plan. Page: https://horseracingapi.org/documentation/meetings-meeting_id. MCP tool: `get_meeting`

| Parameter | In | Type | Required | Description | Example |
| --- | --- | --- | --- | --- | --- |
| `meeting_id` | path | string | yes | The meeting. | `mt_31tcke0x` |

| Field | Type | What it holds |
| --- | --- | --- |
| `meeting_id` | string | Our own id for the meeting. |
| `course_id` | string | The course. |
| `course` | string | The course name. |
| `country` | string | USA, GB, IRE, FR or RSA. |
| `date` | string | The course's own calendar day. |
| `going` | string | The going for the meeting. |
| `surface` | string | Turf, Dirt or All Weather. |
| `races` | array | Every race, with its conditions. Open a race for its finishing order. |

Errors: 400 `bad_request`, 401 `unauthorized`, 403 `upgrade_required`, 404 `not_found`, 422 `unknown_parameter`, 429 `rate_limited`, 429 `quota_exceeded`, 503 `server_error`

Sample answer:

```json
{
  "data": {
    "meeting_id": "mt_31tcke0x",
    "course_id": "cr_t6pv23df",
    "course": "Bordeaux Le Bouscat",
    "country": "FR",
    "date": "2026-10-20",
    "going": "Soft",
    "surface": "Turf",
    "races": [
      {
        "race_id": "rc_9c9cmvvp",
        "meeting_id": "mt_31tcke0x",
        "course_id": "cr_t6pv23df",
        "course": "Bordeaux Le Bouscat",
        "country": "FR",
        "date": "2026-10-20",
        "time": "09:20",
        "off_time": "09:20:42",
        "name": "Prix De Suffren Stakes",
        "race_type": "Flat",
        "class": null,
        "distance": "1m 1f 97y",
        "going": "Soft",
        "surface": "Turf",
        "handicap": false,
        "runners": 6,
        "result_available": true
      },
      "… 7 more"
    ]
  },
  "meta": {
    "plan": "results",
    "window": "2022-01-01 to now",
    "data_as_of": "2026-10-21T21:19:06Z"
  }
}
```

### Horses

Find a horse, its profile and record, and its last ten runs.

#### `GET /v1/horses/search` (Results plan)

Find a horse by name.

Plans: Results plan. Page: https://horseracingapi.org/documentation/horses-search. MCP tool: `find_horse`

| Parameter | In | Type | Required | Description | Example |
| --- | --- | --- | --- | --- | --- |
| `q` | query | string | yes | Part of the name, two letters or more. | `Druma` |
| `per_page` | query | integer | no | How many to a page, 1 to 100. Default 25. |  |
| `cursor` | query | string | no | meta.next_cursor from the previous answer, as it came. |  |

| Field | Type | What it holds |
| --- | --- | --- |
| `horse_id` | string | Our own id for the horse. |
| `name` | string | The name it races under. |
| `sex` | string | As published. |
| `last_run` | string | The day it last ran. |

Errors: 400 `bad_request`, 401 `unauthorized`, 403 `upgrade_required`, 422 `unknown_parameter`, 429 `rate_limited`, 429 `quota_exceeded`, 503 `server_error`

Sample answer:

```json
{
  "data": [
    {
      "horse_id": "hs_s8vyb06e",
      "name": "Drumadeise",
      "sex": "m",
      "last_run": "2026-10-05"
    },
    {
      "horse_id": "hs_wja7ehed",
      "name": "Drumadoon",
      "sex": "h",
      "last_run": "2024-07-06"
    },
    "… 2 more"
  ],
  "meta": {
    "plan": "results",
    "window": "2022-01-01 to now",
    "data_as_of": "2026-10-21T21:19:06Z",
    "count": 4,
    "next_cursor": null
  }
}
```

#### `GET /v1/horses/{horse_id}` (Results plan)

A horse's profile, its current trainer and owner, and its record since 2022.

Plans: Results plan. Page: https://horseracingapi.org/documentation/horses-horse_id. MCP tool: `get_horse`

| Parameter | In | Type | Required | Description | Example |
| --- | --- | --- | --- | --- | --- |
| `horse_id` | path | string | yes | The horse. | `hs_yrjp3fkv` |

| Field | Type | What it holds |
| --- | --- | --- |
| `horse_id` | string | Our own id for the horse. |
| `name` | string | The name it races under. |
| `sex` | string | As published. |
| `colour` | string | As published, where it is. |
| `trainer_id` | string | The trainer at its latest run. |
| `trainer` | string | The trainer name. |
| `owner_id` | string | The owner at its latest run. |
| `owner` | string | The owners named in full, exactly as they race. |
| `record` | object | Since the archive starts, in our five nations: runs, wins, seconds, thirds, placed, win_pct, since, last_run. |

Errors: 400 `bad_request`, 401 `unauthorized`, 403 `upgrade_required`, 404 `not_found`, 422 `unknown_parameter`, 429 `rate_limited`, 429 `quota_exceeded`, 503 `server_error`

Sample answer:

```json
{
  "data": {
    "horse_id": "hs_yrjp3fkv",
    "name": "Drumard",
    "sex": "c",
    "colour": "bay",
    "trainer_id": "tr_aawg032s",
    "trainer": "E Libaud",
    "owner_id": "ow_8d2tqgw5",
    "owner": "Hyperion Sarl",
    "record": {
      "runs": 24,
      "wins": 7,
      "seconds": 4,
      "thirds": 3,
      "placed": 14,
      "win_pct": 0.292,
      "since": "2023-12-03",
      "last_run": "2026-10-20"
    }
  },
  "meta": {
    "plan": "results",
    "window": "2022-01-01 to now",
    "data_as_of": "2026-10-21T21:19:06Z"
  }
}
```

#### `GET /v1/horses/{horse_id}/form` (Results plan)

The last ten runs, newest first.

Plans: Results plan. Page: https://horseracingapi.org/documentation/horses-horse_id-form. MCP tool: `horse_form`

| Parameter | In | Type | Required | Description | Example |
| --- | --- | --- | --- | --- | --- |
| `horse_id` | path | string | yes | The horse. | `hs_yrjp3fkv` |

| Field | Type | What it holds |
| --- | --- | --- |
| `date` | string | The day of the run. |
| `race_id` | string | The race, for the full result. |
| `course_id` | string | The course. |
| `course` | string | The course name. |
| `country` | string | USA, GB, IRE, FR or RSA. |
| `race_type` | string | Flat, Hurdle, Chase and so on. |
| `class` | string | Where published, else null. |
| `distance` | string | As the course states it. |
| `going` | string | The ground on the day. |
| `surface` | string | Turf, Dirt or All Weather. |
| `runners` | integer | How many started. |
| `position` | integer | Where it finished, or null. |
| `status` | string | finished, or how the run ended: pulled_up, fell and so on. |
| `distance_beaten` | string | How far behind the winner, where published. |
| `weight` | string | The weight carried. |
| `jockey_id` | string | Who rode it. |
| `jockey` | string | The jockey name. |
| `trainer_id` | string | Who trained it then. |
| `trainer` | string | The trainer name. |

Errors: 400 `bad_request`, 401 `unauthorized`, 403 `upgrade_required`, 404 `not_found`, 422 `unknown_parameter`, 429 `rate_limited`, 429 `quota_exceeded`, 503 `server_error`

Sample answer:

```json
{
  "data": [
    {
      "date": "2026-10-20",
      "race_id": "rc_9c9cmvvp",
      "course_id": "cr_t6pv23df",
      "course": "Bordeaux Le Bouscat",
      "country": "FR",
      "race_type": "Flat",
      "class": null,
      "distance": "1m 1f 97y",
      "going": "Soft",
      "surface": "Turf",
      "runners": 6,
      "position": 1,
      "status": "finished",
      "distance_beaten": null,
      "weight": "9-2",
      "jockey_id": "jk_3k4knfq5",
      "jockey": "I Mendizabal",
      "trainer_id": "tr_aawg032s",
      "trainer": "E Libaud"
    },
    {
      "date": "2026-09-21",
      "race_id": "rc_n7exgvs9",
      "course_id": "cr_k3311dqx",
      "course": "Angers",
      "country": "FR",
      "race_type": "Flat",
      "class": null,
      "distance": "1m 2f 96y",
      "going": "Good",
      "surface": "Turf",
      "runners": 6,
      "position": 1,
      "status": "finished",
      "distance_beaten": null,
      "weight": "8-13",
      "jockey_id": "jk_tvq9x47z",
      "jockey": "A Pouchin",
      "trainer_id": "tr_aawg032s",
      "trainer": "E Libaud"
    },
    "… 8 more"
  ],
  "meta": {
    "plan": "results",
    "window": "2022-01-01 to now",
    "data_as_of": "2026-10-21T21:19:06Z",
    "count": 10
  }
}
```

### Jockeys

Find a jockey, their record, and their last ten rides.

#### `GET /v1/jockeys/search` (Results plan)

Find a jockey by name.

Plans: Results plan. Page: https://horseracingapi.org/documentation/jockeys-search. MCP tool: `find_jockey`

| Parameter | In | Type | Required | Description | Example |
| --- | --- | --- | --- | --- | --- |
| `q` | query | string | yes | Part of the name, two letters or more. | `Mendizabal` |
| `per_page` | query | integer | no | How many to a page, 1 to 100. Default 25. |  |
| `cursor` | query | string | no | meta.next_cursor from the previous answer, as it came. |  |

| Field | Type | What it holds |
| --- | --- | --- |
| `jockey_id` | string | Our own id for the jockey. |
| `name` | string | The name they ride under. |

Errors: 400 `bad_request`, 401 `unauthorized`, 403 `upgrade_required`, 422 `unknown_parameter`, 429 `rate_limited`, 429 `quota_exceeded`, 503 `server_error`

Sample answer:

```json
{
  "data": [
    {
      "jockey_id": "jk_3k4knfq5",
      "name": "I Mendizabal"
    },
    {
      "jockey_id": "jk_74zk91mt",
      "name": "I.Mendizabal"
    },
    "… 1 more"
  ],
  "meta": {
    "plan": "results",
    "window": "2022-01-01 to now",
    "data_as_of": "2026-10-21T21:19:06Z",
    "count": 3,
    "next_cursor": null
  }
}
```

#### `GET /v1/jockeys/{jockey_id}` (Results plan)

A jockey's profile and record since 2022.

Plans: Results plan. Page: https://horseracingapi.org/documentation/jockeys-jockey_id. MCP tool: `get_jockey`

| Parameter | In | Type | Required | Description | Example |
| --- | --- | --- | --- | --- | --- |
| `jockey_id` | path | string | yes | The jockey. | `jk_3k4knfq5` |

| Field | Type | What it holds |
| --- | --- | --- |
| `jockey_id` | string | Our own id for the jockey. |
| `name` | string | The name they ride under. |
| `record` | object | Since the archive starts, in our five nations: runs, wins, seconds, thirds, placed, win_pct, since, last_run. |

Errors: 400 `bad_request`, 401 `unauthorized`, 403 `upgrade_required`, 404 `not_found`, 422 `unknown_parameter`, 429 `rate_limited`, 429 `quota_exceeded`, 503 `server_error`

Sample answer:

```json
{
  "data": {
    "jockey_id": "jk_3k4knfq5",
    "name": "I Mendizabal",
    "record": {
      "runs": 3612,
      "wins": 291,
      "seconds": 331,
      "thirds": 369,
      "placed": 991,
      "win_pct": 0.081,
      "since": "2025-10-21",
      "last_run": "2026-10-21"
    }
  },
  "meta": {
    "plan": "results",
    "window": "2022-01-01 to now",
    "data_as_of": "2026-10-21T21:19:06Z"
  }
}
```

#### `GET /v1/jockeys/{jockey_id}/rides` (Results plan)

The last ten rides, newest first.

Plans: Results plan. Page: https://horseracingapi.org/documentation/jockeys-jockey_id-rides. MCP tool: `jockey_rides`

| Parameter | In | Type | Required | Description | Example |
| --- | --- | --- | --- | --- | --- |
| `jockey_id` | path | string | yes | The jockey. | `jk_3k4knfq5` |

| Field | Type | What it holds |
| --- | --- | --- |
| `date` | string | The day of the run. |
| `race_id` | string | The race, for the full result. |
| `course_id` | string | The course. |
| `course` | string | The course name. |
| `country` | string | USA, GB, IRE, FR or RSA. |
| `race_type` | string | Flat, Hurdle, Chase and so on. |
| `class` | string | Where published, else null. |
| `distance` | string | As the course states it. |
| `going` | string | The ground on the day. |
| `surface` | string | Turf, Dirt or All Weather. |
| `runners` | integer | How many started. |
| `position` | integer | Where it finished, or null. |
| `status` | string | finished, or how the run ended: pulled_up, fell and so on. |
| `distance_beaten` | string | How far behind the winner, where published. |
| `weight` | string | The weight carried. |
| `horse_id` | string | The horse ridden. |
| `horse` | string | The horse name. |
| `trainer_id` | string | Its trainer. |
| `trainer` | string | The trainer name. |

Errors: 400 `bad_request`, 401 `unauthorized`, 403 `upgrade_required`, 404 `not_found`, 422 `unknown_parameter`, 429 `rate_limited`, 429 `quota_exceeded`, 503 `server_error`

Sample answer:

```json
{
  "data": [
    {
      "date": "2026-10-21",
      "race_id": "rc_gg85jmrt",
      "course_id": "cr_nj571bgj",
      "course": "Pau",
      "country": "FR",
      "race_type": "Flat",
      "class": null,
      "distance": "1m 1f 207y",
      "going": "Standard",
      "surface": "All Weather",
      "runners": 7,
      "position": 3,
      "status": "finished",
      "distance_beaten": "1 1/2 length",
      "weight": "8-11",
      "horse_id": "hs_yb08315h",
      "horse": null,
      "trainer_id": "tr_mk57zjck",
      "trainer": "D De Watrigant"
    },
    {
      "date": "2026-10-21",
      "race_id": "rc_04mrfmfv",
      "course_id": "cr_nj571bgj",
      "course": "Pau",
      "country": "FR",
      "race_type": null,
      "class": null,
      "distance": "1m 1f 207y",
      "going": "Standard",
      "surface": "All Weather",
      "runners": 12,
      "position": 3,
      "status": "finished",
      "distance_beaten": null,
      "weight": "8-12",
      "horse_id": "hs_kf232ksx",
      "horse": null,
      "trainer_id": "tr_vpvjtfgx",
      "trainer": "F R Monfort"
    },
    "… 8 more"
  ],
  "meta": {
    "plan": "results",
    "window": "2022-01-01 to now",
    "data_as_of": "2026-10-21T21:19:06Z",
    "count": 10
  }
}
```

### Trainers and owners

Trainers and owners, opened from any result they appear in.

#### `GET /v1/trainers/{trainer_id}` (Results plan)

A trainer's profile and record since 2022.

Plans: Results plan. Page: https://horseracingapi.org/documentation/trainers-trainer_id. MCP tool: `get_trainer`

| Parameter | In | Type | Required | Description | Example |
| --- | --- | --- | --- | --- | --- |
| `trainer_id` | path | string | yes | The trainer, from any result. | `tr_aawg032s` |

| Field | Type | What it holds |
| --- | --- | --- |
| `trainer_id` | string | Our own id for the trainer. |
| `name` | string | The name they train under. |
| `record` | object | Since the archive starts, in our five nations: runs, wins, seconds, thirds, placed, win_pct, since, last_run. |

Errors: 400 `bad_request`, 401 `unauthorized`, 403 `upgrade_required`, 404 `not_found`, 422 `unknown_parameter`, 429 `rate_limited`, 429 `quota_exceeded`, 503 `server_error`

Sample answer:

```json
{
  "data": {
    "trainer_id": "tr_aawg032s",
    "name": "E Libaud",
    "record": {
      "runs": 948,
      "wins": 95,
      "seconds": 106,
      "thirds": 97,
      "placed": 298,
      "win_pct": 0.1,
      "since": "2025-10-24",
      "last_run": "2026-10-20"
    }
  },
  "meta": {
    "plan": "results",
    "window": "2022-01-01 to now",
    "data_as_of": "2026-10-21T21:19:06Z"
  }
}
```

#### `GET /v1/owners/{owner_id}` (Results plan)

An owner's record since 2022.

Plans: Results plan. Page: https://horseracingapi.org/documentation/owners-owner_id. MCP tool: `get_owner`

| Parameter | In | Type | Required | Description | Example |
| --- | --- | --- | --- | --- | --- |
| `owner_id` | path | string | yes | The owner, from any result. | `ow_8d2tqgw5` |

| Field | Type | What it holds |
| --- | --- | --- |
| `owner_id` | string | Our own id for the owner. |
| `name` | string | The owners named in full, exactly as they race. |
| `record` | object | Since the archive starts, in our five nations: runs, wins, seconds, thirds, placed, win_pct, since, last_run. |

Errors: 400 `bad_request`, 401 `unauthorized`, 403 `upgrade_required`, 404 `not_found`, 422 `unknown_parameter`, 429 `rate_limited`, 429 `quota_exceeded`, 503 `server_error`

Sample answer:

```json
{
  "data": {
    "owner_id": "ow_8d2tqgw5",
    "name": "Hyperion Sarl",
    "record": {
      "runs": 161,
      "wins": 18,
      "seconds": 20,
      "thirds": 18,
      "placed": 56,
      "win_pct": 0.112,
      "since": "2022-01-01",
      "last_run": "2026-10-20"
    }
  },
  "meta": {
    "plan": "results",
    "window": "2022-01-01 to now",
    "data_as_of": "2026-10-21T21:19:06Z"
  }
}
```

### Reference

The five nations, every course, and your own usage.

#### `GET /v1/countries`

The five nations and what each one publishes.

Plans: Free key, Results plan. Page: https://horseracingapi.org/documentation/countries

| Field | Type | What it holds |
| --- | --- | --- |
| `country` | string | USA, GB, IRE, FR or RSA. |
| `name` | string | The nation. |
| `note` | string | Anything worth knowing, else null. |
| `from` | string | The first day served: 2022-01-01. |
| `publishes` | object | finishing_order, class and winning_time: true where the nation publishes them. |

Errors: 400 `bad_request`, 401 `unauthorized`, 422 `unknown_parameter`, 429 `rate_limited`, 429 `quota_exceeded`, 503 `server_error`

Sample answer:

```json
{
  "data": [
    {
      "country": "USA",
      "name": "United States",
      "note": null,
      "from": "2022-01-01",
      "publishes": {
        "finishing_order": true,
        "class": false,
        "winning_time": true
      }
    },
    {
      "country": "GB",
      "name": "Great Britain",
      "note": null,
      "from": "2022-01-01",
      "publishes": {
        "finishing_order": true,
        "class": true,
        "winning_time": true
      }
    },
    "… 3 more"
  ],
  "meta": {
    "plan": "free",
    "window": "yesterday",
    "data_as_of": "2026-10-21T21:19:06Z",
    "count": 5
  }
}
```

#### `GET /v1/courses`

Every course in the five nations, with its latest meeting.

Plans: Free key, Results plan. Page: https://horseracingapi.org/documentation/courses. MCP tool: `list_courses`

| Parameter | In | Type | Required | Description | Example |
| --- | --- | --- | --- | --- | --- |
| `country` | query | string | no | Only this nation: USA, GB, IRE, FR or RSA. | `FR` |
| `page` | query | integer | no | Which page, from 1. |  |

| Field | Type | What it holds |
| --- | --- | --- |
| `course_id` | string | Our own id for the course. |
| `name` | string | The course. |
| `country` | string | USA, GB, IRE, FR or RSA. |
| `last_meeting` | string | The day it last raced. |

Errors: 400 `bad_request`, 401 `unauthorized`, 422 `unknown_parameter`, 429 `rate_limited`, 429 `quota_exceeded`, 503 `server_error`

Sample answer:

```json
{
  "data": [
    {
      "course_id": "cr_cgp1pfpm",
      "name": "Agen",
      "country": "FR",
      "last_meeting": "2026-06-20"
    },
    {
      "course_id": "cr_ergv3yxp",
      "name": "Agon-Coutainville",
      "country": "FR",
      "last_meeting": "2026-07-05"
    },
    "… 95 more"
  ],
  "meta": {
    "plan": "free",
    "window": "yesterday",
    "data_as_of": "2026-10-21T21:19:06Z",
    "count": 97,
    "page": 1,
    "per_page": 500,
    "total": 97,
    "total_pages": 1,
    "next_page": null,
    "prev_page": null,
    "next_cursor": null
  }
}
```

#### `GET /v1/courses/{course_id}` (Results plan)

One course: its surfaces, race types and distances.

Plans: Results plan. Page: https://horseracingapi.org/documentation/courses-course_id

| Parameter | In | Type | Required | Description | Example |
| --- | --- | --- | --- | --- | --- |
| `course_id` | path | string | yes | The course. | `cr_t6pv23df` |

| Field | Type | What it holds |
| --- | --- | --- |
| `course_id` | string | Our own id for the course. |
| `name` | string | The course. |
| `country` | string | USA, GB, IRE, FR or RSA. |
| `surfaces` | array | Turf, Dirt or All Weather, as raced on since 2022. |
| `race_types` | array | The race types run there since 2022, where published. |
| `distances` | array | The distances it races over, most used first. |
| `last_meeting` | string | The day it last raced. |

Errors: 400 `bad_request`, 401 `unauthorized`, 403 `upgrade_required`, 404 `not_found`, 422 `unknown_parameter`, 429 `rate_limited`, 429 `quota_exceeded`, 503 `server_error`

Sample answer:

```json
{
  "data": {
    "course_id": "cr_t6pv23df",
    "name": "Bordeaux Le Bouscat",
    "country": "FR",
    "surfaces": [
      "Turf"
    ],
    "race_types": [
      "Flat",
      "Trot",
      "Hurdle",
      "… 1 more"
    ],
    "distances": [
      "1m 5f 38y",
      "1m 1f 97y",
      "7f 209y",
      "… 14 more"
    ],
    "last_meeting": "2026-10-20"
  },
  "meta": {
    "plan": "results",
    "window": "2022-01-01 to now",
    "data_as_of": "2026-10-21T21:19:06Z"
  }
}
```

#### `GET /v1/usage`

Your calls today and this month, and your limits. Never counted against them.

Plans: Free key, Results plan. Page: https://horseracingapi.org/documentation/usage. MCP tool: `my_usage`

| Field | Type | What it holds |
| --- | --- | --- |
| `plan` | string | free or results. |
| `plan_name` | string | Free key or Results plan. |
| `window` | string | What your plan reaches. |
| `calls_today` | integer | Calls since 00:00 UTC. |
| `calls_month` | integer | Calls since the 1st, UTC. |
| `per_minute` | integer | Calls allowed in any minute. |
| `per_day` | integer | Calls allowed a day, or null. |
| `per_month` | integer | Calls allowed a month, or null. |
| `resets` | object | day and month: when each count starts again, UTC. |

Errors: 400 `bad_request`, 401 `unauthorized`, 422 `unknown_parameter`, 503 `server_error`

Sample answer:

```json
{
  "data": {
    "plan": "free",
    "plan_name": "Free key",
    "window": "yesterday",
    "calls_today": 0,
    "calls_month": 0,
    "per_minute": 10,
    "per_day": 50,
    "per_month": null,
    "resets": {
      "day": "2026-10-22T00:00:00Z",
      "month": "2026-11-01T00:00:00Z"
    }
  },
  "meta": {
    "plan": "free",
    "window": "yesterday",
    "data_as_of": "2026-10-21T21:19:06Z"
  }
}
```

## MCP server

Address: https://horseracingapi.org/mcp. Add it as a custom connector in Claude or ChatGPT (sign in with your account, no key to paste), or with `X-API-Key` in Cursor and Claude Code. Each tool takes its endpoint's parameters and answers in the same shape, under your plan.

| Tool | Calls | Plan | What it does |
| --- | --- | --- | --- |
| `latest_results` | `GET /v1/results/latest` | Results plan | The newest results, as they are declared, each with its full finishing order. |
| `yesterdays_results` | `GET /v1/results/yesterday` | Free key | Every result from yesterday, at each course's own day, meeting by meeting. |
| `results_on_date` | `GET /v1/results/{date}` | Results plan | Every result for any day since 1 January 2022, meeting by meeting. |
| `search_races` | `GET /v1/races/search` | Results plan | Races by date, nation, course, race type, going or surface. |
| `get_race` | `GET /v1/races/{race_id}` | Results plan | One race in full: the conditions, the time and every runner in finishing order. |
| `get_meeting` | `GET /v1/meetings/{meeting_id}` | Results plan | A meeting and every race run at it. |
| `find_horse` | `GET /v1/horses/search` | Results plan | Find a horse by name. |
| `get_horse` | `GET /v1/horses/{horse_id}` | Results plan | A horse's profile, its current trainer and owner, and its record since 2022. |
| `horse_form` | `GET /v1/horses/{horse_id}/form` | Results plan | The last ten runs, newest first. |
| `find_jockey` | `GET /v1/jockeys/search` | Results plan | Find a jockey by name. |
| `get_jockey` | `GET /v1/jockeys/{jockey_id}` | Results plan | A jockey's profile and record since 2022. |
| `jockey_rides` | `GET /v1/jockeys/{jockey_id}/rides` | Results plan | The last ten rides, newest first. |
| `get_trainer` | `GET /v1/trainers/{trainer_id}` | Results plan | A trainer's profile and record since 2022. |
| `get_owner` | `GET /v1/owners/{owner_id}` | Results plan | An owner's record since 2022. |
| `list_courses` | `GET /v1/courses` | Free key | Every course in the five nations, with its latest meeting. |
| `my_usage` | `GET /v1/usage` | Free key | Your calls today and this month, and your limits. Never counted against them. |

## Guides

### Quick start

A key, your first call and what comes back, in three minutes. (https://horseracingapi.org/documentation/quick-start)

Every call is an HTTPS GET to `https://api.horseracingapi.org` with your key in a header. The answer is JSON: your data under `data`, and under `meta` your plan and how fresh the data is.

### 1. Get a key

Sign in with Google on the [free key](https://horseracingapi.org/free-key) page. Your key is made at once and saved in this documentation, so every Run button works straight away. It starts `hra_live_`.

### 2. Make your first call

```
curl https://api.horseracingapi.org/v1/results/yesterday \
  -H "X-API-Key: hra_live_…"
```

That is every result from yesterday, meeting by meeting, in all five nations. It works on the free key.

### 3. Read the answer

Each meeting carries its course, nation, going and races. Each race carries its conditions, the winning time where the nation publishes one, and `finishing_order`: every runner in order, with its position, horse, jockey, trainer and owner, each with our own id you can open with another call.

- Open a race: [GET /v1/races/{race_id}](https://horseracingapi.org/documentation/races-race_id)
- Open a horse: [GET /v1/horses/{horse_id}](https://horseracingapi.org/documentation/horses-horse_id)
- Its last 10 runs: [GET /v1/horses/{horse_id}/form](https://horseracingapi.org/documentation/horses-horse_id-form)

Next: [Authentication](https://horseracingapi.org/documentation/authentication), then [Results and races](https://horseracingapi.org/documentation/results-and-races).

### Authentication

Your key, the two ways to send it, and rotating it. (https://horseracingapi.org/documentation/authentication)

One key per account, made when you sign up. It follows your plan: upgrade and the same key reads everything on the next call.

### Sending it

Send it in either header:

```
X-API-Key: hra_live_…
Authorization: Bearer hra_live_…
```

Never in the address. A call with `?key=` is refused with a 400, because a key in a URL ends up in logs and browser history.

### Keeping it safe

Keep it on your server and out of pages people load. Your account shows its first characters and copies it in full when you press Copy.

### Rotating it

In your account, under Keys, Rotate makes a new key at once. The old one keeps working for 24 hours, so you can swap it in your code without a gap, then it stops.

A key we do not know gets a 401 `unauthorized`. See [Errors](https://horseracingapi.org/documentation/errors).

### Plans and limits

What each plan reads, its rate limit and its allowance. (https://horseracingapi.org/documentation/plans-and-limits)

| Plan | Price | Reads | Rate | Allowance |
| --- | --- | --- | --- | --- |
| Free key | Free | Yesterday's results, 4 endpoints | 10 a minute | 50 a day |
| Results plan | $29 a month or $299 a year | Every race since 1 January 2022, live, all 19 endpoints | 60 a minute | 30,000 a month |

### Headers on every answer

- `X-RateLimit-Limit`: calls allowed in a minute
- `X-RateLimit-Remaining`: left this minute
- `X-RateLimit-Reset`: when the minute starts again, in Unix seconds
- `X-Quota-Remaining`: left today on the free key, this month on the Results plan

### When you reach one

Over the rate: 429 `rate_limited`, with `Retry-After` in seconds. Allowance used: 429 `quota_exceeded`, saying when it comes back (00:00 UTC each day on the free key, the 1st of the month on the Results plan). [GET /v1/usage](https://horseracingapi.org/documentation/usage) shows where you stand and never counts against you.

An endpoint your plan does not open answers 403 `upgrade_required`, naming the plan that does. See [Pricing](https://horseracingapi.org/pricing).

### Pagination

Pages on lists of known length, a cursor on searches. (https://horseracingapi.org/documentation/pagination)

### Pages

Lists of known length (results by day, meetings, courses) take `page` and `per_page`. The first answer's `meta` carries `count`, `page`, `per_page`, `total`, `total_pages`, `next_page` and `prev_page` (null at the ends).

```
curl "https://api.horseracingapi.org/v1/results/2024-06-01?country=GB&page=2" -H "X-API-Key: hra_live_…"
```

### Cursors

Searches (races, horses, jockeys) take `per_page` and `cursor`. Pass `meta.next_cursor` from one answer as `cursor` on the next, until it is null. A cursor is ours and opaque: send it as it came.

```
curl "https://api.horseracingapi.org/v1/races/search?country=IRE&cursor=…" -H "X-API-Key: hra_live_…"
```

A list with nothing in it is a 200 with `count` 0, never a 404.

### Errors

Every error code, and what to do about each. (https://horseracingapi.org/documentation/errors)

Every error is one shape:

```
{"error": {"code": "upgrade_required", "message": "…", "plan": "results", "docs": "https://horseracingapi.org/documentation/errors"}}
```

The code is for your code and never changes; the message is for people.

| Status | Code | What to do |
| --- | --- | --- |
| 400 | bad_request | A value is the wrong shape: a date that is not YYYY-MM-DD, an id that is not ours, a key sent in the address. The message names it. |
| 401 | unauthorized | No key, or one we do not know. Send it in X-API-Key; check your account for the current one. |
| 403 | upgrade_required | Your plan does not open this endpoint. The answer names the plan that does. |
| 404 | not_found | No record with that id, a date before 1 January 2022, or no endpoint at that address. |
| 422 | unknown_parameter | A parameter this endpoint does not take. The answer lists the ones it does. |
| 429 | rate_limited | Over your calls a minute. Wait the Retry-After seconds. |
| 429 | quota_exceeded | Your allowance is used. The answer says when it comes back. |
| 500, 503 | server_error | On our side. Try again in a minute; it is logged and we see it. |

### Results and races

Days of results, single races, the newest results and race search. (https://horseracingapi.org/documentation/results-and-races)

A result is a race with its finishing order: every runner who took part, in order, with its position, how it ended (finished, pulled up, fell and so on), the distance beaten, its cloth and draw, and the horse, jockey, trainer and owner, each with our own id.

### The common jobs

- A day's results: [GET /v1/results/{date}](https://horseracingapi.org/documentation/results-date), meeting by meeting, filter with `country`.

- Yesterday, on any plan: [GET /v1/results/yesterday](https://horseracingapi.org/documentation/results-yesterday).

- The newest as they are declared: [GET /v1/results/latest](https://horseracingapi.org/documentation/results-latest).

- One race in full: [GET /v1/races/{race_id}](https://horseracingapi.org/documentation/races-race_id).

- Races by date range, nation, course, race type, going or surface: [GET /v1/races/search](https://horseracingapi.org/documentation/races-search).

### Good to know

- Dates are the course's own calendar day. Times are as published at the course.

- The archive starts on 1 January 2022, the year the United States began publishing the full finishing order.

- A race not yet declared shows its conditions with `result_available` false and an empty finishing order.

- `winning_time` and `class` are null where a nation does not publish them. Never guessed. See [Coverage](https://horseracingapi.org/coverage).

### Meetings

Every meeting on a day, and one meeting with its races. (https://horseracingapi.org/documentation/meetings)

A meeting is one course's racing on one day: its going, its surface and its races.

- Every meeting on a day: [GET /v1/meetings/{date}](https://horseracingapi.org/documentation/meetings-date), with how many races each held.

- One meeting and every race at it: [GET /v1/meetings/{meeting_id}](https://horseracingapi.org/documentation/meetings-meeting_id). Open a race for its finishing order.

Every race carries its `meeting_id`, so you can always step back from a race to its meeting.

### Horses and jockeys

Finding a horse or a jockey, their records and their last ten runs. (https://horseracingapi.org/documentation/horses-and-jockeys)

### Finding them

Search by part of the name, two letters or more: [GET /v1/horses/search](https://horseracingapi.org/documentation/horses-search) and [GET /v1/jockeys/search](https://horseracingapi.org/documentation/jockeys-search). Or take the ids straight from any result.

### Horses

[GET /v1/horses/{horse_id}](https://horseracingapi.org/documentation/horses-horse_id) gives the profile, its trainer and owner at its latest run, and its record since 2022 in our five nations: runs, wins, seconds, thirds and the win rate. [GET /v1/horses/{horse_id}/form](https://horseracingapi.org/documentation/horses-horse_id-form) gives its last 10 runs, newest first.

### Jockeys

[GET /v1/jockeys/{jockey_id}](https://horseracingapi.org/documentation/jockeys-jockey_id) gives the record since 2022, and [GET /v1/jockeys/{jockey_id}/rides](https://horseracingapi.org/documentation/jockeys-jockey_id-rides) the last 10 rides with the horse and trainer.

No sire, dam or breeding is carried, and no odds.

### Trainers and owners

Opening a trainer or an owner from any result. (https://horseracingapi.org/documentation/trainers-and-owners)

Trainers and owners open from any result they appear in: every runner carries `trainer_id` and `owner_id`.

- [GET /v1/trainers/{trainer_id}](https://horseracingapi.org/documentation/trainers-trainer_id): the record since 2022 in our five nations.

- [GET /v1/owners/{owner_id}](https://horseracingapi.org/documentation/owners-owner_id): the same for an owner, named in full exactly as they race, partnerships included.

### Courses and nations

The five nations, every course in them, and what each publishes. (https://horseracingapi.org/documentation/courses-and-nations)

- [GET /v1/countries](https://horseracingapi.org/documentation/countries): the five nations, USA, GB, IRE (with Northern Ireland), FR and RSA, and whether each publishes race class and winning times.

- [GET /v1/courses](https://horseracingapi.org/documentation/courses): every course, with its nation and latest meeting. Both work on the free key.

- [GET /v1/courses/{course_id}](https://horseracingapi.org/documentation/courses-course_id): one course, its surfaces, race types and distances.

Use a `course_id` to narrow [race search](https://horseracingapi.org/documentation/races-search) to one course.

### MCP server

Ask for results in plain English from Claude, ChatGPT, Cursor or Claude Code. (https://horseracingapi.org/documentation/mcp)

The MCP server puts the API in front of an assistant as tools. The address is `https://horseracingapi.org/mcp`.

### Claude and ChatGPT

Add a custom connector with that address. You sign in with your HorseRacingAPI account once; there is no key to paste. Your plan applies as it does to the API.

### Cursor and Claude Code

Add the server with your key in a header:

```
claude mcp add --transport http horseracingapi https://horseracingapi.org/mcp --header "X-API-Key: hra_live_…"
```

### An example

Ask "Who won the Derby at Epsom in 2024, and what has the winner done since?" The assistant calls `search_races`, then `get_race`, then `horse_form`, and answers with the result and the last runs.

Every tool is listed on [MCP tools](https://horseracingapi.org/documentation/mcp-tools).

### Agent skill

One file that teaches an agent the whole API. (https://horseracingapi.org/documentation/agent-skill)

[/skill.md](https://horseracingapi.org/skill.md) is one markdown file that tells an agent what HorseRacingAPI is for, how to get a key, which calls answer the common questions, the limits and the errors.

Save it as `~/.claude/skills/horseracingapi/SKILL.md`, or upload it as a skill in Claude's settings. Agents that look for skills find it at `https://horseracingapi.org/.well-known/agent-skills/index.json`.

For everything at once, [the full manual](https://horseracingapi.org/documentation/manual.md) is the whole API in one file.

### Changelog

What changed, newest first. (https://horseracingapi.org/documentation/changelog)

### 21 October 2026

First release: 19 endpoints over every race in the United States, Great Britain, Ireland, France and South Africa since 1 January 2022, live as results are declared. The free key reads yesterday's results on 4 of them.

## Errors

Every error is `{"error": {"code", "message", "docs"}}`, with `plan` on `upgrade_required` and `allowed` on `unknown_parameter`. The code never changes; the message is for people. The full list: https://horseracingapi.org/documentation/errors

## About this document

Generated from the API's own registry, guides and pricing configuration, never written by hand. The date at the top is the newest change to those sources: two copies with the same date are the same. Also: https://horseracingapi.org/openapi.json (OpenAPI 3.1), https://horseracingapi.org/mcp (MCP server), https://horseracingapi.org/skill.md (agent skill), https://horseracingapi.org/llms.txt (index).
