---
title: "Bulk export of the permanent price-movement archive"
method: GET
path: "/v1/racing/price-paths"
tags: ["Racing Archive"]
---

# Bulk export of the permanent price-movement archive

`GET /v1/racing/price-paths`

## Request

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

Cost: 5 credits (JSON) / 20 credits (CSV). 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.

## Query parameters

- `from` string, nullable: ISO date/time on the race start.
- `to` string, nullable: ISO date/time.
- `date` string, nullable: Single-day alias: date=YYYY-MM-DD is exactly from=YYYY-MM-DD&to=YYYY-MM-DD. Mutually exclusive with from/to.
- `venue` string, nullable: Venue name, case-insensitive, exact match.
- `bookmakers` string, nullable: Comma-separated bookmaker keys, case-insensitive. An unrecognised key is a free 422 naming the valid keys.
- `category` string, nullable: 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` string, nullable: Alias of `category`. Comma-separated horse, harness, greyhound. Both spellings are accepted on every racing endpoint.
- `country` string, nullable: Two-letter country code, case-insensitive; comma-separated for more than one, e.g. AU, or GB,IE for British and Irish racing.
- `race_id` string, nullable: Single race. With race_id the window guard does not apply.
- `include_flagged` boolean: Include rows flagged venue_split_suspect or name_fragment_suspect. False by default.
- `format` string: 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` integer
- `offset` integer

## Headers

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

## Response `200`

Successful Response

- PricePathsOut
  - `rows_returned` integer, required
  - `total_points` integer, required: Points matching the filter before limit/offset
  - `limit` integer, required
  - `offset` integer, required
  - `archive_from` string, nullable
  - `rows` 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)
