Thoroughbred form, career record and profile for one horse
Cost: 3 credits. One AU thoroughbred's recent form, career record and profile, parsed from Racing Australia's public horse page — the registry's own data.
SOURCE AND FRESHNESS. The horse's page is read on demand and the parsed result stored (source.fetched_at dates it), so the first query for a horse pays a page-read to RA (typically under two seconds) and later reads are instant. A horse's form only changes when it races or trials, so the stored copy is re-read from RA only when our own results archive shows the horse has started since it was read, or when the copy is more than seven days old (a barrier trial is invisible to results, so it surfaces within a week at worst). Inside six hours it is served without even that check. A horse collected once, by you or by anyone, is therefore served from our store without another RA read for as long as it stays unraced. When RA cannot be reached and a stored copy exists, that copy is served with source.stale: true rather than an error. When RA is refusing reads (its bot wall) and no stored copy exists, the answer is 503 with a Retry-After header and fresh reads pause for that long; stored horses keep serving throughout. Only a 200 is billed — a 404, 502 or 503 costs nothing.
FRESH READS ARE CAPPED PER KEY AND PACED (2026-10-13). A fresh read is one that has to go to Racing Australia because we hold no current copy of the horse's page. Each API key may make at most 30 fresh reads in any rolling 60 seconds, and fresh reads from every caller together leave at most one every 2.5 seconds (bursts of three); a read whose turn is more than 20 seconds away is not held open. Past either limit nothing is sent to Racing Australia and the answer is the one given while RA is refusing reads: a horse we hold is served from the store, otherwise its runs in our own results where we have them, otherwise a 503 with Retry-After, unbilled, whose detail says which limit it was. Why, measured to 2026-10-12: over the previous 14 days Racing Australia's refusals closed fresh reads for every caller 94 times, each refused read prolongs a closure, and one key alone sent 44 calls in two minutes.
RESOLVING horse. Pass a name, or the pe: horse_ref that every thoroughbred surface now publishes — they resolve identically, because the horse_ref is the folded name. An ra: code from runner_ref is accepted too, but prefer the name or horse_ref: Racing Australia issues a new code for the same horse each day. Names resolve against horses seen on our own feeds: acceptances roughly three days ahead, plus every archived result runner (from 2026-09-01) — and, failing both, our own run store and stored form pages, so a horse that last raced months ago still resolves (2026-10-03). Where we hold runs but no readable RA page, those runs are the answer (source.feed 'puntersedge_results'). A name we have never seen returns 404 even when RA knows the horse — there is deliberately no name-search against RA itself.
ONE NAME, ONE HORSE. A name resolves to the horse, and the most recently seen of its Racing Australia codes is read — a horse that has raced more than once holds several codes, because Racing Australia issues a new one each day. ambiguous is always false and candidates always empty; both stay on the response for compatibility. Two different horses sharing one folded name cannot be told apart here; the AU register does not allow two ACTIVE horses to share one (a name is released about 17 years after its horse retires).
horse_ref ON THE RESPONSE is that identity, pe:<name>, and it is the same value on /v1/racing/results runners and placings, the live board, the closing-line and price-path archives, /v1/racing/horses/runs and the backfill. runner_ref is the code the page was read under; it changes from day to day. Join a horse on horse_ref; join the rows about one race on runner_ref.
THE STORE IS KEYED ON THE HORSE. A page collected under ANY of the horse's codes — by you, by another customer, or by the paced backfill — answers every later lookup of that horse.
READING A RUN. Runs come newest first: the page's recent-form table, typically the last ten starts plus barrier trials (trial: true — RA publishes those with $0 prize, 0kg weights and no market). race_class is RA's official class string ('MDN-SW', 'BM64', 'CTRY MDN'), the first place race class appears anywhere on this API. prices is the published price path oldest-quote-to-SP and sp its last entry. margin_l is lengths behind the winner — on the horse's own win, its winning margin. pir is position in running at the 800m and 400m marks where published. placegetters names the two nearest finishers with their own ra: codes, so one horse's page walks to its rivals'. Track names are RA's compressed codes ('W FM', 'RAND') served as published. trainer on a run is the trainer OF THAT DAY, joined from our own results archive — populated for runs from 2026-09-01 onward and NULL before, because RA's form table names only the current trainer (profile.trainer); the archive floor moves back nowhere, so the populated share grows with every meeting.
THE CAREER BLOCK. RA's own summaries, not derived from the runs list: overall starts/wins/seconds/thirds, prizemoney, and per-dimension splits (first_up, second_up, firm/good/soft/heavy/synthetic; track/distance splits appear when RA renders them). These cover the whole career even where the runs list shows only recent starts.
JOINING TO LIVE DATA. runner_ref here, on /v1/racing/results runners and on enriched live runners is the same identifier FOR ONE MEETING, so a pre-race model row joins to that day's official result and to the form read for it. Across meetings, join on horse_ref instead — it is on all of those surfaces too and it does not change. jockey_code and meet_code are RA's own identifiers for the rider and the meeting, and those do persist.
Cached 10 minutes per query on top of the six-hour page store.
Request
- Base URL: https://api.puntersedge.online
- URL: https://api.puntersedge.online/v1/racing/horses/form
- Auth: API key in header X-API-Key
Query parameters
Horse name, the pe: horse_ref published on every thoroughbred surface ('pe:custo'), or the ra: entry code from runner_ref on results/live runners. Names match case- and punctuation-insensitively on the whole name, and a name and its horse_ref resolve identically.
Headers
Response
Successful Response
Example response
{
"ambiguous": false,
"candidates": [],
"career": {
"prizemoney": 4875,
"seconds": 0,
"splits": {
"first_up": {
"seconds": 0,
"starts": 2,
"thirds": 1,
"wins": 0
},
"good": {
"seconds": 0,
"starts": 2,
"thirds": 1,
"wins": 0
}
},
"starts": 3,
"thirds": 1,
"wins": 0
},
"horse_name": "Zweigelt",
"horse_ref": "pe:zweigelt",
"profile": {
"age_sex": "4yo Bay Gelding",
"colours": "Lime, Purple And Gold Braces And Armbands",
"dam": "MEDGRINDA",
"dob": "2022-11-01",
"last_gear_change": "20-Aug-2026, Blinkers, First Time",
"sire": "VINO ROSSO (USA)",
"trainer": "Matthew Dunn",
"trainer_location": "Murwillumbah"
},
"runner_ref": "ra:MTMwNDY2ODI0NA",
"runs": [
{
"barrier": 9,
"date": "2026-08-18",
"distance_m": 1430,
"field_size": 9,
"going": "Soft",
"going_rating": 7,
"jockey": "Andrew Mallyon",
"jockey_code": "MTQ5MjQ5OTIw",
"margin_l": 0.76,
"meet_code": "MTI2NzM2MDQwMA",
"pir": [
{
"at_m": 800,
"position": 7
},
{
"at_m": 400,
"position": 7
}
],
"placegetters": [
{
"name": "Sunset Trail",
"position": 1,
"runner_ref": "ra:MTMwMDQ4MDk2MA",
"weight_kg": 58
}
],
"position": 4,
"prices": [
7.5,
4,
4.8,
5
],
"prize_total": 27000,
"prize_won": 1300,
"race_class": "MDN",
"race_number": 4,
"result_raw": "4th of 9",
"sectional_distance_m": 600,
"sectional_time_s": 37.16,
"sp": 5,
"time_s": 86.82,
"track": "GRAF",
"trial": false,
"weight_kg": 58.5
}
],
"source": {
"feed": "racing_australia",
"fetched_at": "2026-09-01T11:20:00+00:00",
"region": "AU",
"sport": "thoroughbred",
"stale": false
}
}Changes
No recorded changes to this operation across all 1 revision of this API.