18+ Only  |  Gambling can be addictive — please gamble responsibly  |  Gambling Help: 1800 858 858  |  GambleAware

API reference / Racing

GET /v1/racing/consensus

The market consensus on one race: margin-free probability per runner

The market's consensus on one race while its market is open: for every runner, the implied probability after each book's margin is removed and the books are combined, the price that probability corresponds to, the median, mean, best and worst price, and how far the best available price sits from the consensus. The inputs are printed beside the answer — which books were counted, which were left out and why. **Naming the race.** `race_id` from /v1/racing/next-to-go or /v1/racing/events is exact. Failing that, `venue` + `meeting_date` (the AET meeting date) + `race_number`; two races sharing those three answer 409 `race_ambiguous` and cost nothing. **How the number is made.** Each included book's win prices on the field are turned into implied probabilities (1 / price) and its margin removed — `power` (default) raises them to the exponent that makes them sum to one, `multiplicative` divides them by their sum — so every book contributes a field that sums to 1. The consensus for a runner is the MEDIAN of those per-book probabilities, and the medians are scaled once so the field sums to 1 again; `market.consensus_sum_before_normalisation` shows how far from 1 they were before scaling. Every included book counts once. Nothing is weighted by an opinion of which book is sharp. **Which books count.** A book is included when it is visible to you (the exchange and the non-Australian reference books are withheld here as on every racing endpoint), its quote is inside its refresh-tier threshold (the same per-book rule next-to-go publishes: 120 s inside three hours of the jump, 1800 s on the morning card, with Betr and NextBet on their own horizons), and it prices the complete field — `market.field_size` is the number of runners a complete-field book quotes, and a book pricing fewer is `partial_field`. Placeholder prices are dropped quote by quote on the rule /v1/racing/best-odds applies (a long price at least four times the median of three or more other books) and listed in `excluded_quotes`. Scratched runners are dropped and listed under `scratchings`. **Reading `consensus_prob`.** It is the market's implied probability after margin, not a probability of winning and not a fair price. `best_vs_consensus_pct` is best_price / consensus_price − 1: negative is normal, because every quoted price carries margin and the best quote usually sits below the panel's margin-free price (about −20 % on a 130 % greyhound market); it turns positive only when one book prices a runner above the panel's margin-free view. It says where the best quote sits, nothing more. The fields a reader would use to judge how much to trust a figure — `books`, the spread, the ages, `secs_to_jump` — are separate on purpose; there is no single confidence score. **Live only.** The consensus is read from the open market. A race that has left the live board answers 404; the consensus at the close will arrive as columns on the permanent archive, where it belongs. A race inside the ten minutes after its jump is still served (`status: jumped`) while the books drop off, with a note. Every plan, Free included. 3 credits, charged after the race is found; a race we cannot find, an unparseable parameter and an unknown bookmaker key are free.

Requires X-API-Key 3 credits

Request

curl 'https://api.puntersedge.online/v1/racing/consensus' \
  -H 'X-API-Key: YOUR_KEY'

Parameters

NameIn TypeRequired Description
race_id query string no The race_id from /v1/racing/next-to-go or /events. The primary form; use venue + meeting_date + race_number only when you do not hold it.
venue query string no Venue name as the feeds spell it (case-insensitive). Needs meeting_date and race_number.
meeting_date query string no AET meeting date, YYYY-MM-DD.
race_number query string no
method query string no How each book's margin is removed before the books are combined. `power` (default) raises the implied probabilities to the exponent that makes them sum to one, which takes more margin off the longshots and is calibrated across the price range. `multiplicative` divides them by their sum and reproduces from the published overround.
bookmakers query string no Comma-separated bookmaker keys to build the consensus from. Omit for every visible book.
min_books query integer no Included books a runner needs before it carries a consensus probability.
include_stale query boolean no Count books whose quote is older than their refresh-tier threshold. Off by default; they are listed under books_excluded with reason stale.

Responses

Status codes: 200, 401, 402, 404, 409, 422, 429, 500. Response bodies are JSON; the full schema is in /openapi.json.

Example response

200 application/json — this call costs 3 credits. Field names and types are as the API returns them; values are a real sample, trimmed to a few items.

{
  "race": {
    "race_id": "2e33fc28-13a5-421b-a6f3-6f99283c5cb6",
    "venue": "Menangle",
    "race_number": 2,
    "category": "harness",
    "country": "AU",
    "start_time": "2026-10-06T01:43:00Z",
    "secs_to_jump": 771,
    "refresh_tier": "live",
    "status": "upcoming",
    "as_of": "2026-10-06T01:30:08Z"
  },
  "method": {
    "normalisation": "power",
    "aggregation": "median_probability",
    "renormalised": true,
    "stale_rule": "refresh_tier",
    "field_rule": "complete_field",
    "min_books": 2,
    "include_stale": false
  },
  "books_quoting": 13,
  "books_in_consensus": 13,
  "books_excluded": [],
  "bookmakers": [
    {
      "bookmaker": "betdeluxe",
      "runners_priced": 10,
      "overround_pct": 135.08,
      "age_seconds": 35,
      "last_update": "2026-10-06T01:29:32.828100Z",
      "refresh_tier": "live",
      "stale": false,
      "included": true,
      "reason": null
    },
    {
      "bookmaker": "betgold",
      "runners_priced": 10,
      "overround_pct": 143.72,
      "age_seconds": 35,
      "last_update": "2026-10-06T01:29:33.273348Z",
      "refresh_tier": "live",
      "stale": false,
      "included": true,
      "reason": null
    },
    {
      "bookmaker": "betr_au",
      "runners_priced": 10,
      "overround_pct": 139.49,
      "age_seconds": 48,
      "last_update": "2026-10-06T01:29:20.528874Z",
      "refresh_tier": "live",
      "stale": false,
      "included": true,
      "reason": null
    },
    {
      "bookmaker": "betright",
      "runners_priced": 10,
      "overround_pct": 129.01,
      "age_seconds": 37,
      "last_update": "2026-10-06T01:29:31.051977Z",
      "refresh_tier": "live",
      "stale": false,
      "included": true,
      "reason": null
    }
  ],
  "market": {
    "median_overround_pct": 137.08,
    "best_price_market_pct": 124.44,
    "median_runner_spread_pct": 20.33,
    "consensus_sum_before_normalisation": 0.9933,
    "field_size": 10
  },
  "runners": [
    {
      "number": 10,
      "name": "Alpha Stride",
      "runner_ref": null,
      "horse_ref": null,
      "scratched": false,
      "books": 13,
      "consensus_prob": 0.2652,
      "consensus_price": 3.77,
      "median_price": 3.1,
      "mean_price": 3.14,
      "best_price": 3.6,
      "best_bookmaker": "playup",
      "worst_price": 2.9,
      "price_spread_pct": 24.14,
      "best_vs_consensus_pct": -4.54,
      "freshest_age_seconds": 32,
      "oldest_age_seconds": 168,
      "quotes": [
        {
          "bookmaker": "playup",
          "price": 3.6,
          "no_vig_prob": 0.2213,
          "age_seconds": 168
        },
        {
          "bookmaker": "ladbrokes_au",
          "price": 3.3,
          "no_vig_prob": 0.2508,
          "age_seconds": 36
        },
        {
          "bookmaker": "neds",
          "price": 3.3,
          "no_vig_prob": 0.2508,
          "age_seconds": 36
        }
      ]
    },
    {
      "number": 2,
      "name": "Call Me Jack",
      "runner_ref": null,
      "horse_ref": null,
      "scratched": false,
      "books": 13,
      "consensus_prob": 0.2021,
      "consensus_price": 4.95,
      "median_price": 3.8,
      "mean_price": 3.88,
      "best_price": 4.0,
      "best_bookmaker": "betright",
      "worst_price": 3.7,
      "price_spread_pct": 8.11,
      "best_vs_consensus_pct": -19.17,
      "freshest_age_seconds": 32,
      "oldest_age_seconds": 168,
      "quotes": [
        {
          "bookmaker": "betright",
          "price": 4.0,
          "no_vig_prob": 0.2053,
          "age_seconds": 37
        },
        {
          "bookmaker": "ladbrokes_au",
          "price": 4.0,
          "no_vig_prob": 0.2007,
          "age_seconds": 36
        },
        {
          "bookmaker": "neds",
          "price": 4.0,
          "no_vig_prob": 0.2007,
          "age_seconds": 36
        }
      ]
    },
    {
      "number": 1,
      "name": "Bertinelli",
      "runner_ref": null,
      "horse_ref": null,
      "scratched": false,
      "books": 13,
      "consensus_prob": 0.149,
      "consensus_price": 6.71,
      "median_price": 5.0,
      "mean_price": 5.04,
      "best_price": 5.5,
      "best_bookmaker": "betright",
      "worst_price": 5.0,
      "price_spread_pct": 10.0,
      "best_vs_consensus_pct": -18.08,
      "freshest_age_seconds": 32,
      "oldest_age_seconds": 168,
      "quotes": [
        {
          "bookmaker": "betright",
          "price": 5.5,
          "no_vig_prob": 0.1427,
          "age_seconds": 37
        },
        {
          "bookmaker": "betdeluxe",
          "price": 5.0,
          "no_vig_prob": 0.152,
          "age_seconds": 35
        },
        {
          "bookmaker": "betgold",
          "price": 5.0,
          "no_vig_prob": 0.1416,
          "age_seconds": 35
        }
      ]
    }
  ],
  "excluded_quotes": [],
  "scratchings": [],
  "note": null
}

Related endpoints

Try it against live data

The free tier needs no credit card, and the sandbox endpoints need no key at all.

Get a free API key Quickstart
This site contains wagering-related analysis and is intended for Australian users aged 18+. Gambling involves risk. Please gamble responsibly.