APIHorseRacing

Documentation / Races & markets

Finishing order

GET /v1/races/{race_id}/result

Finishing order, casualties, dividends and prize money.

Available on

Free key 24h – 7d
Live today + 3 days
Archive back to 2017
Complete 2017 → upcoming
Complete + Analyst 2017 → upcoming

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.

Parameters

NameInTypeRequiredDescription
race_id path string yes Opaque race identifier.

Request

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

Response

200 OK application/json harvested 8 days ago
{
    "meta": {
        "request_id": "87908b1270b3d626",
        "data_as_of": "2026-09-12T05:10:27+00:00",
        "plan": "analyst",
        "window": "2017 → upcoming",
        "count": 5
    },
    "data": {
        "race_id": "rc_1P005G7",
        "course": "Fontwell",
        "date": "2021-02-28",
        "off_time": "16:40:58",
        "winning_time": "7m 10.95s",
        "book_percentage": 127.62,
        "placed": 3,
        "dividends": {
            "currency": "GBP",
            "win": 4.7,
            "place": [
                1.4,
                1.1,
                "… 1 more"
            ],
            "exacta": 6.9,
            "trifecta": 86.9,
            "straight_forecast": 5.4,
            "swingers": [
                1.2,
                11.2,
                "… 1 more"
            ],
            "place_pot": {
                "pot": 5.7,
                "pool": 80542.57,
                "win_stakes": 10142.17
            },
            "quad_pot": {
                "pot": 1.8,
                "pool": 6901.81,
                "win_stakes": 2706.56
            }
        },
        "prizes": [
            {
                "position": 1,
                "prize": 1949
            },
            {
                "position": 2,
                "prize": 572
            },
            "… 2 more"
        ],
        "result": [
            {
                "position": 1,
                "cloth": 13,
                "horse_id": "hs_V8JGH6",
                "horse": "The Galloping Bear",
                "sp": "10/3",
                "sp_decimal": 4.3333,
                "beaten_distance": null,
                "casualty": null,
                "jockey": "Millie Wonnacott",
                "trainer": "Ben Clarke"
            },
            {
                "position": 2,
                "cloth": 4,
                "horse_id": "hs_SJX1WR",
                "horse": "Sametegal",
                "sp": "2/5",
                "sp_decimal": 1.4,
                "beaten_distance": null,
                "casualty": null,
                "jockey": "H Cobden",
                "trainer": "P F Nicholls"
            },
            "… 3 more"
        ],
        "unplaced": [
            {
                "position": null,
                "cloth": 1,
                "horse_id": "hs_3QX1NCY",
                "horse": "Djin Conti",
                "sp": "16/1",
                "sp_decimal": 17,
                "beaten_distance": null,
                "casualty": "PulledUp",
                "jockey": "Tom Cannon",
                "trainer": "S C Robinson"
            },
            {
                "position": null,
                "cloth": 2,
                "horse_id": "hs_YBW9N7",
                "horse": "Le Reve",
                "sp": "50/1",
                "sp_decimal": 51,
                "beaten_distance": null,
                "casualty": "PulledUp",
                "jockey": "Bryony Frost",
                "trainer": "J P Owen"
            },
            "… 9 more"
        ]
    }
}

A real response from /v1/races/rc_1P005G7/result, captured by calling the live API rather than written by hand.

Response fields

Each field is described once and rendered on every endpoint that returns it, so the wording cannot drift between pages.

FieldTypeWhat it is
race_id string Opaque race identifier. Stable, and safe to store.
course string Course name.
date date Race date, YYYY-MM-DD.
off_time string Actual off time where recorded.
winning_time string Winning time as published.
book_percentage number The field's implied probabilities added up. 100 would be a fair book; the excess is the margin. Ours average 117 in small fields and 133 in sixteen or more.
placed integer How many places the race paid.
dividends object Tote returns to a one unit stake.
prizes array Prize money by finishing position.
position integer Finishing position. Null means the runner did not complete or was not published below the placings.
cloth integer Cloth number.
horse_id string Opaque horse identifier.
horse string Horse name.
sp string Starting price, fractional, as returned.
sp_decimal number Starting price as a decimal, computed once at load so you are not parsing fractions per row.
beaten_distance string Distance behind the horse in front.
casualty string Why a runner did not complete: PulledUp, Fell, UnseatedRider, BroughtDown, RefusedToRace, RanOut.
jockey string Rider name.
trainer string Trainer name.

What varies by jurisdiction

Winning times are patchy outside Britain and Ireland

Where a time exists it is as published. /v1/courses/{id}/standard-times reports the median per distance and counts the implausible ones rather than quietly serving them.

Tote dividends are published in some jurisdictions and not others

An absent dividend is a fact about the race rather than a gap in the data, and the endpoint says so instead of returning an error.

Not every jurisdiction publishes a full finishing order

America publishes the placings and nothing below them, so position is null for most of the field. Britain, Ireland, France and South Africa carry a position for the great majority of runners. /v1/countries returns the current figure for each.

Beaten distances arrive with the pedigree pass

They are published on the horse profile rather than the result page, so they fill in behind the main crawl.

Errors this endpoint returns

CodeStatusWhen
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.
outside_window 403 You asked for a date your plan cannot see. The body names the window and the date you asked for. Search endpoints clamp instead of refusing, and report what they actually searched in meta.
not_found 404 The identifier is well formed but nothing has it. Distinct from invalid_id, which means the identifier itself is wrong.
invalid_id 422 The prefix does not match the kind of thing the path expects, or the identifier is malformed. A horse id passed where a race is wanted lands here rather than returning the wrong race.
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.

Run it live

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.

What this is for

The finishing order, split into those who completed and those who did not, with the winning time, the book percentage, tote dividends and prize money.

The split is deliberate. Lumping a horse that pulled up in with a horse that finished eleventh, both carrying a null position, is how you end up counting casualties as runners. Here the unplaced array carries a casualty reason where one exists, so you always know which you are looking at.

Six casualty reasons are published: PulledUp, Fell, UnseatedRider, BroughtDown, RefusedToRace and RanOut. In jumps racing these are a real signal rather than an edge case; Britain records one on about five percent of runners.

Common uses

Settling your own records

Positions one to four are returned separately rather than as a placed count, so you apply your own place terms instead of ours.

Studying non-completion

The casualty reasons make a jumps analysis possible that a placings-only feed cannot support at all.

Showing a result properly

The margin to second and the winning time are here; the beaten distance for each runner fills in behind the pedigree pass.

Notes

Returns not_found where a race exists but has no result yet. That is a different thing from a missing race and deserves a different response.

Related in Races & markets