Racing
Get a race
One race: every book's fixed win and place price, Betfair back and lay, and the result.
GET
/odds/v1/racing/race- Cost
- 1 token
- Authentication
- X-API-Key, demo key works
Parameters
race_idstringrequiredA race_id from /racing/meetings.bookscomma listoptionalOnly these books, e.g. sportsbet,tab,ladbrokes.Default:all booksformatstringoptionaldecimal, american or fractional. See Odds formats.Default:decimal
Request
# Take the first race of the first AU thoroughbred meeting, then price it
RACE_ID=$(curl -s "https://api.b337.ai/odds/v1/racing/meetings?country=au&race_type=thoroughbred" \
-H "X-API-Key: demo" | jq -r '.data[0].races[0].race_id')
curl -sG "https://api.b337.ai/odds/v1/racing/race" \
--data-urlencode "race_id=$RACE_ID" \
--data-urlencode "books=sportsbet,tab,ladbrokes" \
-H "X-API-Key: demo" --compressedimport requests
BASE = "https://api.b337.ai/odds/v1"
HEADERS = {"X-API-Key": "demo"}
meetings = requests.get(f"{BASE}/racing/meetings", headers=HEADERS, timeout=10,
params={"country": "au", "race_type": "thoroughbred"}).json()
race_id = meetings["data"][0]["races"][0]["race_id"]
race = requests.get(f"{BASE}/racing/race", headers=HEADERS, timeout=10, params={
"race_id": race_id,
"books": "sportsbet,tab,ladbrokes",
}).json()
for runner in race["runners"]:
if runner["scratched"]:
continue
best = max(runner["books"].items(), key=lambda kv: kv[1]["win"])
print(runner["number"], runner["name"], "best win:", best[0], best[1]["win"])// Node 18+, saved as an .mjs file
const BASE = "https://api.b337.ai/odds/v1";
const headers = { "X-API-Key": "demo" };
const meetings = await (await fetch(`${BASE}/racing/meetings?country=au&race_type=thoroughbred`, { headers })).json();
const raceId = meetings.data[0].races[0].race_id;
const url = new URL(`${BASE}/racing/race`);
url.searchParams.set("race_id", raceId);
url.searchParams.set("books", "sportsbet,tab,ladbrokes");
const race = await (await fetch(url, { headers })).json();
for (const r of race.runners) console.log(r.number, r.name, r.books);Response
200 OK
{"schema_version": 1, "race_id": "gawler/4/(R)/au/2026-10-05",
"meeting_id": "gawler/(R)/au/2026-10-05", "venue": "Gawler", "country": "au",
"race_type": "thoroughbred", "race_type_code": "R", "race_number": 4, "date": "2026-10-05",
"start_time": "2026-10-05T05:10:00Z", "betfair_status": "OPEN", "in_play": false,
"race_state": "upcoming", "abandoned": false, "distance_m": 1100, "race_class": "CL1",
"betfair_market_ids": {"win": "1.263274446", "place_3": "1.263274447"},
"books": ["123bet", "allbets", "baggybet", "ballrbet", "bet365", "bet66", "..."],
"book_urls": {"betdeluxe": "https://www.betdeluxe.com.au/racing/Thoroughbred/AUS/gawler/829483/4/11830376"},
"runners": [
{"number": 1, "name": "Super Choice", "scratched": false, "selection_id": 103415948,
"books": {"123bet": {"win": 3.2, "place": 1.28, "changed_at": "2026-10-05T03:39:11Z"},
"allbets": {"win": 3.5, "place": 1.5, "changed_at": "2026-10-05T03:44:06Z"},
"baggybet": {"win": 3.4, "place": 1.38}},
"betfair": {"win": {"back": 4.1, "lay": 5.7, "back_size": 1.9, "lay_size": 5.01},
"place_3": {"back": 1.55, "lay": 3.05, "back_size": 13.49, "lay_size": 71.41}},
"tote": {"nsw": {"win": 3.6, "place": 1.4}, "vic": {"win": 3.5, "place": 1.4}},
"finish_position": null},
{"number": 2, "name": "Leliyn", "...": "..."}],
"source": "terminal_snapshot", "as_of": "2026-10-05T04:13:18Z",
"betfair_matched": {"win": 372.61, "place_3": 0.0},
"tote_pools": {"nsw": {"win": {"pool_total": 5120.4, "status": "Open", "jackpot": 0.0},
"place": {"pool_total": 1633.9, "status": "Open", "jackpot": 0.0}}},
"tote_updated_at": "2026-10-05T04:13:40Z", "result": null,
"odds_format": "decimal", "generated_at": "2026-10-05T04:14:03Z"}
// The same race once it has finished (trimmed):
{"race_id": "gawler/1/(R)/au/2026-10-05", "betfair_status": "CLOSED", "in_play": false,
"race_state": "finalised", "source": "spine",
"runners": [
{"number": 9, "name": "Miami Miller", "scratched": false,
"books": {"tab": {"win": 81.0, "place": 10.0, "changed_at": "2026-10-05T03:25:36Z"}},
"betfair": {}, "finish_position": "1st"},
{"number": 1, "name": "Ateyate", "scratched": false, "selection_id": 98273722,
"books": {"ladbrokes": {"win": 11.0, "place": 2.35, "changed_at": "2026-10-05T03:26:16Z"}},
"betfair": {}, "finish_position": null, "bsp": {"win": 16.0, "place": 3.0},
"tote": {"nsw": {"win": 12.6, "place": 2.4}, "vic": {"win": 12.1, "place": 2.3}}}],
"tote_pools": {"nsw": {"win": {"pool_total": 31058.62, "status": "Closed", "jackpot": 0.0},
"place": {"pool_total": 8202.98, "status": "Closed", "jackpot": 0.0}}},
"tote_updated_at": "2026-10-05T03:26:19Z",
"result": {"source": "tab", "official": true, "tab_status": "Paying",
"placings": [[9], [4], [6], [7]],
"dividends": {"nsw": {"win": [{"runners": [9], "dividend": 70.3}],
"place": [{"runners": [9], "dividend": 9.3},
{"runners": [4], "dividend": 1.2},
{"runners": [6], "dividend": 1.8}],
"quinella": [{"runners": [9, 4], "dividend": 83.6}]}},
"scratchings": [], "updated_at": "2026-10-05T03:37:15Z"}}Fields
runners[]carrynumber(saddlecloth or box),name,scratchedand, where we hold it, the Betfairselection_id.finish_positionis set once the race has a result.books[book].winandplaceare the book's fixed odds.changed_atis when that book's price last changed; see changed_at. A book that does not price a runner is absent.betfairholdswinand, where Betfair runs them,place_2andplace_3, each withback,lay,back_sizeandlay_size.bsp(win and place) appears once the race is off.tote.nswandtote.vicare TAB's win and place approximates (dollars per $1) for AU and NZ races, from about 30 minutes out.tote_poolsholds each pool'spool_total($),statusandjackpot, per jurisdiction;tote_updated_atis when TAB last updated them. Tote values are dollars and are never converted byformat.book_urlsmaps a book to its own page for the race, where we hold its id.betfair_matchedis the money matched per Betfair market.sourcesays where the body came from:terminal_snapshot(a race with live prices; theas_oftime is when it was built),spine(the field from our race record and, for a finished race, each book's last recorded price) orresults_store(only the result is left).resultisnulluntil TAB has one.placingsis a list of saddlecloth numbers per finishing slot; a dead heat puts several numbers in one slot and leaves the next slot empty.officialis true once TAB is paying.dividends(andmulti_dividendsfor the multi-race pools) are per $1 staked, stake included, per jurisdiction and pool, asrunnersplusdividend.scratchingslists the scratched runners.- A finished race's
booksprices are each book's last recorded price, which can be a tick taken just after the scheduled start. Checkchanged_at. formatconverts every book and Betfair price; see odds formats.
Tote fields are AU and NZ TAB only and appear once TAB has published them: tote, tote_pools, tote_updated_at and result.dividends. Placings and scratchings are always served.