The CARD - every race scheduled in the requested window, optionally filtered by racing category and country. EVERY country is served by default, not only AU and NZ — pass `country=AU` (pairing it with `include_unresolved=true`, per that parameter) if you want an Australian card. **How far ahead you can see (2026-09-22).** `hours_ahead` reaches 24h on Free and Hobby, 48h on Standard, and 168h (7 days) on Plus and above. Every response carries your ceiling as `X-Horizon-Hours` and the window actually used as `X-Horizon-Used-Hours`, so you never have to discover it by trial. Asking past it is a 403 naming the tier that reaches further, and costs no credit -- it is never a silent clamp to a shorter window. `date=YYYY-MM-DD` is the single-day alias for an Australian Eastern meeting day; a day is reachable when its last moment is inside your horizon, so it is bounded by the same ceiling rather than being a second entitlement. Nothing lost anything here: 24h was the hard cap for every plan, free included, until this landed, and Free and Hobby still have exactly that. AU cards carry thoroughbred, harness and greyhound; NZ cards carry thoroughbred and harness. Hong Kong (`country=HK`, feature_requests #29, 2026-09-09) is listed too, on the HKJC's own nights — Happy Valley on Wednesday evenings and Sha Tin on Saturday or Sunday afternoons, AEST — whenever an Australian bookmaker quotes the meeting. Thoroughbred only, and PRICES ONLY on /v1/racing/next-to-go and /v1/racing/best-odds: a Hong Kong race carries no results, form, race conditions or `runner_ref`, because those come from Racing Australia and cover AU thoroughbreds only (`horse_ref` is derived from the runner's name, so it is there). Foreign meetings other than Hong Kong still appear here whenever a book in the panel lists them; this endpoint has never filtered them out. This is the schedule endpoint, and it lists a race before any book has priced it. Since 2026-09-01 the feed reads the early markets (Sportsbet's full card 3-12h out, BetRight's early lane, a PointsBet wide sweep), so the FIRST price on a race now lands well ahead of the jump: measured over the 48h to 2026-09-03 11:00 UTC, median 10.1h before the jump on AU thoroughbreds (79 of 80 races priced 3h+ out), 5.3h on AU greyhounds, 6.8h on AU harness. That first price is one or two books; the full board still fills inside about an hour of the jump. A race listed here with no prices yet is one no book has opened - /v1/racing/next-to-go carries the priced window, and the `race.odds_open` webhook fires the moment a race's first prices land. `scratchings` is served here as well as on /v1/racing/next-to-go, so a scratching is visible from the moment a book reports it rather than only once the race enters the priced window. Its entries are {name, number, barrier, scratched_at, late, emergency}, merged across every reporting book — `scratched_at` (UTC) is the book's own stamp where it sends one (Ladbrokes, Neds, BetRight) and Racing Australia's official scratching sheet otherwise for AU thoroughbreds, read every two hours; `late` and `emergency` come from the same sources. On greyhound cards some entries are box-vacancy placeholders - a literal '....' or 'Vacant Box' - and several books can report the same empty box separately, so count boxes by `number`, not by list length. `status` is written when the race row is created and never revised, so every race in this window reads 'open'. `market_status` and `inplay` are the live betting-market state. `market_closed_at` and `inplay_at` are the race lifecycle: when the market was first seen shut, and when the race was first seen in-play — the actual off, where `start_time` is the advertised one and moves. Both are POLL-OBSERVED: racing polls every 8s inside 120s of a jump, 15s inside 10 minutes and 20s otherwise, so each is the first poll at which the state was seen and lands within about one poll interval after it. Both are SET ONCE, so a market that flickers SUSPENDED -> OPEN -> SUSPENDED reports the first suspension. Null never means the market stayed open. This endpoint serves upcoming races and the live row is purged 10 minutes after the jump; /v1/racing/results carries the same fields permanently. **Who reports market state, and what you see.** Two feeds do. PointsBet closes its book at the real jump — measured 2026-09-03 over 9 live races followed across their jump, 0.2 to 1.4 minutes AFTER the advertised start on AU/NZ cards — and that is what populates `market_closed_at` for most races. The Betfair Exchange reports state while it quotes the market, which ends about two minutes BEFORE the jump (measured 2026-09-01 on Horsham R3, advertised 05:24 UTC: betfair last seen 119s before the off), which is why these fields read null on every race before 2026-09-03. Exchange-sourced market state is withheld from customer plans pending a data licence, so a race whose state only the exchange saw reads null for customers; bookmaker-sourced state is served to everybody. `market_state_source` names the feed behind the values you receive, and is itself null whenever they are withheld. `inplay_at` stays exchange-only: bookmakers do not run racing in-play, so a bookmaker feed can say when it CLOSED its book but not when the race went. For a race the exchange has stopped quoting, `market_closed_at` is the closest measured instant to the off.
X-API-Key
1 credit
curl 'https://api.puntersedge.online/v1/racing/events' \
-H 'X-API-Key: YOUR_KEY'
| Name | In | Type | Required | Description |
|---|---|---|---|---|
hours_ahead |
query | integer | no | How many hours ahead to list, from now. Your ceiling depends on the plan and is returned on every response as X-Horizon-Hours: 24 on Free and Hobby, 48 on Standard, 168 (7 days) on Plus and above. Asking past your ceiling is a free 403 naming it, never a silent clamp. Mutually exclusive with date. |
date |
query | string | no | Single-day alias: date=YYYY-MM-DD lists that Australian Eastern meeting day instead of a rolling hours_ahead window. Mutually exclusive with hours_ahead. A day is reachable when its last moment is inside your horizon, so this is bounded by the same X-Horizon-Hours ceiling and is not a separate entitlement. Forward-looking only -- a past date is a free 422 pointing at /v1/racing/results and /v1/racing/closing-lines. |
categories |
query | string | no | |
category |
query | string | no | Alias of `categories`. 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. An unknown value is a free 422 naming the valid ones. |
country |
query | string | no | ISO country codes, comma-separated (e.g. AU). Omit for every country. HK lists Sha Tin and Happy Valley on the HKJC race nights (prices only, no results or form). Pair with include_unresolved=true or you will silently lose races whose meeting is not confirmed yet. |
include_unresolved |
query | boolean | no | 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 — 58.8% of horse races in a measured window — so country=AU alone can return far fewer races than are running, or none at all. Pass true to get them, and read `country` per race to see which are still unresolved. |
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 1 credit.
Field names and types are as the API returns them; values are a real sample, trimmed to a few items.
[
{
"race_id": "2f0e2df8-5386-4aae-bbdb-7b784dee3e43",
"venue": "Grafton",
"venue_id": "grafton",
"venue_canonical": "Grafton",
"venue_site": "grafton",
"race_number": 1,
"category": "horse",
"start_time": "2026-09-07T03:20:00Z",
"country": "AU",
"race_name": "TEAM AIDAN MARKETS @ CRJC 22 NOVEMBER CB MDN PLT",
"distance_m": 1700,
"track_condition": "Soft (5)",
"weather": "Clear Sky",
"race_class": "MDN",
"conditions": "Maiden Plate",
"prize_total": 27000,
"rail": "True",
"track_name": "Grafton",
"scratchings": [
{
"name": "Airhawk",
"number": 1,
"barrier": 9,
"scratched_at": "2026-09-03T11:47:44Z"
},
{
"name": "Sweet September",
"number": 9,
"barrier": 3,
"scratched_at": "2026-09-06T18:58:41Z"
}
],
"track_condition_changed_at": null,
"status": "open",
"market_status": null,
"inplay": null,
"market_closed_at": null,
"inplay_at": null,
"market_state_source": null
},
{
"race_id": "c2cfc9b1-a037-4bcb-841f-5496bb0c9b82",
"venue": "Tatura",
"venue_id": "tatura",
"venue_canonical": "Tatura",
"venue_site": "tatura",
"race_number": 1,
"category": "horse",
"start_time": "2026-09-07T03:30:00Z",
"country": "AU",
"race_name": "Shepparton Club Maiden Plate",
"distance_m": 1000,
"track_condition": "Heavy (8)",
"weather": "Broken Clouds",
"race_class": "MDN",
"conditions": "Maiden Plate",
"prize_total": 27000,
"rail": "True Entire Circuit",
"track_name": "Tatura",
"scratchings": [],
"track_condition_changed_at": null,
"status": "open",
"market_status": null,
"inplay": null,
"market_closed_at": null,
"inplay_at": null,
"market_state_source": null
}
]
GET /v1/racing/acceptances — Full-day AU thoroughbred acceptance card: every meeting, race and runnerGET /v1/racing/best-odds — Best racing price per runner across booksGET /v1/racing/changes — Races and prices that changed since a timestampPOST /v1/racing/clv — Score bets against the closing line (CLV)GET /v1/racing/form — Every runner in one race with its recent runs, from our own resultsGET /v1/racing/greyhounds/form — Greyhound form history for one dog, from our own resultsGET /v1/racing/greyhounds/stats — Greyhound record by track, distance, box or grade, from our own resultsPOST /v1/racing/horses/backfill — Queue a batch of horses for paced form collectionThe free tier needs no credit card, and the sandbox endpoints need no key at all.
Get a free API key Quickstart