---
title: "Racing steamers and drifters"
method: GET
path: "/v1/racing/movers"
tags: ["Racing"]
---

# Racing steamers and drifters

`GET /v1/racing/movers`

## Request

- Base URL: `https://api.puntersedge.online`
- URL: `https://api.puntersedge.online/v1/racing/movers`
- Auth: API key in header `X-API-Key`

Cost: 3 credits. 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.

## Query parameters

- `direction` string: firming, drifting, or omit for both
- `min_move_pct` number: Minimum consensus move, percent
- `min_books` integer: Only report a move confirmed by at least this many books
- `categories` string: horse,greyhound,harness or omit for all
- `category` string: Alias of `categories`. Both spellings are accepted on every racing endpoint. An unknown value is a free 422 naming the valid ones.
- `country` string: 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` boolean: 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` integer
- `limit` integer

## Headers

- `X-API-Key` string
- `X-RapidAPI-Proxy-Secret` string

## Response `200`

Successful Response

- MoverOut[]
  - `venue` string, nullable, required
  - `race_number` integer, nullable, required
  - `category` string, nullable, required
  - `country` string, nullable, required
  - `start_time` string, required
  - `mins_to_jump` integer, nullable
  - `runner` string, required
  - `number` integer, nullable
  - `direction` string, required
  - `open_price` number, required
  - `current_price` number, required
  - `move_pct` number, required
  - `books_quoting` integer, required: Books with a price for this runner, including ones we have never observed move.
  - `books_with_history` integer, required: Books with two or more captured prices — the only ones that can evidence a move. The consensus and the `min_books` gate use THIS count, not `books_quoting`.
  - `books_firming` integer, required
  - `books_drifting` integer, required
  - `books_unchanged` integer, required: Books whose captured price has not moved, including books seen only once. books_firming + books_drifting + books_unchanged == books_quoting.
  - `bookmakers` object[], required

## Other responses

- `401`: Missing or invalid API key. Send `X-API-Key`. See https://puntersedge.online/developers/errors
- `402`: Monthly credit allowance exhausted for this plan. Upgrade at https://puntersedge.online/api/pricing
- `422`: Validation Error
- `429`: Rate limit exceeded for this plan. Honour the `Retry-After` header (seconds) before retrying. See https://puntersedge.online/developers/rate-limits
- `500`: Unexpected server error. Safe to retry with backoff.

---

[API](https://skmtc.dev/puntersedge/apis/puntersedge-odds-api.md) · [All operations](https://skmtc.dev/puntersedge/apis/puntersedge-odds-api/llms.txt) · [OpenAPI document](https://skmtc.dev/puntersedge/apis/puntersedge-odds-api/revisions/f35b6aab1946?raw)
