APIHorseRacing

Documentation / Races & markets

Race in context

GET /v1/races/{race_id}/analysis

Market shape, movers, and what this course and distance usually produces.

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/analysis \
  -H "X-API-Key: $AHR_KEY"

Response

200 OK application/json harvested 8 days ago
{
    "meta": {
        "request_id": "fbb0948873d18864",
        "data_as_of": "2026-09-12T05:10:27+00:00",
        "plan": "analyst",
        "window": "2017 → upcoming"
    },
    "data": {
        "race_id": "rc_1P005G7",
        "course": "Fontwell",
        "date": "2021-02-28",
        "conditions": {
            "run_type": null,
            "distance": "3m 1f 210y",
            "going": "Soft (Good to Soft in places)",
            "surface": "TURF",
            "class": "6",
            "handicap": false,
            "runners": 16
        },
        "market": {
            "book_percentage": 127.62,
            "shape": {
                "shortest": 1.4,
                "longest": 251,
                "median": 66,
                "spread": 179.3,
                "priced": 16
            },
            "favourites": [
                {
                    "horse_id": "hs_SJX1WR",
                    "horse": "Sametegal",
                    "sp": "2/5",
                    "position": 2
                }
            ],
            "movers": [
                {
                    "horse_id": "hs_YBW9N7",
                    "horse": "Le Reve",
                    "opening": "18/1",
                    "sp": "50/1",
                    "drift": 1.6842,
                    "direction": "drifted",
                    "position": null
                },
                {
                    "horse_id": "hs_3GX9FQ",
                    "horse": "Heaney",
                    "opening": "40/1",
                    "sp": "100/1",
                    "drift": 1.4634,
                    "direction": "drifted",
                    "position": null
                },
                "… 6 more"
            ]
        },
        "at_this_course_and_distance": {
            "races": 132,
            "typical_field": 6.7,
            "typical_book": 112.2,
            "favourite_runs": 144,
            "favourite_wins": 45,
            "favourite_strike": 0.3125
        },
        "note": "Context for one race. For statistics across the archive see /v1/analysis/angles and the course endpoints."
    }
}

A real response from /v1/races/rc_1P005G7/analysis, 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.
run_type string Flat, Hurdle, Chase or NH Flat.
distance string Official distance as published, e.g. "3m 3f 119y".
going string Ground description at the off.
surface string Turf, Dirt, Polytrack and so on.
class string Race class.
handicap boolean Whether the race was run off official ratings.
runners integer Runners who took part.
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.
priced integer Rides carrying a starting price. A/E and the profit figures use these; runs is the wider count.
horse_id string Opaque horse identifier.
horse string Horse name.
sp string Starting price, fractional, as returned.
position integer Finishing position. Null means the runner did not complete or was not published below the placings.
opening string The price the runner opened at, where published.
drift number Starting price against opening, as a ratio. Positive drifted, negative shortened.
direction string drifted, shortened or unchanged.

What varies by jurisdiction

Run type is filled in behind the results crawl

The code a race was run under lives on the horse profile rather than the race, so it arrives on a later pass. A null run_type means not yet fetched, not unclassified.

Race class is a British idea

Class is published in Britain and reliably absent elsewhere. Do not treat a null class as an ungraded race.

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.

Price movement is an opening price, not a full series

The source publishes no odds history on a settled race, only an opening price and any prices touched. About one runner in five carries one. price_path.source tells you which kind you have on every runner, so you never have to infer it.

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

One race in context: how the market was shaped, who the favourite was, which prices moved, and what this course and distance usually produces.

It is the things a reader would otherwise assemble from four calls. The market shape in particular is a summary that is tedious to compute per race: the shortest and longest prices, the median, and the spread between them, which tells you whether this was a market with a short favourite and a long tail or one where everything was six to one.

This is context for a single race, not statistics over an archive, which is why it is on every paid plan. The archive-wide work lives on the Analyst endpoints.

Common uses

A preview or a review panel

One call gives you enough to write about a race without a model.

Judging whether a field was competitive

The spread figure separates a two-horse race from a genuine handicap far faster than reading fourteen prices.

Setting expectations

The favourite strike rate at this course and distance is the honest baseline against which anything you predict should be measured.

Related in Races & markets