Krok Odds

Racing — Results & international

The results & international half of the Racing reference — every route with its parameters, credit cost and a worked example.

5 of 23 endpoints · all categories · get a free API key

GET/api/v1/racing/internationalAPI plan

UK/IE and other international racecards and results, from the licensed international racecards / results store.

Built on the shared the standard v1 route factory() factory (v1-collection-route.ts) with feature: 'historical' — this is the ONLY racing endpoint that is NOT free-tier (historical/bulk_export are the only two paid-only features). Empty result set returns 200 + `data: []` with the credit refunded; the internal request log labels that case 204, but the wire status is always 200. Runners expose the same derived price fields as /racing/meetings (`best_win`, `best_win_bookmaker`, `fixed_win`, `bookmakers_priced`) **only when the row carries an `odds[]` array**; The Racing API racecards/results rows carry no `odds[]`, so on today's data those four keys are absent — the documented example above shows the fields that are actually present. `type=results` rows swap in the result fields (`position`, `sp`, `sp_dec`, `bsp`, `time`, `btn`, `or`, `rpr`, `tsr`, `prize`, `ovr_btn`) and add `comments`, `non_runners`, `tote_win`, `tote_pl`, `winning_time_detail`, `distance_m`, `distance_y`. **Not served on this feed:** there is no runner `name` (the horse is `horse`) and no `or_rating` (the rating is `ofr` on racecards / `or` on results) — earlier drafts used those two names but the route never emits them. **Not yet available / always null:** `margin` is never served here; `comments`, `non_runners`, `tote_win`, `tote_pl` and `winning_time_detail` are carried but frequently `null`; there are no dividends and no starting-price/tote-dividend fields on this feed at all.

Query parameters
NameTypeRequiredDefaultDescription
typestring enumNoracecardsOne of racecards | results — which collection to query.
datestring (YYYY-MM-DD)No–Exact-match filter on race date.
limitintegerNo100Max rows returned; capped at 50 (free) / 200 (api). Note: gated to api-tier anyway via the 'historical' feature.
Example request
curl \
  -H "X-API-Key: YOUR_KEY" \
  "https://krokodds.com.au/api/v1/racing/international?type=racecards"
Example response
{
  "success": true,
  "data": [
    {
      "id": "intl_gb_20260903_ascot_1535",
      "date": "2026-09-03",
      "sport": "horse_racing",
      "type": "racecards",
      "region": "GB",
      "country": "GB",
      "venue": "Ascot",
      "venue_slug": "ascot",
      "race_id": "912345",
      "race_number": 3,
      "race_name": "3:35 Ascot - Handicap",
      "race_class": "Class 4",
      "race_status": "racecard",
      "off_dt": "2026-09-03T15:35:00Z",
      "off_time": "15:35",
      "distance_f": 8,
      "going": "Good",
      "surface": "Turf",
      "field_size": 8,
      "runners": [
        {
          "number": 1,
          "horse": "Northern Lad",
          "horse_id": "123456",
          "draw": 3,
          "jockey": "R Moore",
          "jockey_id": "987",
          "trainer": "C Appleby",
          "trainer_id": "42",
          "age": 4,
          "sex": "Gelding",
          "weight_lbs": 126,
          "ofr": 78,
          "form": "2213",
          "last_run": "2026-08-10"
        }
      ]
    }
  ],
  "meta": {
    "count": 1,
    "tier": "api",
    "limit": 100,
    "requested_limit": 100,
    "type": "racecards",
    "date": null,
    "timestamp": "2026-09-03T04:00:00.000Z",
    "rate_limit": {
      "limit": 200,
      "remaining": 1998,
      "reset": "2026-10-01"
    }
  }
}
GET/api/v1/racing/international/resultsAPI plan

Race results from the international (UK/IE and wider) store, for one date. The same handler, auth, tier and credit cost as /api/v1/racing/international with type=results fixed, so callers do not have to know the parameter shape.

Convenience sub-route: rewrites the URL to /api/v1/racing/international, injects type=results and forwards with the original headers, so every other query option and the whole response contract belong to the parent. Verified interchangeable with the parent query form - the two responses match on sha256 once meta.timestamp is normalised (measured, limit=2). Gated on the 'historical' feature, so api tier only, and priced as the parent is: x-credits-cost: 2 on the wire (measured). The default request is heavy - 100 rows / 1,031,665 bytes with meta.truncated: true and no total - so pass `date` and a low `limit`. Rows carry an internal _raw passthrough field alongside the parsed columns; it is omitted from the example below, which is abridged to one race and two runners. Every other value in the example was served by production.

Query parameters
NameTypeRequiredDefaultDescription
datestring (YYYY-MM-DD)No–Exact-match filter on race date.
limitintegerNo100Max rows returned; capped at 50 (free) / 200 (api). Note: gated to api-tier anyway via the 'historical' feature.
typestring enumNoresultsFixed to results by this path - it is set after the URL is rewritten, so a caller-supplied ?type= is overwritten.
Example request
curl \
  -H "X-API-Key: YOUR_KEY" \
  "https://krokodds.com.au/api/v1/racing/international/results?date=2026-09-01"
Example response
{
  "success": true,
  "data": [
    {
      "id": "2026-10-19_wolverhampton-aw_1",
      "date": "2026-10-19",
      "type": "Flat",
      "going": "Standard",
      "sport": "racing",
      "venue": "Wolverhampton (AW)",
      "off_dt": "2026-10-19T16:45:00+01:00",
      "region": "GB",
      "country": "UK",
      "pattern": "",
      "race_id": "rac_32301595443",
      "runners": [
        {
          "or": "56",
          "sp": "",
          "age": "2",
          "bsp": null,
          "btn": "",
          "dam": "Silver Games (IRE)",
          "rpr": "",
          "sex": "F",
          "tsr": "",
          "draw": "1",
          "sire": "Bated Breath (GB)",
          "time": "",
          "horse": "Miss You Nights (GB)",
          "prize": "",
          "dam_id": "dam_4968166",
          "jockey": "Clifford Lee",
          "number": "11",
          "sp_dec": "",
          "weight": "9-0",
          "damsire": "Verglas",
          "ovr_btn": "",
          "sire_id": "sir_5178992",
          "trainer": "Jack Channon",
          "headgear": "",
          "horse_id": "hrs_66527342",
          "position": "1",
          "jockey_id": "jky_280440",
          "damsire_id": "dsi_3126837",
          "trainer_id": "trn_361449",
          "weight_lbs": "126"
        },
        {
          "or": "65",
          "sp": "",
          "age": "2",
          "bsp": null,
          "btn": "",
          "dam": "No More Regrets (IRE)",
          "rpr": "",
          "sex": "F",
          "tsr": "",
          "draw": "7",
          "sire": "Space Traveller (GB)",
          "time": "",
          "horse": "Global Success (IRE)",
          "prize": "",
          "dam_id": "dam_13988023",
          "jockey": "Oisin Murphy",
          "number": "2",
          "sp_dec": "",
          "weight": "9-9",
          "damsire": "Kodiac",
          "ovr_btn": "",
          "sire_id": "sir_13788215",
          "trainer": "J. S. Moore",
          "headgear": "b",
          "horse_id": "hrs_63741895",
          "position": "2",
          "jockey_id": "jky_278184",
          "damsire_id": "dsi_4080139",
          "trainer_id": "trn_46089",
          "weight_lbs": "135"
        }
      ],
      "surface": "AW",
      "tote_pl": null,
      "age_band": "2yo",
      "comments": null,
      "off_time": "4:45",
      "tote_win": null,
      "race_name": "Download The At The Races App Nursery Handicap Stakes (Div II)",
      "distance_f": "7f",
      "distance_m": "",
      "distance_y": "",
      "race_class": "Class 6",
      "venue_slug": "wolverhampton-aw",
      "non_runners": null,
      "race_number": 1,
      "rating_band": "0-65",
      "sex_restriction": "",
      "winning_time_detail": null
    }
  ],
  "meta": {
    "count": 1,
    "truncated": true,
    "tier": "api",
    "limit": 2,
    "requested_limit": 2,
    "type": "results",
    "date": null,
    "timestamp": "2026-10-22T05:34:14.020Z",
    "rate_limit": {
      "limit": 200,
      "remaining": 439252,
      "reset": "2026-11-01T00:00:00.000Z"
    }
  }
}
GET/api/v1/racing/predictionsFree tier

AI-powered win/place probability predictions for AU/NZ races, from the racing predictions model.

Live (served 2026-10-13): per-race model ratings expanded to one row per runner. Each ratable horse in a modelled race becomes its own row — win_probability from the model's per-runner win probability, the model rank the horse's place INSIDE its own race (winProb desc, slug asc breaks ties), code derived from the race type (T/H/G → thoroughbred/harness/greyhound). Reads the picks model store mirror, bounded by the date prefix on data.date (an ISO timestamp, so a lexicographic range is used rather than equality). Cross-model consensus is merged when a matching race exists, adding ensemble_agreement_count and ensemble_pick. place_probability is null — the model publishes a win probability only, not a place model. Built on the standard v1 route factory() factory. Sorted by the model rank asc then win_probability desc. Fetches limit*4 rows (limit*8 when venue/code/horse filters are set, JS post-filter), capped at the requested limit on return. An unmodelled day still answers meta.status='not_yet_populated'.

Query parameters
NameTypeRequiredDefaultDescription
datestring (YYYY-MM-DD)NotodayRace date to filter.
venuestringNo–Case-insensitive substring match on venue.
codestring enumNo–Racing code: thoroughbred | harness | greyhound.
horsestringNo–Case-insensitive substring match on horse name.
limitintegerNo200Max rows returned; capped at 200 (free) / 1000 (api).
Example request
curl \
  -H "X-API-Key: YOUR_KEY" \
  "https://krokodds.com.au/api/v1/racing/predictions?date=today"
Example response
{
  "success": true,
  "data": [
    {
      "id": "2026-09-03_flemington_T_5_admiral-vos",
      "date": "2026-09-03",
      "venue": "Flemington",
      "race_no": 5,
      "horse_name": "Zaaki",
      "win_probability": 0.31,
      "place_probability": 0.58,
      "model_rank": 1,
      "code": "thoroughbred"
    }
  ],
  "meta": {
    "count": 1,
    "tier": "free",
    "limit": 200,
    "requested_limit": 200,
    "date": "2026-09-03",
    "venue": null,
    "code": null,
    "horse": null,
    "source": "Krok Odds Predictions",
    "timestamp": "2026-09-03T04:00:00.000Z",
    "rate_limit": {
      "limit": 200,
      "remaining": 942,
      "reset": "2026-10-01"
    }
  }
}
GET/api/v1/racing/resultsFree tier

Archive query across settled race results (exchange-SP-based, lighter fields than the date-specific endpoint) with filters by track, race type, runner and date range.

Tier detail: free (date-range filters `since`/`until` require api tier via the 'historical' feature)

Explicit the credit cost=5 archive endpoint; refunds the full credit cost when the result set is empty. Setting `since`/`until` on a free-tier key returns a 402 tier-gate error for the 'historical' feature BEFORE the per-key clamp runs. `track`/`runner_slug`/`race_name` filters are applied client-side after an over-fetch (up to 2000 rows). `data_quality.results_source` is a NEUTRAL provenance class (primary|secondary|exchange, or null) — it never names the feed. Derived by applying provenanceClass() to the source mix measured 2026-10-01 across a full 2026-09-30 day (200 rows: 193 secondary-token, 7 exchange-token). `has_result` is the reliable settled flag (`status` is free-text: the same day returned both `resulted` and `finished`). `books` counts per-book quote rows and is 0 on most settled races (198/200 that day), so treat it as "quotes captured", not "quality score". `last_price_update` is the newest quote timestamp and equals `ingested_at` when the result and the quotes arrived in the same write. Rows whose source is the fast path can carry a blank `track`/`race_key_v2` (6/200 that day) — fall back to `id` for those. Always-null / not yet available on today's data: `starting_price` (populated on 66.2% of the trailing-7-day runner rows, but most values are the last pre-jump market price rather than a settled SP), `tote_dividend` (15.5%) and `margin` (1.7%) - each is `null` when nothing has been reconciled, never guessed, and its `*_source` is `null` too. Measured 2026-10-14. `meeting_id` is the meeting's own identity, served beside `meeting_key` (the Sydney-day key the row is filed under) — the two differ for a UK/IE card that runs after midnight in Sydney. `is_final` collapses `status` to a boolean settled flag: `true` for `resulted`/`finished`/`Final`, `false` for `pending`/`abandoned`/`closed`. `meta.data_note` is always present — `null` when there is nothing to report, else a sentence naming the pending races or the `scheduled_time` fill count. All six (`meeting_id`, `meeting_key`, `local_date`, `jump_time_utc`, `meta.data_note`, `is_final`) verified served on prod 2026-10-23.

Query parameters
NameTypeRequiredDefaultDescription
race_typestring enumNo–One of T | H | G.
track_slugstringNo–Exact match on venue slug.
trackstringNo–Case-insensitive substring match on track name.
race_namestringNo–Case-insensitive substring match on race name.
runner_slugstringNo–Exact match on a runner's slug within the race.
sincestring (YYYY-MM-DD)No–Results on/after this date. Requires 'historical' feature (api tier); also clamped per-key by verification.historicalDays.
untilstring (YYYY-MM-DD)No–Results on/before this date. Requires 'historical' feature (api tier).
limitintegerNo50Max rows returned; capped at 50 (free) / 2000 (api).
Example request
curl \
  -H "X-API-Key: YOUR_KEY" \
  "https://krokodds.com.au/api/v1/racing/results?race_type=value"
Example response
{
  "success": true,
  "data": [
    {
      "id": "2026-09-30_laval_H_1",
      "race_key_v2": "2026-09-30_laval_H_1",
      "meeting_id": "2026-09-30_laval",
      "date": "2026-09-30",
      "track": "Laval",
      "track_slug": "laval",
      "state": null,
      "race_no": 1,
      "race_type": "H",
      "racing_type": "H",
      "distance_m": 2850,
      "race_name": "GRAND PRIX ANJOU-MAINE SS",
      "scheduled_time": "2026-09-30 11:55:00",
      "jump_time_utc": "2026-09-30T11:55:00Z",
      "local_date": "2026-09-30",
      "actual_off_time": null,
      "win_market_id": null,
      "place_market_id": null,
      "winning_time": null,
      "mile_rate": null,
      "status": "finished",
      "is_final": true,
      "data_quality": {
        "books": 0,
        "has_result": true,
        "results_source": "secondary",
        "last_price_update": "2026-10-01T00:00:43.347Z"
      },
      "winner": {
        "tab_number": 5,
        "name": "Jilord Viva",
        "slug": "jilord-viva",
        "jockey": "E Raffin",
        "trainer": "J Raffestin",
        "win_bsp": null
      },
      "runners": [
        {
          "tab_number": 5,
          "name": "Jilord Viva",
          "slug": "jilord-viva",
          "is_scratched": false,
          "finish_position": 1,
          "margin": null,
          "win_result": "Winner",
          "win_bsp": null,
          "place_result": "Placed",
          "place_bsp": null,
          "jockey": "E Raffin",
          "trainer": "J Raffestin",
          "barrier": null,
          "weight": null,
          "rpr": null,
          "or_rating": null,
          "age": null,
          "ts": null,
          "mr": null,
          "margin_source": null,
          "finish_position_source": null,
          "starting_price": null,
          "starting_price_source": null,
          "tote_dividend": null,
          "tote_dividend_source": null
        },
        {
          "tab_number": 9,
          "name": "Ibiscus Man",
          "slug": "ibiscus-man",
          "is_scratched": false,
          "finish_position": 2,
          "margin": null,
          "win_result": null,
          "win_bsp": null,
          "place_result": "Placed",
          "place_bsp": null,
          "jockey": "M Mottier",
          "trainer": "R Larue",
          "barrier": null,
          "weight": null,
          "rpr": null,
          "or_rating": null,
          "age": null,
          "ts": null,
          "mr": null
        }
      ],
      "going": null,
      "race_class": null,
      "ingested_at": "2026-10-01T00:00:43.347Z",
      "non_tab": null
    }
  ],
  "meta": {
    "count": 1,
    "tier": "free",
    "limit": 50,
    "requested_limit": 50,
    "race_type": null,
    "track_slug": null,
    "race_name": null,
    "runner_slug": null,
    "since": null,
    "until": null,
    "data_note": "This endpoint returns exchange-SP-based results (limited fields). For richer results including margins, times and full connections, use the date-specific endpoint /api/v1/racing/results/{date}.",
    "timestamp": "2026-09-03T04:00:00.000Z",
    "rate_limit": {
      "limit": 50,
      "remaining": 937,
      "reset": "2026-10-01"
    }
  }
}
GET/api/v1/racing/results/{date}Free tier

Full settled race results for every meeting on a single date — finishing order, margins, times, BSP, jockey/trainer — merged with national-tote pools and dividends.

Tier detail: free (subject to per-key historicalDays clamp)

Same `{date}_{venueSlug}_{raceNo}` race_key/meeting_key join keys as /racing/meetings. Sorts results by raceNo in JS (NOT in SQL) to avoid a full seq-scan on the unindexed data->>raceNo JSONB path — deliberate perf workaround. Cache 300s (respects the engineering notes floor). `source_id` is a deterministic 16-char hex id, `sha256(race_key_v2).slice(0,16)`, so it is stable across runs and restarts without a DB sequence — verified on prod 2026-10-01: `2026-09-30_addington_H_1` → `eda51f0bc5a4ffd2`. The delivered `race_key`/`race_key_v2` carry the meeting type (`{date}_{venueSlug}_{type}_{raceNo}`), and `first_seen_at` is when the race first appeared in the results table (distinct from `ingested_at`, the last write). `pools`, `dividends` and `exotic_pools` are empty arrays where national-tote data does not exist for the meeting (all NZ harness that day) — an empty array is a real "no tote data", never a partial omission. NZ results are thinner than AU: `margin`, `time` and settled SP and dividends are frequently absent, and `pools`/`dividends`/`exotic_pools` come back as empty arrays where no national-tote data exists for the meeting. `meeting_id` is the meeting's own identity, served beside `meeting_key` (unchanged: the Sydney-day key the row is filed under) — the two differ for a UK/IE card that runs after midnight in Sydney, and `race_key`/`race_key_v2`/`meeting_key`/`meeting_id` all follow from the row's stored id. `is_final` collapses `status` to a boolean settled flag (`true` for `resulted`/`finished`/`Final`). `meta.data_note` is always present — `null` when there is nothing to report, else a sentence naming the pending races, the `scheduled_time` fill count (`scheduled_time populated for X of Y rows; remaining rows have no jump time on record.`) or a short board read. All six (`meeting_id`, `meeting_key`, `local_date`, `jump_time_utc`, `meta.data_note`, `is_final`) verified served on prod 2026-10-23.

Path parameters
NameTypeRequiredDefaultDescription
datestring (YYYY-MM-DD)No–Meeting date to fetch results for; 400 if not a valid YYYY-MM-DD string.
Query parameters
NameTypeRequiredDefaultDescription
venuestringNo–Case-insensitive substring match on track name/slug. Alias: `track`.
trackstringNo–Alias for venue.
Example request
curl \
  -H "X-API-Key: YOUR_KEY" \
  "https://krokodds.com.au/api/v1/racing/results/2026-09-01?venue=Randwick"
Example response
{
  "success": true,
  "data": [
    {
      "id": "2026-09-30_addington_H_1",
      "race_key": "2026-09-30_addington_H_1",
      "meeting_key": "2026-09-30_addington",
      "meeting_id": "2026-09-30_addington",
      "date": "2026-09-30",
      "track": "Addington",
      "track_slug": "addington",
      "state": null,
      "race_no": 1,
      "race_type": "H",
      "distance_m": 2000,
      "race_name": "WOODLANDS STUD BREED TO SUCCEED TROT SS",
      "scheduled_time": "2026-09-30 03:55:00",
      "jump_time_utc": "2026-09-30T03:55:00Z",
      "local_date": "2026-09-30",
      "actual_off_time": null,
      "winning_time": null,
      "going": null,
      "race_class": null,
      "status": "finished",
      "is_final": true,
      "winner": {
        "tab_number": 4,
        "name": "Tamsin Trouble",
        "slug": "tamsin-trouble",
        "jockey": "S O'Reilly",
        "trainer": "S Sutherland",
        "win_bsp": null
      },
      "pools": [],
      "dividends": [],
      "runners": [
        {
          "tab_number": 4,
          "name": "Tamsin Trouble",
          "slug": "tamsin-trouble",
          "finish_position": 1,
          "is_scratched": false,
          "margin": null,
          "time": null,
          "win_result": "Winner",
          "win_bsp": null,
          "place_result": "Placed",
          "place_bsp": null,
          "jockey": "S O'Reilly",
          "trainer": "S Sutherland",
          "barrier": 4,
          "weight": 0
        },
        {
          "tab_number": 6,
          "name": "Deltoro",
          "slug": "deltoro",
          "finish_position": 2,
          "is_scratched": false,
          "margin": null,
          "time": null,
          "win_result": null,
          "win_bsp": null,
          "place_result": "Placed",
          "place_bsp": null,
          "jockey": "R Close",
          "trainer": "J Aarts",
          "barrier": 6,
          "weight": 0
        }
      ],
      "exotic_pools": [],
      "ingested_at": "2026-10-01T00:00:36.359Z",
      "first_seen_at": "2026-09-30T04:20:09.807Z",
      "race_key_v2": "2026-09-30_addington_H_1",
      "source_id": "eda51f0bc5a4ffd2",
      "non_tab": null
    }
  ],
  "meta": {
    "count": 1,
    "date": "2026-09-03",
    "venue": null,
    "data_note": null,
    "tier": "free",
    "timestamp": "2026-09-03T05:30:00.000Z",
    "rate_limit": {
      "limit": 50,
      "remaining": 942,
      "reset": "2026-10-01"
    }
  }
}

Other categories

Gameday8 endpoints

Per-event head-to-head odds, alternate lines, best prices across books, player props, live state and AI-generated summaries.

Opportunities7 endpoints

Krok's built-in scanners — arbitrage, positive EV, middles, low-holds, player props and same-game multi (SGM) picks, already devigged against a blended sharp baseline.

Odds Feed16 endpoints

Raw per-bookmaker prices as scraped, before any analysis layer. `/scraped-odds/*` paths are aliases of the equivalent `/odds-feed/*` route.

Sports8 endpoints

Sport reference data, team form guides, league standings, injury reports and weather-impact analysis.

Tips & Predictions4 endpoints

AI-generated betting tips with confidence scores and historical accuracy tracking.

Insights1 endpoint

Ranked leaderboards built on top of the stat feeds — per-sport player leaders for a chosen stat, season and view.

Tennis5 endpoints

ATP/WTA rankings, upcoming fixtures, match results, Elo ratings and surface-specific stats.

Historical7 endpoints

Deep archives — player and team game logs, shot charts, AFL/NRL historical data. Paid API plan only.

Reference7 endpoints

Static lookup data for building your own UI — players, venues, headshots, team logos and sport/league metadata.

Other Sports22 endpoints

Cricket, cycling, esports, Formula 1, golf, MMA, boxing, NFL and soccer coverage beyond the AU staples.

Infrastructure15 endpoints

Account and billing endpoints, bulk export, SSE streaming, webhook management and API health.

Additional Data19 endpoints

Odds history, steam-move detection, player prop stats/results, injuries, weather and other supporting feeds.

Prediction Markets3 endpoints

Polymarket and Kalshi sentiment plus a universal prediction-market catalogue and resolutions feed.