GET
    /v1/prices
    Premium

    Live 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.

    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

    datestring
    required
    Meeting date, YYYY-MM-DD. Accepts dates within the last 30 days up to 7 days ahead. Requests outside this window return a 400.
    trackstring
    required
    Track name, e.g. Eagle Farm. Resolved through the same venue matching as other endpoints.
    raceinteger
    required
    Race number on the card.
    race_codestring
    optional
    One of gallops, harness, greyhounds. Defaults to gallops.
    countrystring
    optional
    Two-letter country code. Defaults to au.
    timezonestring
    optional
    Timezone the date is interpreted in. Defaults to Australia/Sydney.

    Request

    bash
    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

    datestring
    Meeting date.
    trackstring
    Canonical track name.
    raceNumberinteger
    Race number.
    raceCodestring
    gallops, harness or greyhounds.
    countrystring
    Two-letter country code.
    startTimestring
    Race start in UTC, ISO 8601.
    timezonestring
    Venue timezone, e.g. Australia/Perth.
    numberOfRunnersinteger
    Runners in the field, including scratchings. May exceed the number carrying prices.
    runnersarray
    One entry per runner, ordered by runner number.
    runners[].numberinteger
    Runner number — saddlecloth, or box number for greyhounds.
    runners[].namestring
    Horse or greyhound name.
    runners[].scratchedboolean
    Withdrawn before the race. Taken from the official race card, or from agreement among the independent bookmaker feeds covering the race where they have it first — all but one of them must agree, and never fewer than two, so a single feed reporting a scratching is not one. Never inferred from a missing price. The overlay is one-way: agreement can mark a runner scratched, but can never un-scratch one the official card has out. The same value appears on Race Form and Results for the same runner.
    runners[].pricesarray
    One entry per bookmaker and market, ordered by bookmaker name. Empty when there is no quote worth serving — see below.
    runners[].prices[].bookmakerstring
    Which bookmaker is quoting, e.g. tab.
    runners[].prices[].marketTypestring
    fixed or tote. One bookmaker can quote both, so a runner may carry two entries for the same bookmaker differing only here.
    runners[].prices[].winnumber | omitted
    Win price as decimal odds. Omitted when this bookmaker is not currently pricing the win market, and whenever stale is true — it is a claim about what you can bet right now, so it is withheld rather than served old.
    runners[].prices[].placenumber | omitted
    Place price as decimal odds. Omitted on the same basis.
    runners[].prices[].staleboolean
    The quote is older than the freshness allowance for this race — which tightens as the jump approaches, and to roughly two minutes once the race is off. A stale quote is served, not withheld: its live fields are dropped and its historical ones stand. Always present.
    runners[].prices[].opennumber | omitted
    Opening win price. Omitted where the bookmaker publishes none and the market opened before we first saw it.
    runners[].prices[].closenumber | omitted
    The last win price this bookmaker offered. Tracks win 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 | omitted
    Longest (highest) win price seen — the “top fluc”. Taken over every point known, including any dropped from movements by its length cap.
    runners[].prices[].lownumber | omitted
    Shortest (lowest) win price seen.
    runners[].prices[].startingPricenumber | omitted
    This bookmaker's settled starting price — what they settled win bets at. Not the official figure of the same name on Results: these are per bookmaker, they disagree with each other, and they disagree with the official one. Present only once a race has settled, and only for books that publish it.
    runners[].prices[].movementsarray
    Win-price movements, oldest first, most recent 20. Where a bookmaker publishes its own history this is that history, reaching back to the market open — before we began polling. Otherwise it is what we observed, recorded only when the price actually changed, so consecutive entries always differ. An empty array means no movement is known, never that the price has not moved.
    runners[].prices[].movements[].pricenumber
    Win price at this point, as decimal odds.
    runners[].prices[].movements[].atstring
    When the bookmaker published this price (UTC, ISO 8601). Their timestamp, not our capture time, so points from books polled at different rates stay correctly ordered against each other and against the race start.
    runners[].prices[].capturedAtstring
    When the source observed this quote (UTC). Per bookmaker, because books update independently — read it with stale 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.

    200
    Response
    json
    {
      "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 jumpQuote stays fresh for
    Under 30 min2 min
    Under 90 min3 min
    Under 3 h6 min
    Under 6 h9 min
    Under 24 h15 min
    Over 24 h30 min
    Race is off, or start time unknown2 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[].winprices[].startingPriceplacings[].startingPrice
    Where/v1/prices and /v1/resultsAlongside it, on the same quote/v1/results only
    What it isWhat this book is offering right nowWhat this book settled its own win bets atThe racing body's official settled price
    WhenBefore the jump, moving continuouslyAfter the race settles, fixedAfter the race, fixed forever
    Per bookmakerYes — one entry per book and marketYes — books disagree with each otherNo — one figure for the runner
    TierPremium and EnterprisePremium and EnterprisePro and above
    CoverageWhatever the books are pricingOnly books that publish onePartial 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

    403Forbidden
    Your tier does not include real-time data. Free and Pro keys cannot reach this endpoint.
    404Not Found
    No such race. Note that a race with no market yet returns 200 with empty arrays, not 404.
    429Too Many Requests
    Daily quota or burst limit exceeded. See Rate Limiting.