Runners whose price has moved materially since the market opened, across upcoming races. `move_pct` is **negative for a firming runner** (the price shortened — money has arrived) and positive for a drifter. `open_price` is the FIRST PRICE CAPTURED inside the 60-minute pre-race window, not a market open. Capture begins when the race enters that window, so how close that first price is to a true 60-minute open depends on the book — and it is not a clean two-way split. Measured over 13 days of snapshots, the share of series whose first capture sits at the 60-minute mark falls into three groups. `sportsbet`, `betright` and `betr_au` are there on 96-98% of series. `tab`, `ladbrokes_au` and `neds` are there on about 55%, but that figure swings between 16% and 77% from one day to the next, so for these three it is not a property you can rely on per book. `playup` (6%), `unibet` and `pointsbetau` (3%), `tabtouch` (1%) and `palmerbet` (0%) rarely or never are: `palmerbet` has not once been captured earlier than 33 minutes out across 23,641 series. Do not infer any of this from the book. Read `open_secs_to_jump` on each bookmaker row, which says how far out that book's open actually was; `/v1/racing/price-history` additionally returns `open_is_baseline` per series. A move is reported at the RUNNER level and only when at least `min_books` bookmakers have **two or more captured prices**, because a book seen only once has evidenced nothing: its open equals its current by construction. Those books are still listed, with `points: 1` and `counted_in_consensus: false`, and still counted in `books_quoting` — but they do not enter the consensus or the gate. Use `books_with_history` as the confirmation count. **Broad-market support versus one book moving: read the breadth counters.** `books_firming`, `books_drifting` and `books_unchanged` partition `books_quoting`, so they say how many books actually agreed with the headline move. A steam the market backs shows most of `books_with_history` moving the same way; a single-book move shows one, with the rest unchanged, and can still clear `min_move_pct` because the consensus averages over the few books that carry history. Measured live on 2026-09-01, Geelong R4 greyhound "Got Immunity" reported +102.8% drifting on 13 books quoting, of which 5 had history: 3 drifting, 1 firming, 9 unchanged - the headline number came from a minority. Gate on the counters, and on `bookmakers[].move_pct` where you need the per-book evidence, rather than on `move_pct` alone. Raising `min_books` raises how many books must carry history, not how many must agree.
X-API-Key
3 credits
curl 'https://api.puntersedge.online/v1/racing/movers' \
-H 'X-API-Key: YOUR_KEY'
| Name | In | Type | Required | Description |
|---|---|---|---|---|
direction |
query | string | no | firming, drifting, or omit for both |
min_move_pct |
query | number | no | Minimum consensus move, percent |
min_books |
query | integer | no | Only report a move confirmed by at least this many books |
categories |
query | string | no | horse,greyhound,harness or omit for all |
category |
query | string | no | Alias of `categories`. Both spellings are accepted on every racing endpoint since 2026-09-24; before that, using one family's spelling on the other was a hard 422. An unknown value is a free 422 naming the valid ones. |
country |
query | string | no | ISO country codes, comma-separated (e.g. AU). Omit for every country. Pair with include_unresolved=true or you will silently lose races whose meeting is not confirmed yet. |
include_unresolved |
query | boolean | no | Include races whose country is not resolved yet (country is null). OFF by default. Most of an Australian card is unresolved until the meeting is confirmed — measured 3 labelled AU against 21 unresolved in a live window — so country=AU alone can return far fewer races than are running, or none at all. |
max_mins_to_jump |
query | integer | no | |
limit |
query | integer | no |
Status codes: 200, 401, 402, 422, 429, 500. Response bodies are JSON; the full schema is in /openapi.json.
200 application/json
— this call costs 3 credits.
Field names and types are as the API returns them; values are a real sample, trimmed to a few items.
[
{
"bookmakers": [
{
"counted_in_consensus": true,
"current_price": 9.0,
"key": "betright",
"move_pct": -18.18,
"open_price": 11.0,
"open_secs_to_jump": 3597,
"points": 4
}
],
"books_drifting": 0,
"books_firming": 9,
"books_quoting": 10,
"books_unchanged": 1,
"books_with_history": 9,
"category": "horse",
"country": "AU",
"current_price": 9.17,
"direction": "firming",
"mins_to_jump": 3,
"move_pct": -18.3,
"number": 10,
"open_price": 11.22,
"race_number": 8,
"runner": "Magnaburma",
"start_time": "2026-08-15T08:52:00Z",
"venue": "Broome"
}
]
GET /v1/racing/acceptances — Full-day AU thoroughbred acceptance card: every meeting, race and runnerGET /v1/racing/best-odds — Best racing price per runner across booksGET /v1/racing/changes — Races and prices that changed since a timestampPOST /v1/racing/clv — Score bets against the closing line (CLV)GET /v1/racing/events — Upcoming race listGET /v1/racing/form — Every runner in one race with its recent runs, from our own resultsGET /v1/racing/greyhounds/form — Greyhound form history for one dog, from our own resultsGET /v1/racing/greyhounds/stats — Greyhound record by track, distance, box or grade, from our own resultsThe free tier needs no credit card, and the sandbox endpoints need no key at all.
Get a free API key Quickstart