Race Data API
Resulted Australian thoroughbred races with exchange odds before the start, starting prices and results.
https://racedata.prospec-au.comX-API-Key headerOverview
The API gives read-only access to resulted Australian thoroughbred races recorded from the Betfair Exchange. For every race you get:
- Race details: venue, race number, distance, class, scheduled start time, field size and places paid.
- Odds for every runner in the WIN and PLACE markets at 8 time points before the scheduled start: best back price, best lay price, the amount available at each, and the last traded price.
- Results: winner, placings, scratchings and the Betfair Starting Price (BSP).
A race is identified by its race id, which is the Betfair WIN market id, for example 1.264460108.
All dates and times are UTC.
Quick start
Replace YOUR_KEY with the key you were given.
curl -H "X-API-Key: YOUR_KEY" "https://racedata.prospec-au.com/v1/coverage"
{"first_date": "2026-10-19", "last_date": "2026-10-29", "days": 11, "races": 408}
Then list a day's races, and request one race by its id:
curl -H "X-API-Key: YOUR_KEY" "https://racedata.prospec-au.com/v1/races?date=2026-10-27"
curl -H "X-API-Key: YOUR_KEY" "https://racedata.prospec-au.com/v1/races/1.264460108"
Or download a whole day as a spreadsheet-ready CSV file:
curl -H "X-API-Key: YOUR_KEY" -o races.csv "https://racedata.prospec-au.com/v1/runners?date=2026-10-27&format=csv"
Authentication
Send your key in the X-API-Key header with every request. Requests without a valid key get
401 Unauthorized.
Endpoints
Coverage
The first and last dates that have resulted races, the number of days with data, and the total number of races. Use it to find out which dates you can request.
List races
A summary of each resulted race (no runners or odds), ordered by start time.
| Parameter | Example | Description |
|---|---|---|
date | 2026-10-27 | One day (UTC). Use either date, or from and to. |
from | 2026-10-20 | First day of a range. |
to | 2026-10-27 | Last day of a range (included). At most 31 days per request. |
venue | Flemington | Optional. Only races at this venue (not case-sensitive). |
With no dates, the endpoint returns yesterday's races.
{
"from": "2026-10-27",
"to": "2026-10-27",
"count": 23,
"races": [
{
"race_id": "1.264460108",
"date": "2026-10-27",
"start_utc": "2026-10-27T02:15:00",
"venue": "Hawkesbury",
"race": "R1 1100m Mdn",
"race_no": 1,
"distance_m": 1100,
"class": "Mdn",
"field_size": 10,
"places_paid": 3,
"win_market_id": "1.264460108",
"place_market_id": "1.264460109"
}
]
}
Get one race
One race with every runner, including scratched runners. Each runner has its result, its starting prices, and its WIN and PLACE prices at every time point.
{
"race_id": "1.264460108",
"venue": "Hawkesbury",
"race": "R1 1100m Mdn",
"start_utc": "2026-10-27T02:15:00",
"field_size": 10,
"places_paid": 3,
"...": "(other race fields as in the list above)",
"runners": [
{
"selection_id": 2714086,
"saddlecloth": 1,
"horse": "Time To Dance",
"win_result": "WINNER",
"place_result": "PLACED",
"win_bsp": 19.36,
"place_bsp": 5.7,
"win_prices": {
"T-30m": {"back": 18.5, "back_size": 26.98, "lay": 130.0, "lay_size": 27.52, "ltp": 19.5},
"T-90s": {"back": 21.0, "back_size": 33.05, "lay": 27.0, "lay_size": 50.73, "ltp": 23.0}
},
"place_prices": {
"T-90s": {"back": 4.3, "back_size": 79.85, "lay": 80.0, "lay_size": 15.32, "ltp": 5.0}
}
}
]
}
The example is shortened: a full response has all 8 time points for each market, when they were recorded.
Runners table
A flat table with one row per horse for all races in the date range. This is the easiest way to load data into a spreadsheet, pandas or R.
| Parameter | Example | Description |
|---|---|---|
date, from, to, venue | As for List races. At most 31 days per request. | |
format | csv | json (default) or csv. CSV is sent as a file download. |
The JSON form returns {"from", "to", "count", "columns", "rows"}, where each row has the
CSV columns as keys.
Time points
Prices are recorded at these times before the scheduled start. Races often start a little after the scheduled time, so the real time before the start can be longer.
| Label | Time before scheduled start |
|---|---|
T-60m | 60 minutes |
T-30m | 30 minutes |
T-15m | 15 minutes |
T-5m | 5 minutes |
T-2m | 2 minutes |
T-90s | 90 seconds |
T-60s | 60 seconds |
T-30s | 30 seconds |
Fields
Race
| Field | Type | Description |
|---|---|---|
race_id | string | Race identifier (the WIN market id). Use it with /v1/races/{race_id}. |
date | string | Date of the scheduled start (UTC), YYYY-MM-DD. |
start_utc | string | Scheduled start time (UTC). |
venue | string | Track name. |
race | string | Race name, for example R1 1100m Mdn. |
race_no | integer | Race number at the meeting. |
distance_m | integer | Distance in metres. |
class | string | Race class, for example Mdn (maiden), Hcap (handicap), CL1, Grp1. |
field_size | integer | Number of runners that started (scratched runners not counted). |
places_paid | integer | Number of places paid in the PLACE market (usually 3, or 2 in small fields). |
win_market_id, place_market_id | string | Exchange market ids of the WIN and PLACE markets. |
Runner
| Field | Type | Description |
|---|---|---|
selection_id | integer | Exchange runner id (the same in the WIN and PLACE markets). |
saddlecloth | integer | Saddlecloth (race book) number. |
horse | string | Horse name. |
win_result | string | WINNER, LOSER or REMOVED (scratched). |
place_result | string | PLACED, UNPLACED or REMOVED. |
win_bsp, place_bsp | number | Betfair Starting Price in the WIN and PLACE markets. |
win_prices, place_prices | object | Prices by time point (see below). |
Prices at a time point
| Field | Description |
|---|---|
back | Best price available to back (bet on the horse). |
back_size | Amount available at that back price (AUD). |
lay | Best price available to lay (bet against the horse). |
lay_size | Amount available at that lay price (AUD). |
ltp | Last traded price. |
Prices are decimal odds (for example 3.5 returns $3.50 for each $1, including the stake).
null means no price was available.
CSV columns (runners table)
One row per horse. The first columns describe the race and the result:
date, start_utc, venue, race, race_no, distance_m, class, field_size, places_paid,
win_market_id, place_market_id, selection_id, saddlecloth, horse,
win_result, place_result, win_bsp, place_bsp
Then 48 price columns, named {market}_{price}_{time point}:
market:winorplaceprice:back,layorltptime point:T-60m…T-30s
For example win_back_T-90s is the best back price in the WIN market 90 seconds before the scheduled start.
The amounts available (back_size, lay_size) are only in the per-race JSON.
Empty cells mean no price was available.
date,start_utc,venue,race,race_no,distance_m,class,field_size,places_paid,win_market_id,place_market_id,selection_id,saddlecloth,horse,win_result,place_result,win_bsp,place_bsp,win_back_T-60m,win_lay_T-60m,win_ltp_T-60m,win_back_T-30m,...
2026-10-27,2026-10-27T02:15,Hawkesbury,R1 1100m Mdn,1,1100,Mdn,10,3,1.264460108,1.264460109,2714086,1,Time To Dance,WINNER,PLACED,19.361193061783,5.7,22.0,1000.0,23.0,18.5,...
Data notes and limits
- Coverage: Australian thoroughbred races only. Harness racing is not included. Use
/v1/coverageto see the dates available. - Only resulted races are returned. A race appears after its result is recorded, usually 10–30 minutes after the start.
- Price timing: prices come from a delayed exchange feed and can be up to about 3 minutes old at the time point shown. They are suitable for research, not for live betting decisions.
- Gaps: some days or races can be missing, and some time points can be empty, for example if a market had no prices yet or the race was added late.
- Lay prices in quiet markets can be very high (for example 1000) when nobody is offering a lay bet at a normal price.
- Currency: amounts are in Australian dollars.
- Limits: 60 requests per minute for each key, and at most 31 days in one request. For longer periods, make one request per month.
Errors
Errors return a JSON body: {"detail": "description of the problem"}.
| Status | Meaning |
|---|---|
400 | Bad request: a date is not YYYY-MM-DD, to is before from, the range is more than 31 days, or the race id is not valid. |
401 | The key is missing, wrong or turned off. |
404 | No resulted race with that id. |
429 | Too many requests. Wait a minute and try again. |
5xx | Temporary server problem. Try again later. |
Code examples
Python: one month into a pandas table
import io
import requests
import pandas as pd
API = "https://racedata.prospec-au.com"
HEADERS = {"X-API-Key": "YOUR_KEY"}
resp = requests.get(f"{API}/v1/runners",
params={"from": "2026-10-01", "to": "2026-10-31", "format": "csv"},
headers=HEADERS, timeout=60)
resp.raise_for_status()
runners = pd.read_csv(io.StringIO(resp.text))
winners = runners[runners.win_result == "WINNER"]
print(winners[["date", "venue", "race", "horse", "win_back_T-90s", "win_bsp"]])
Python: every race on a day, with all prices
import requests
API = "https://racedata.prospec-au.com"
HEADERS = {"X-API-Key": "YOUR_KEY"}
day = requests.get(f"{API}/v1/races", params={"date": "2026-10-27"}, headers=HEADERS, timeout=30).json()
for summary in day["races"]:
race = requests.get(f"{API}/v1/races/{summary['race_id']}", headers=HEADERS, timeout=30).json()
for runner in race["runners"]:
late = runner["win_prices"].get("T-90s") or {}
print(race["venue"], race["race"], runner["horse"], late.get("back"), runner["win_result"])
Stay under 60 requests per minute. For a whole day, the runners table needs only one request.
JavaScript (Node.js 18+)
const API = "https://racedata.prospec-au.com";
const res = await fetch(`${API}/v1/races?date=2026-10-27`, { headers: { "X-API-Key": "YOUR_KEY" } });
if (!res.ok) throw new Error(`HTTP ${res.status}: ${(await res.json()).detail}`);
const { races } = await res.json();
console.log(races.map(r => `${r.start_utc} ${r.venue} ${r.race}`));
Excel (Power Query)
Data → Get Data → From Other Sources → Blank Query → Advanced Editor, then paste:
let
Source = Web.Contents("https://racedata.prospec-au.com/v1/runners",
[Query = [from = "2026-10-01", to = "2026-10-31", format = "csv"],
Headers = [#"X-API-Key" = "YOUR_KEY"]]),
Table = Csv.Document(Source, [Delimiter = ",", Encoding = 65001]),
Promoted = Table.PromoteHeaders(Table)
in
Promoted
Interactive docs
The interactive documentation lists every endpoint and lets you try requests in the browser:
click Authorize, or add your key in the X-API-Key field of a request.