Racing Archive

Permanent closing-line and result archive

Cost: 5 credits (JSON) / 20 credits (CSV). The permanent closing-line archive: one row per race, runner and bookmaker, carrying the last price seen before the jump, the first price seen after the market opened, and the finishing position where one is known.

Plans. Plus and above read the whole archive back to its first race; window_days comes back null for them. Standard reads a rolling 90-day window: window_days is 90, the response carries X-Archive-Window-Days, and a range entirely older than the window is a free 403 that says so. Free and Hobby get a free 403 that carries a sample: five of this endpoint's rows, for one recent race. Standard keys issued before 2026-09-20 keep the whole archive they were sold; the window_days field tells you which case yours is. Legacy starter, pro and growth keys keep their access but cannot be bought.

This is not the same data as /v1/racing/price-history. That endpoint reads the live 45-day snapshot store and returns every price move for one race. This one reads a separate, permanent table and returns one collapsed row per series across any date range. Price history disappears at 45 days; the archive does not.

Read the flags before you model on this. Measured 2026-08-18 over 348,889 archived series:

  • is_closing_line — only 85.5% of series have their final observation within 300s of the jump. The rest stopped being quoted early and their close is a last-seen price. Filtered in by default.
  • open_is_baseline — only 42.5% of series start at the 60-minute window entry. The rest joined mid-window, so their "open" is not a market open.
  • finish_position NULL does not mean the runner lost. Before 2026-10-04 harness and NZ results named the placegetters only, so most runners in a resulted race there have no published position; AU harness and NZ results carry the full field from 2026-10-04, and GB and IE thoroughbred results from 2026-10-06. AU thoroughbred and greyhound results carry the whole field, and there every finisher has its position (5th, 6th, …) — applied back to 2026-09-14 on 2026-09-29; before that only 1-4 were stored. Check result_status: NULL there means the race has no result at all.
  • venue_split_suspect / name_fragment_suspect — the archive's record of its own known contamination. Excluded by default.
  • duplicate_suspect / duplicate_of_race_id — the race appears to duplicate another race record (the same race filed twice); the second names the other race_id. These keys, and meeting_date_local, appear on a row once the archive carries them; an absent key means not assessed, not false.
  • scratched — the result marked this runner scratched, yet the row holds a price: four books (Betfair, Sportsbet, Ladbrokes, Neds) keep quoting a scratched runner and the archive kept their last price as its "close". Excluded by default since 2026-09-28 (include_scratched=true to see them). Only AU thoroughbred and greyhound results name their scratchings, so on harness and NZ rows false means not known, never ran.

Recommended filter for modelling. Keep a row when is_closing_line AND NOT scratched AND NOT venue_split_suspect AND NOT name_fragment_suspect AND NOT duplicate_suspect (treat an absent duplicate_suspect as false). For GB/IE racing, key a race on race_id or on (venue, meeting_date_local, race_number), never on meeting_date_aet: a British or Irish evening card falls on the next Australian Eastern date. Every row's fields are typed in the ClosingLineRow schema.

Three columns appended LAST on 2026-09-29, after scratched — every earlier column keeps its position:

  • open_place_price / close_place_price — the book's PLACE price at the first and the last observation of the series. NULL on rows archived before 2026-09-29 (the capture began storing place that day) and at books that quote no place price (Betfair, and any book that prices win only). The place price is recorded at the moments the WIN price moved, not at every place move, so a close_place_price can be older than the book's final place price by up to the time since its last win move.
  • scratched_at — when the runner was scratched, as the scratching book stamped it on the live board, captured at the jump from 2026-09-29. NULL for a runner that ran, for a scratching the board carried without a time, and for every race before that date.

Six Top-N columns appended LAST on 2026-10-05, after scratched_at: open_top2_price, open_top3_price, open_top4_price, close_top2_price, close_top3_price, close_top4_price — the book's Same Race Multi Top 2 / Top 3 / Top 4 price at the first and the last observation of the series. Only sportsbet, ladbrokes_au, neds and pointsbetau publish these legs, so they are NULL at every other book; NULL on every race before 2026-09-29, when the capture began holding them. Like the place price, they are recorded at the moments the WIN price moved, not at every Top-N move.

Five consensus columns appended LAST on 2026-10-05, after close_top4_price: consensus_close_prob, consensus_close_price, books_in_consensus_close, consensus_open_price, books_in_consensus_open — the market's margin-free probability for the runner at the close, the price it corresponds to, and how many books it rests on, plus the same price at the 60-minute open. The arithmetic is GET /v1/racing/consensus's default (power method): each included book's implied probabilities raised to the exponent that makes them sum to one, the median across books per runner, the field scaled to one. The archive is one method throughout — the rows written under the multiplicative default on 5 October 2026 were recomputed when power became the default on 6 October. A book is included at the close when its row is a genuine closing line (is_closing_line), it prices the complete field, it is not withheld from customers and the runner was not scratched; placeholder quotes leave on the best-odds rule. The open figure counts only series that began at the baseline (open_is_baseline), so it rests on fewer books and is NULL where fewer than two qualify. The value is the same on every bookmaker row of a runner, like finish_position, and it is recomputed by the hourly archiver whenever a row changes. It describes the market, not the outcome: it is what the panel implied, with margin removed, not a probability of winning.

Result coverage is forward-growing and depends on the market. Australian and New Zealand results begin on 2026-08-15, British and Irish results on 2026-10-06 and South African thoroughbred results on 2026-10-15; races in every other country carry closing prices but no result, and archived races before those dates carry none. resulted_rows on the response is the measured count for your actual selection, not a marketing figure, and /v1/racing/closing-lines/coverage publishes the share by code.

runner_ref joins a row to everything about its race; horse_ref joins a HORSE across meetings. 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 — the same value /v1/racing/results carries on every runners[] entry and /v1/racing/markets carries for the race, so an archive row joins to its result and its market rows without matching names. On a THOROUGHBRED it is the code Racing Australia published on race day; Racing Australia issues a new code for the same horse each day, so it changes from meeting to meeting and a season keyed on it splits into one group per start — horse_ref is the key for that. On greyhounds grv:/grsa: are per-dog registry ids and do persist. Filled from Racing Australia acceptances for AU thoroughbreds and from the result itself for every code once it lands, so it can be null on a row whose race has not resulted yet, on harness (no registry feed), and on rows archived before the identifiers existed (late August 2026). Appended after venue_site so positional CSV readers keep working.

horse_ref (the column after runner_ref, added 2026-09-21) is the horse identity: pe:<name>, the registered name folded to letters and digits with any trailing country parenthetical removed. It is DERIVED at serve time from runner_name, not stored, so every row the archive has ever held carries it from today — no backfill, and nothing already exported moved: it is appended after runner_ref, exactly as runner_ref was appended after venue_site. It is the same value on /v1/racing/results runners and placings, the live board, /v1/racing/horses/form, /v1/racing/horses/runs and the form backfill, so a horse's whole price history across meetings is one GROUP BY horse_ref. Null on greyhound and harness rows, and on any row whose runner_name is missing.

Betfair Exchange rows are withheld from customer responses pending a data licence, so close_lay_price — which only an exchange quotes — is NULL for customers.

Dates. from/to bound the range. date=YYYY-MM-DD is a convenience alias for a single day and is exactly from=YYYY-MM-DD&to=YYYY-MM-DD; it exists because /v1/racing/results and /v1/racing/acceptances take date and callers reasonably expect the same spelling here. /v1/racing/events does NOT — it is forward-looking and takes hours_ahead. Passing date together with from or to is a free 422 rather than a silent precedence rule.

An unknown bookmaker key, an unknown parameter, a reversed date range or a bad date is an error and costs nothing.

get/v1/racing/closing-lines

Request

  • Base URL: https://api.puntersedge.online
  • URL: https://api.puntersedge.online/v1/racing/closing-lines
  • Auth: API key in header X-API-Key

Query parameters

fromstring nullable

ISO date/time on the race start. Defaults to the archive floor.

tostring nullable

ISO date/time. Defaults to now.

datestring nullable

Single-day alias: date=YYYY-MM-DD is exactly from=YYYY-MM-DD&to=YYYY-MM-DD. Mutually exclusive with from/to. Also accepted by /v1/racing/results. NOT by /v1/racing/events, which is forward-looking and takes hours_ahead.

venuestring nullable

Venue name, case-insensitive, exact match.

bookmakersstring nullable

Comma-separated bookmaker keys, case-insensitive. An unrecognised key is a free 422 naming the valid keys, so a typo costs nothing.

categorystring 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.

categoriesstring nullable

Alias of category. Comma-separated horse, harness, greyhound. Both spellings are accepted on every racing endpoint.

countrystring 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_idstring nullable

Single race, joinable to /v1/racing/price-history while that race is still inside the 45-day snapshot window.

race_numberinteger nullable

Race number. With venue and date it names one race, as on /v1/racing/price-history and /v1/racing/markets: date here is the UTC day of the start, and when that day holds no row for the race the Australian Eastern meeting date (the date /v1/racing/results gives) is tried instead.

closing_onlyboolean

Only rows whose last observation was within 300s of the jump. True by default because 14.5% of series are NOT closing lines and silently mixing them in is how a CLV study goes wrong.

resulted_onlyboolean

Only rows from races that have a result.

include_flaggedboolean

Include rows flagged venue_split_suspect or name_fragment_suspect. False by default; set true if you want the contaminated rows and intend to handle them.

include_scratchedboolean

Include runners the result marked scratched. False by default: four books keep pricing a scratched runner, so the archive holds a "close" for a runner that never started (rows carry scratched=true). Only AU thoroughbred and greyhound results name their scratchings, so on harness and NZ rows scratched=false means not known, never ran.

formatstring

csv streams a flat table with a stable column order — the format a modeller actually wants.

limitinteger
offsetinteger

Headers

X-API-Keystring
X-RapidAPI-Proxy-Secretstring

Response

Successful Response

rows_returnedinteger required
total_rowsinteger required

Rows matching the filter before limit/offset

limitinteger required
offsetinteger required
archive_fromstring nullable

Earliest start_time in the whole archive. The archive only grows; this is its true floor as of right now, not a promise.

window_daysinteger nullable

Set when the caller's plan clamps the lookback. NULL means the full archive was searched.

resulted_rowsinteger required

Rows in the matching set that have a result_status. Published because result coverage is thin and growing (5.3% of archived races on 2026-08-18) — do not assume a NULL finish_position means the runner lost. It means no position was published for the runner: harness and NZ results named the placegetters only before 2026-10-04 and carry the full field from then, while AU thoroughbred and greyhound results position every finisher (from 2026-09-14).

Example response

{
  "archive_from": "2026-08-04T09:20:44Z",
  "limit": 500,
  "offset": 0,
  "resulted_rows": 640,
  "rows": [
    {
      "bookmaker_key": "sportsbet",
      "books_in_consensus_close": 12,
      "books_in_consensus_open": 9,
      "category": "horse",
      "close_place_price": 2.3,
      "close_secs_to_jump": 41,
      "close_top2_price": 3.3,
      "close_top3_price": 2.2,
      "close_top4_price": 1.75,
      "close_win_price": 7,
      "consensus_close_price": 7.2,
      "consensus_close_prob": 0.1388,
      "consensus_open_price": 7.6,
      "country": "AU",
      "finish_position": 2,
      "horse_ref": "pe:aeolian",
      "is_closing_line": true,
      "meeting_date_aet": "2026-08-15",
      "name_fragment_suspect": false,
      "open_is_baseline": true,
      "open_place_price": 2.2,
      "open_secs_to_jump": 3595,
      "open_top2_price": 3.1,
      "open_top3_price": 2.1,
      "open_top4_price": 1.7,
      "open_win_price": 6.5,
      "points_observed": 10,
      "race_id": "race_123",
      "race_number": 2,
      "result_status": "final",
      "runner_key": "aeolian",
      "runner_name": "Aeolian",
      "runner_number": 4,
      "runner_ref": "ra:2098765",
      "scratched": false,
      "start_time": "2026-08-15T03:00:00Z",
      "venue": "Caulfield",
      "venue_id": "caulfield",
      "venue_site": "caulfield",
      "venue_split_suspect": false
    }
  ],
  "rows_returned": 1,
  "total_rows": 12480
}

Changes

No recorded changes to this operation across all 1 revision of this API.