---
title: "Every market on one race: win, place, Top 2/3/4 and exotic dividends"
method: GET
path: "/v1/racing/markets"
tags: ["Racing"]
---

# Every market on one race: win, place, Top 2/3/4 and exotic dividends

`GET /v1/racing/markets`

## Request

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

Cost: 2 credits. One race, every market the panel quotes on it, book by book: the win
price, the place price, the Same Race Multi legs (**Top 2, Top 3 and Top 4**), each book's
market percentage, the best price per runner in each market, and — once the race has been
run — the exotic dividends it paid.

**Naming the race.** `race_id` is exact. Failing that, `venue` + `meeting_date` (the AET
meeting date) + `race_number`, with `country` when two meetings share the name on one
date (Ascot in Perth and in Berkshire on 2026-10-17); two races still sharing those answer
409 `race_ambiguous`, naming each, and cost nothing, because picking one of a venue split
silently would be worse than refusing.

**Who quotes what.** Every book in the panel prices win, and all but the exchange price
place. Top 2/3/4 come from **sportsbet, ladbrokes_au, neds and pointsbetau** — no other
book in the feed publishes Same Race Multi legs, so an empty `top3` is a fact about the
market and not a gap in the feed. Each leg is validated against the chain
win >= top2 >= top3 >= top4 > 1.0 before it is stored, so a leg that would break it is
absent rather than wrong.

**overround_pct** is Σ (1/price) × 100 over the runners that book quoted in that market —
100% is a book taking no margin. It is null where a book quoted fewer than two runners,
which is not a market. Place and Top-N percentages are naturally far above 100 because
several runners can win the bet.

**Live or stored.** A race whose market is still open is read live (`source: "live"`). Once
it has been run the live rows are purged, and the answer comes from the minute-by-minute
capture of the last price each book showed before the jump (`source: "closes"`). Races that
jumped before that capture began carry no market rows and say so in `note`.

**exotics** are SETTLED DIVIDENDS — what the tote paid — not prices. Nothing in this feed
quotes fixed-odds quinella, exacta, trifecta or first four before a race, so `exotics` is
null until the race is resulted, and then carries the result's own `status`: `interim`
dividends can still move, `final` cannot.

**quote_detail** (since 2026-09-29) lists, per book and runner, what the book publishes
beside its prices: `open_price` (the book's own opening price — not the archive's first
captured price), `flucs` (its recent prices, most recent first), `tote_win` / `tote_place`
(its tote dividends keyed by product code; `PROV` is an indicative pre-race dividend that
moves until the pool closes), `boost_price` / `boost_type`, and the exchange fields. Each
entry names `bookmaker`, `number`, `name`, `runner_ref` and `seen_at` — when the book was
last read — and only the fields that book carries. Who carries them, measured on the AU
board of 2026-09-29 before its first race (20 races, 2,219 book-runner rows): flucs at
betdeluxe, betgold, boostbet and betright on every row, open_price at betright on every
row, tote_win at tabtouch on 89 of 194 rows and tote_place on 26 (a provisional dividend
appears as the pool forms), no boost that morning. The exchange fields are Betfair's, and
Betfair is withheld from customer answers. For a race that has been run the values are
the last read before the jump.

**scratchings** (since 2026-09-29) are the race's scratched runners — `name`, `number`,
`barrier`, `scratched_at` (the scratching book's own time, UTC) and `vacant` for an empty
box — from the open race while it is live, and once it has been purged from the capture
taken at the jump. A race run before 2026-09-29 answers an empty list after its purge. A
scratching no Australian bookmaker has reported, while two or more of them still price the
runner, is held back: the runner stays in the markets until a bookmaker reports it.

**tote_series** (since 2026-10-06) is each book's tote approximate as the pools formed:
one entry per book and runner, with points at fixed marks before the jump — 3600, 1800,
900, 600, 300, 180, 120 and 60 seconds — each the first read the minute-by-minute capture
saw at or inside its mark, carrying `mark_seconds`, `read_at`, `secs_to_jump` and
`tote_win` / `tote_place` by product code. TABtouch publishes a provisional dividend
(`PROV`) for every runner hours out; BetRight its products in the final quarter hour. A
race first seen inside a mark has no earlier points: nothing is filled in. The last read
before the jump stays in `quote_detail`.

**book_scratchings** (since 2026-10-06) says, book by book, when a scratching reached each
feed: `reported_at` is the first time that book's own feed listed it, `book_scratched_at`
the scratch time the book itself states (Ladbrokes, Neds, BetRight and the BetMakers books
carry one), and `first_quoted_at` / `last_quoted_at` with `last_quoted_win` the polls, to
the minute, at which that book's feed still carried a win price for the runner after the
race's scratchings named it. Ladbrokes and Neds keep a scratched runner's last price on the
card beside their own flag, so for a book that flags it the price is recorded once, at the
flag: its price at the scratching. A book that carries a price with no flag is recorded
each minute until it clears the runner. None of these prices is served as a price: a
scratched runner is dropped from every price endpoint.

**Your own links** (`source_link` beside each runner's `source_url`, since 2026-10-14, Plus
and above): store your bookmaker link templates once with `PUT /v1/link-templates` and each
price also carries a ready-made link built through them. Absent where there is nothing to
link, and for an account with no stored templates.

Plus and above, and Standard keys issued before 20 September 2026. Every other key below
Plus receives a 403 and is not charged; the 403 carries a `sample`: the winner of one
recent race, its best price in each market and the book that offered it.

## Query parameters

- `race_id` string, nullable: The race_id from /v1/racing/next-to-go, /events, /results or the archive. The primary form; use venue + meeting_date + race_number only when you do not hold it.
- `venue` string, nullable: Venue name as the feeds spell it (case-insensitive). Needs meeting_date and race_number.
- `meeting_date` string, date, nullable: AET meeting date, YYYY-MM-DD.
- `race_number` integer, nullable
- `country` string, nullable: Alpha-2 country of the meeting (AU, NZ, GB, IE, ZA, ...). Only needed with venue when two meetings share the name on one AET date — Ascot (Perth) and Ascot (Berkshire) on 17 October 2026 — and the 409 names each candidate's country.
- `bookmakers` string, nullable: Comma-separated bookmaker keys to restrict to. Omit for every book quoting the race.
- `markets` string, nullable: Comma-separated subset of win, place, top2, top3, top4, exotics. Omit for all of them.

## Headers

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

## Response `200`

Successful Response

- MarketsOut
  - `race` RaceHeadOut, required
    - `race_id` string, required
    - `venue` string, nullable
    - `race_number` integer, nullable
    - `category` string, nullable
    - `country` string, nullable
    - `start_time` string, nullable
    - `places_paid` integer, nullable
    - `status` string, required: upcoming · jumped · resulted.
    - `source` string, required: live (read from the open market) or closes (read from the stored last observation before the jump).
  - `markets` MarketOut[]
    - `market` string, required: win · place · top2 · top3 · top4.
    - `places` integer, nullable: Runners the bet covers: 1 for win, the race's places_paid for place, 2/3/4 for the Same Race Multi legs. Null when the race never reported places_paid.
    - `bookmakers` MarketBookOut[]
      - `key` string, required
      - `runners_quoted` integer, required
      - `overround_pct` number, nullable: Sum of 1/price across this book's quoted runners, as a percentage. Null when fewer than two runners are quoted, where the figure would describe nothing.
      - `observed_at` string, nullable: Newest price change this book made on this race, UTC.
      - `runners` MarketRunnerOut[]
        - `number` integer, nullable: Saddlecloth number (box for greyhounds).
        - `name` string, nullable
        - `runner_ref` string, nullable: Registry id — ra:<horsecode> or grv:<dogId> — where one is known.
        - `horse_ref` string, nullable: pe:<name>, the stable thoroughbred identity. Null off AU thoroughbreds.
        - `price` number, required
        - `source_url` string, nullable: The book's own page for this race. Present while the market is open (source is live); null on a race answered from the stored close, which keeps the prices and the runner identities but not the links.
        - `source_link` string, nullable: Your own link for this price: source_url built through the link templates stored with PUT /v1/link-templates (since 2026-10-14). Absent when your account stores none, or there is nothing to link.
    - `best` MarketBestOut[]
      - `number` integer, nullable
      - `name` string, nullable
      - `runner_ref` string, nullable
      - `price` number, required
      - `bookmaker` string, required
    - `suspect` object[]: Quotes kept out of `best` (since 2026-10-20) by the same rules as /v1/racing/best-odds `suspect_win`: each entry is number, name, bookmaker, price, and either `consensus` alone (a placeholder price, 4x or more the median of the other books) or a `reason` (pre_scratching, book_under_round, book_not_repriced) with the figure behind it. The quote itself is still listed under `bookmakers`.
  - `exotics` ExoticsOut
    - `source` string
    - `status` string, nullable: The result's own status: interim or final. An interim dividend can still move.
    - `dividends` object[]
  - `note` string, nullable
  - `quote_detail` object[]: Per book and runner, the fields the book publishes beside its prices (since 2026-09-29): open_price (the book's own opening price), flucs (its fluctuations, most recent first), tote_win / tote_place (its tote dividends by product code; PROV is an indicative pre-race dividend), boost_price / boost_type, and the exchange fields. Each entry carries bookmaker, number, name, runner_ref, seen_at (UTC, when the fields were read) and only the fields that book carries.
  - `scratchings` object[]: The race's scratched runners: name, number, barrier, scratched_at (the scratching book's own time, UTC), vacant for an empty box. Read from the open race while it is live and from the capture taken at the jump once it has been purged (from 2026-09-29).
  - `tote_series` object[]: Per book and runner, the book's tote approximates at fixed marks before the jump (since 2026-10-06): points at 3600, 1800, 900, 600, 300, 180, 120 and 60 seconds, each the first read the capture saw at or inside its mark: mark_seconds, read_at (UTC), secs_to_jump, and tote_win / tote_place by product code (PROV is an indicative pre-race dividend). The last read before the jump is tote_win / tote_place in quote_detail.
  - `book_scratchings` object[]: Per book and scratched runner (since 2026-10-06): reported_at, the first time that book's own feed listed the scratching; book_scratched_at, the scratch time the book itself states, where it carries one; first_quoted_at / last_quoted_at, when that book's feed still carried a win price for the runner after the race's scratchings named it, to the minute, with last_quoted_win (for a book that flags the scratching itself, recorded once at the flag: its price at the scratching). All UTC. Never served as a price.

## 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
- `403`: The race markets view is available on Plus, Business, Platform and Scale plans only. Standard keys issued before 20 September 2026 have it too. The refusal is free and carries a `sample` of the view for one recent race.
- `404`: No race matched. The body says whether the id is unknown or a known race with no stored market, and where its prices are; for a race named by venue that no bookmaker has framed yet, when its board opens. Nothing was charged.
- `409`: Two races share that venue, date and number (a venue split); the body names each one. Pass country= or race_id. Nothing was charged.
- `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)
