API reference / Racing Archive
GET /v1/racing/price-pathsBulk export of the permanent price-movement archive
Every OBSERVED PRICE POINT in the permanent movement archive: one row per race, runner, bookmaker and captured price, from market open to the last pre-jump quote. **This is the bulk, downloadable form of `/v1/racing/price-history`.** That endpoint answers for one race; this one exports a date range as a flat table (CSV loads straight into pandas — `pd.read_csv(url)` with your key in the header; `format=parquet` returns the same table typed, `pd.read_parquet(BytesIO(r.content))`). The archive is permanent: nothing here ages out, and coverage runs from `archive_from` (capture began 2026-08-04) forward, forever. **The series is change-only.** Each row is a price MOVE, not a fixed-interval sample — a gap between two points means the price held. To build T-snapshots (T-60/30/15/5/2), take the last row at or before each mark per (race_id, runner_key, bookmaker_key). **Window guard.** Points run ~9x the series rows, so a request without `race_id` is capped at a 7-day `from`/`to` window per call — page a season week by week. A reversed or over-wide range is a free 422. **`runner_ref`** (the column after `venue_site`, since 2026-09-05) is the registry id stored on the row — `ra:<horsecode>` for thoroughbreds, `grv:<dogId>` for greyhounds — identical to the `runner_ref` on `/v1/racing/results` `runners[]` entries, on `/v1/racing/closing-lines` and `/v1/racing/markets`, and on the live board on race day, so every point in this export joins to its result and to the race's other rows on one key. Null where no registry id is known: harness, rows archived before late August 2026, and a race whose result has not landed and whose acceptance list we did not hold. On a thoroughbred it is the code Racing Australia published on race day, not the horse: Racing Australia issues a new code for the same horse each day, so it changes from meeting to meeting. A greyhound's does not — `grv:`/`grsa:` are per-dog registry ids. **`horse_ref`** (the column after `runner_ref`, added 2026-09-21) is the horse identity this export was missing: `pe:<name>`, the registered name folded to letters and digits (trailing country parenthetical removed). Derived at serve time from `runner_name` rather than stored, so every point ever archived carries it from today, and appended after `runner_ref` so no positional reader moves. Group a horse's whole price history on it — across meetings, tracks and seasons — and use `runner_ref` to join the rows about one race. Null on greyhound and harness rows. **`place_price`** (LAST column, added 2026-09-29) is the book's PLACE price at that point. NULL on every point archived before 2026-09-29 and at books that quote no place price. A point is written when the WIN (or exchange lay) price moves — a place-only move does not create one — so it is the place price at each win move, not a separate place series. **`top2_price`, `top3_price`, `top4_price`** (LAST three columns, added 2026-10-05) are the book's Same Race Multi Top 2 / Top 3 / Top 4 price at that point, on the same win-move cadence. Only sportsbet, ladbrokes_au, neds and pointsbetau publish them — NULL at every other book — and NULL on every point before 2026-09-29. **Plans.** Plus and above, and Standard keys issued before 2026-09-20. Any other key gets a free 403 that carries a `sample`: the last five moves in one book's price for the winner of one recent race, as rows of this export. Betfair Exchange rows are withheld pending a data licence, so `lay_price` — which only an exchange quotes — is NULL for customers. BSP and traded volume are absent for the same reason; when a licence lands they will arrive as new columns, not a changed contract.
X-API-Key
5 credits (JSON) / 20 credits (CSV)
curl 'https://api.puntersedge.online/v1/racing/price-paths?date=2026-08-15&category=horse&format=csv' \
-H 'X-API-Key: YOUR_KEY'
| Name | In | Type | Required | Description |
|---|---|---|---|---|
from |
query | string | no | ISO date/time on the race start. |
to |
query | string | no | ISO date/time. |
date |
query | string | no | Single-day alias: date=YYYY-MM-DD is exactly from=YYYY-MM-DD&to=YYYY-MM-DD. Mutually exclusive with from/to. |
venue |
query | string | no | Venue name, case-insensitive, exact match. |
bookmakers |
query | string | no | Comma-separated bookmaker keys, case-insensitive. An unrecognised key is a free 422 naming the valid keys. |
category |
query | string | no | horse, harness or greyhound — comma-separated for more than one. `categories=` is accepted as an alias (2026-09-24); an unknown value is a free 422. |
categories |
query | string | no | Alias of `category`. Comma-separated horse, harness, greyhound. 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. |
country |
query | string | no | Two-letter country code, e.g. AU, NZ |
race_id |
query | string | no | Single race. With race_id the window guard does not apply. |
include_flagged |
query | boolean | no | Include rows flagged venue_split_suspect or name_fragment_suspect. False by default. |
format |
query | string | no | csv streams a flat table with a stable column order — the bulk download this endpoint exists for. parquet returns the same table, same column order, as one Apache Parquet file with typed columns (UTC timestamps, a date, float64 prices) — pd.read_parquet(BytesIO(r.content)). Same credit cost and row cap as csv. |
limit |
query | integer | no | |
offset |
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 5 credits (JSON) / 20 credits (CSV).
Field names and types are as the API returns them; values are a real sample, trimmed to a few items.
{
"rows_returned": 969,
"total_points": 969,
"limit": 1000,
"offset": 0,
"archive_from": "2026-08-04T09:22:00Z",
"rows": [
{
"race_id": "81db7342-2e44-482a-bf62-5d5166d764f5",
"start_time": "2026-09-29T03:30:00Z",
"meeting_date_aet": "2026-09-29",
"venue": "Tatura",
"race_number": 1,
"category": "horse",
"country": "AU",
"runner_key": "theshyster",
"runner_name": "The Shyster",
"runner_number": 8,
"bookmaker_key": "betgold",
"captured_at": "2026-09-29T02:30:03Z",
"secs_to_jump": 3597,
"win_price": 2.45,
"lay_price": null,
"venue_id": "tatura",
"venue_site": "tatura",
"runner_ref": "ra:NDIzMjAzNDI2NDA",
"horse_ref": "pe:theshyster",
"scratched": false,
"place_price": 1.3,
"top2_price": null,
"top3_price": null,
"top4_price": null
},
{
"race_id": "81db7342-2e44-482a-bf62-5d5166d764f5",
"start_time": "2026-09-29T03:30:00Z",
"meeting_date_aet": "2026-09-29",
"venue": "Tatura",
"race_number": 1,
"category": "horse",
"country": "AU",
"runner_key": "theshyster",
"runner_name": "The Shyster",
"runner_number": 8,
"bookmaker_key": "betgold",
"captured_at": "2026-09-29T03:26:49Z",
"secs_to_jump": 191,
"win_price": 2.35,
"lay_price": null,
"venue_id": "tatura",
"venue_site": "tatura",
"runner_ref": "ra:NDIzMjAzNDI2NDA",
"horse_ref": "pe:theshyster",
"scratched": false,
"place_price": 1.26,
"top2_price": null,
"top3_price": null,
"top4_price": null
},
{
"race_id": "81db7342-2e44-482a-bf62-5d5166d764f5",
"start_time": "2026-09-29T03:30:00Z",
"meeting_date_aet": "2026-09-29",
"venue": "Tatura",
"race_number": 1,
"category": "horse",
"country": "AU",
"runner_key": "theshyster",
"runner_name": "The Shyster",
"runner_number": 8,
"bookmaker_key": "betgold",
"captured_at": "2026-09-29T03:28:40Z",
"secs_to_jump": 80,
"win_price": 2.3,
"lay_price": null,
"venue_id": "tatura",
"venue_site": "tatura",
"runner_ref": "ra:NDIzMjAzNDI2NDA",
"horse_ref": "pe:theshyster",
"scratched": false,
"place_price": 1.26,
"top2_price": null,
"top3_price": null,
"top4_price": null
}
]
}
GET /v1/racing/closing-lines — Permanent closing-line and result archiveGET /v1/racing/closing-lines/coverage — What the closing-line archive actually holdsGET /v1/racing/market-summary — The market's path per runner, stored at the jump: consensus at fixed marks, moves, breadth, first mover, signalThe free tier needs no credit card, and the sandbox endpoints need no key at all.
Get a free API key Quickstart