APIHorseRacing

Documentation / Races & markets

Market

GET /v1/races/{race_id}/market

Book percentage, every price, and what moved before the off.

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

Response

200 OK application/json harvested 8 days ago
{
    "meta": {
        "request_id": "fcbeef222e9d42df",
        "data_as_of": "2026-09-12T05:10:27+00:00",
        "plan": "analyst",
        "window": "2017 → upcoming",
        "count": 16
    },
    "data": {
        "race_id": "rc_1P005G7",
        "book_percentage": 127.62,
        "runners": [
            {
                "horse_id": "hs_V8JGH6",
                "horse": "The Galloping Bear",
                "sp": "10/3",
                "sp_decimal": 4.3333,
                "implied": 0.2308,
                "price_path": {
                    "opening": "6/1",
                    "opening_decimal": 7,
                    "touched": null,
                    "sp": "10/3",
                    "sp_decimal": 4.3333,
                    "drift": -0.381,
                    "source": "movements",
                    "direction": "shortened"
                },
                "favourite": false,
                "market_position": 2
            },
            {
                "horse_id": "hs_SJX1WR",
                "horse": "Sametegal",
                "sp": "2/5",
                "sp_decimal": 1.4,
                "implied": 0.7143,
                "price_path": {
                    "opening": "4/11",
                    "opening_decimal": 1.3636,
                    "touched": [
                        "1/3"
                    ],
                    "sp": "2/5",
                    "sp_decimal": 1.4,
                    "drift": 0.0267,
                    "source": "movements",
                    "direction": "drifted"
                },
                "favourite": true,
                "market_position": 1
            },
            "… 14 more"
        ]
    }
}

A real response from /v1/races/rc_1P005G7/market, 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.
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.
runners integer Runners who took part.
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.
implied number One over the decimal price. Sums to the book percentage across a field, not to one.
price_path object What the price did before the off. See its own fields below.
opening string The price the runner opened at, where published.
touched array Prices touched between opening and starting price.
drift number Starting price against opening, as a ratio. Positive drifted, negative shortened.
source string full_history where a complete price series exists, movements where only an opening price does, starting_price_only where neither.
direction string drifted, shortened or unchanged.
favourite boolean Shortest price in the race. Joint favourites are all flagged.
market_position integer Rank by price, one being shortest. Ties share a rank.

What varies by jurisdiction

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

Every price in a race, what the book added up to, and which runners moved before the off. Two things here do not exist in a results feed.

Book percentage is the field's implied probabilities summed. A hundred would be a fair book; the excess is the margin taken out. Ours average about 117 in fields of two to seven and 133 in sixteen or more, and that spread is the reason backing every runner blind loses more than you would expect from the headline figure.

Price movement is the other. The source publishes no odds history on a settled race, only an opening price and any prices touched, so price_path carries a source field naming exactly which kind you have on every runner rather than leaving you to infer it from a null.

Both favourite and market_position are derived here rather than taken from the source, which leaves both empty on a settled race. Runners are ranked on decimal price, shortest first, and joint favourites share rank one.

Common uses

Testing whether money moving means anything

This endpoint plus /v1/market/movers is the whole experiment. One gives you the movement per race, the other the population answer across the archive.

Comparing a race against its book

implied on each runner sums to the book percentage rather than to one. If you want probabilities that sum to one, use the expected field on the statistics endpoints, which is normalised for exactly this reason.

Finding overbroke fields

A book percentage well above the average for its field size is a race where the layers wanted more margin than usual.

Related in Races & markets