/v1/pricesLive Prices
Current win and place prices for every runner in a race, updated continuously in the lead-up to the jump. Takes the same parameters as Race Form and Results, so a race you already fetch can be priced with the identical query string.
Premium only
403.Built for Polling
This is a deliberately small payload: a runner's number, name, whether it has been scratched, and its quotes. Nothing else. Form, statistics and career history are on Race Form, and they do not change between polls — re-downloading them every few seconds to read a number is wasted bandwidth on both sides.
Each quote carries more than the current number: the opening, high and low win prices, the recent movement history, and — once a race settles — that bookmaker's own starting price. You do not have to poll and store your own history to reconstruct a market move.
Responses are cached for 5 seconds at the edge, so polling faster than that returns the same body without reaching our origin. Poll every 5–10 seconds through the last few minutes before a jump, and as slowly as you like before that — a market hours out is not moving.
That window is deliberately tight because the books themselves are read every 3–5 seconds close to the off, and a late market can cross a price several times in under a minute. Polling on a half-minute cadence will not just lag — it will show you a number the book has already moved away from.
The same prices also appear on Results for Premium keys, so an existing integration gains them with no code change. Use this endpoint when prices are what you actually want.
Before the Jump, and After It
Nothing here implies the race has run. A race with no market up yet returns 200 with empty prices arrays, not a 404 — "the market is not up yet" is a real answer and it changes on its own.
Prices stop moving once a race jumps, but the quote does not vanish. Past the freshness allowance the entry is still served with stale: true, and win and place dropped — those are claims about what you can bet right now. What survives is the record of what happened: open, close, high, low, movements, and each book's own startingPrice once it publishes one. A finished race therefore reads as stale on every quote, and that is correct rather than a coverage gap.
For the official settled figure, see the runner-level startingPrice on Results, which is a different thing from either of these — see below.
Query Parameters
datestringYYYY-MM-DD. Accepts dates within the last 30 days up to 7 days ahead. Requests outside this window return a 400.trackstringEagle Farm. Resolved through the same venue matching as other endpoints.raceintegerrace_codestringgallops, harness, greyhounds. Defaults to gallops.countrystringau.timezonestringdate is interpreted in. Defaults to Australia/Sydney.Request
curl -H "X-API-Key: YOUR_KEY" \
"https://api.formfav.com/v1/prices?date=2026-08-25&track=Gloucester%20Park&race=9&race_code=harness&country=au"Response
The fluctuation fields are win-market only
open, close, high, low, startingPrice and movements describe the win market. There is no place equivalent. Each is optional and a bookmaker that publishes no fluctuation data carries none of them — that is the common case, not an error, so treat every one as absent by default rather than expecting a full set.datestringtrackstringraceNumberintegerraceCodestringcountrystringstartTimestringtimezonestringAustralia/Perth.numberOfRunnersintegerrunnersarrayrunners[].numberintegerrunners[].namestringrunners[].scratchedbooleanrunners[].pricesarrayrunners[].prices[].bookmakerstringtab.runners[].prices[].marketTypestringfixed or tote. One bookmaker can quote both, so a runner may carry two entries for the same bookmaker differing only here.runners[].prices[].winnumber | omittedstale is true — it is a claim about what you can bet right now, so it is withheld rather than served old.runners[].prices[].placenumber | omittedrunners[].prices[].stalebooleanrunners[].prices[].opennumber | omittedrunners[].prices[].closenumber | omittedwin while the market is trading and stops when the book stops quoting, so after the jump it is the true closing price rather than disappearing with the live fields.runners[].prices[].highnumber | omittedmovements by its length cap.runners[].prices[].lownumber | omittedrunners[].prices[].startingPricenumber | omittedrunners[].prices[].movementsarrayrunners[].prices[].movements[].pricenumberrunners[].prices[].movements[].atstringrunners[].prices[].capturedAtstringstale to judge age yourself.Example Response
Runner 1 carries three entries, deliberately different shapes: TAB's fixed quote is fresh and has full fluctuation history; TAB's tote quote is the same bookmaker on the other market, with no place price and no fluctuation data (note movements is still present, just empty); and Ladbrokes has gone stale, so its win and place are withheld while close and its range remain. Runner 2 is scratched, so there is nothing to quote.
{
"date": "2026-08-25",
"track": "Gloucester Park",
"raceNumber": 9,
"raceCode": "harness",
"country": "au",
"startTime": "2026-08-25T13:36:00Z",
"timezone": "Australia/Perth",
"numberOfRunners": 12,
"runners": [
{
"number": 1,
"name": "Fireball Friday",
"scratched": false,
"prices": [
{
"bookmaker": "tab",
"marketType": "fixed",
"win": 8.5,
"place": 2.6,
"open": 12.0,
"high": 12.0,
"low": 8.0,
"movements": [
{ "price": 12.0, "at": "2026-08-25T08:02:11Z" },
{ "price": 9.5, "at": "2026-08-25T09:47:52Z" },
{ "price": 8.0, "at": "2026-08-25T10:11:04Z" },
{ "price": 8.5, "at": "2026-08-25T10:16:33Z" }
],
"stale": false,
"capturedAt": "2026-08-25T10:16:33Z"
},
{
"bookmaker": "tab",
"marketType": "tote",
"win": 9.2,
"movements": [],
"stale": false,
"capturedAt": "2026-08-25T10:16:33Z"
},
{
"bookmaker": "ladbrokes",
"marketType": "fixed",
"close": 8.0,
"high": 11.0,
"low": 8.0,
"movements": [],
"stale": true,
"capturedAt": "2026-08-25T09:58:02Z"
}
]
},
{
"number": 2,
"name": "Handsome Rogue",
"scratched": true,
"prices": []
}
]
}Stale Quotes and Empty prices Arrays
An empty array is a narrower signal than it used to be. A quote that has merely gone stale is still returned, flagged with stale: true and stripped of win and place. An entry disappears only when nothing is left worth serving — that is, when it has no win, no place and no close.
So an empty array means we hold nothing at all for that runner: it is scratched, or no book has priced it yet. Check scratched first — that one is unambiguous, because it is a statement about the runner rather than the absence of a quote. The other cannot be told apart from the outside: a bookmaker withdraws a price the same way whether it has suspended the market or simply not opened it.
Do not read an empty array as “the race is over”. A settled race is the opposite case: its quotes are all stale, all present, and all carrying their closing prices.
How old a quote may be before it is flagged stale is measured against time to jump, not a fixed clock, because the upstream feeds refresh on that same ladder. A ten-minute-old price is current for a race two days out and long stale for one about to start:
| Time to jump | Quote stays fresh for |
|---|---|
| Under 30 min | 2 min |
| Under 90 min | 3 min |
| Under 3 h | 6 min |
| Under 6 h | 9 min |
| Under 24 h | 15 min |
| Over 24 h | 30 min |
| Race is off, or start time unknown | 2 min |
These are indicative rather than contractual — they track the upstream refresh rate and will move if it does. Read stale and capturedAt rather than reimplementing the ladder.
Live Prices vs the Two Starting Prices
Three odds-shaped fields exist across the API and none is interchangeable with another. All are decimal odds, which is what makes them easy to confuse — and two of them are called startingPrice.
prices[].win | prices[].startingPrice | placings[].startingPrice | |
|---|---|---|---|
| Where | /v1/prices and /v1/results | Alongside it, on the same quote | /v1/results only |
| What it is | What this book is offering right now | What this book settled its own win bets at | The racing body's official settled price |
| When | Before the jump, moving continuously | After the race settles, fixed | After the race, fixed forever |
| Per bookmaker | Yes — one entry per book and market | Yes — books disagree with each other | No — one figure for the runner |
| Tier | Premium and Enterprise | Premium and Enterprise | Pro and above |
| Coverage | Whatever the books are pricing | Only books that publish one | Partial and still filling; absent for NZ harness |
Do not average them, and do not treat a starting price as a late live price. They answer different questions: win is what you could have taken, and the two startingPrice fields are what settled — one per book, one officially. If you are reconciling a bet, use the book's own; if you are backtesting against the published record, use the official one. Expect them to disagree, and note that close is a third thing again: the last price offered, which is not necessarily the price the book settled at.
Errors
403Forbidden404Not Found429Too Many Requests