Infrastructure API endpoints
Account and billing endpoints, bulk export, SSE streaming, webhook management and API health.
10 of 15 endpoints · all categories · get a free API key
GET/api/v1/advanced-statsFree tier
Multiplexed advanced/derived stat archives across 14 sources (MLB Statcast, NHL/MoneyPuck skaters+goalies, NCAAB, soccer xG, NCAAF, NFL NGS/snaps/depth, NRL, AFL, tennis closing odds), selected via ?source=.
Tier detail: free (advanced_stats is in the free-tier feature set; both free and api tier keys can call it)
Built on the shared the standard v1 route factory factory. Cache TTL: free=3600s, api=1800s. Falls back to cold archive mirror only if the primary-store loader throws AND a mirror exists; the soccer advanced stats source has no cold-archive mirror, so it reads from the primary store. Empty result set returns HTTP 200 + `data: []` and refunds the credit.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| source | string | Yes | – | One of: mlb_advanced, nhl_skaters, ncaab_schools, soccer_xg, ncaaf_player_games, nfl_ngs, nfl_snaps, nfl_depth, soccer_fbref, nrl_player_games, afl_matches, tennis_closing_odds, nhl_moneypuck_skaters, nhl_moneypuck_goalies. 400 a 400 bad-request error if not in this list. |
| season | integer | No | – | Season/year filter (field name varies per source: 'season' or 'year'). Non-numeric value returns 400. |
| team | playerType | league | sportKey | opponent | flavor | round | teamNickname | playerSlug | team1 | team2 | venue | tour | surface | position | situation | compId | squad | string | No | – | Per-source in-memory filter, only the subset listed in that source's `extra` array is honored (e.g. mlb_advanced accepts team/playerType; nhl_moneypuck_skaters accepts team/position/playerSlug/situation, defaulting situation to 'all' when omitted). |
| limit | integer | No | 100 | Row cap. Free tier max 100, api tier max 500. |
curl \
-H "X-API-Key: YOUR_KEY" \
"https://krokodds.com.au/api/v1/advanced-stats?source=value"{
"success": true,
"data": [
{
"id": "abc123",
"season": 2026,
"team": "NYY",
"playerType": "batter",
"xwOBA": 0.361,
"barrelPct": 9.8
}
],
"meta": {
"count": 1,
"tier": "free",
"limit": 100,
"requested_limit": 100,
"source": "mlb_advanced",
"season": 2026,
"filters": {
"team": "nyy"
},
"timestamp": "2026-09-03T00:00:00.000Z",
"rate_limit": {
"limit": 100,
"remaining": 999,
"reset": "2026-10-01T00:00:00.000Z"
}
}
}GETPOST/api/v1/bets/trackFree tier
GET returns a user's tracked-bet history plus a computed P&L summary (by sport, by bookmaker, streaks, monthly breakdown); POST logs a new bet.
Tier detail: free (bet_tracking is in the free-tier feature set)
Requires a valid API key with the bet_tracking feature (via the standard auth guard) PLUS a dashboard-session-derived `x-user-id` request header — the API key alone is not sufficient, so this is a hybrid api_key+session auth route despite being under v1. Reads/writes the `bets` the primary store table directly (no secondary fallback in this file). POST returns 201 with the created row and its plaintext id (no secret hidden). GET response cached 60s via unstable_cache, keyed per user+filters.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| sport_key | string | No | – | Filter bets by sport. |
| status | string | No | – | Filter by bet status: pending | won | lost | void. |
| from | string (YYYY-MM-DD) | No | – | Start date filter (created_at >= from). |
| to | string (YYYY-MM-DD) | No | – | End date filter (created_at <= to 23:59:59). |
| limit | integer | No | 50 | Max results, clamped 1-200. |
| offset | integer | No | 0 | Pagination offset. |
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| event_id | string | Yes | – | Event identifier. |
| sport_key | string | Yes | – | Sport key. |
| market | string | No | "" | Market type, e.g. h2h, spread, total. |
| selection | string | No | – | What was backed. |
| odds | number | No | – | Decimal odds. |
| stake | number | No | – | Stake amount (AUD). potential_return is auto-computed as odds*stake when both are present. |
| status | string | No | pending | pending | won | lost | void. |
| commence_time | string (ISO) | No | – | Event start time; passed through into the stored `data` jsonb blob (not a dedicated column). |
curl \
-H "X-API-Key: YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"event_id":"evt_abc123","sport_key":"aussierules_afl","market":"\"\"","selection":"value","odds":"10","stake":"10","status":"pending","commence_time":"value"}' \
"https://krokodds.com.au/api/v1/bets/track?sport_key=aussierules_afl"{
"success": true,
"data": [
{
"id": "u1_1735689600_ab12cd",
"user_id": "u1",
"event_id": "evt123",
"sport_key": "basketball_nba",
"market": "h2h",
"selection": "Lakers",
"odds": 1.85,
"stake": 50,
"potential_return": 92.5,
"profit_loss": null,
"status": "pending",
"created_at": "2026-09-01T00:00:00.000Z"
}
],
"meta": {
"license": "krokodds-derived",
"summary": {
"total_bets": 12,
"pending": 2,
"won": 7,
"lost": 3,
"void": 0,
"total_stake": 500,
"total_return": 740,
"net_profit": 240,
"roi_pct": 48,
"win_rate": 70,
"by_sport": {
"basketball_nba": {
"bets": 5,
"stake": 250,
"profit": 60,
"roi": 24,
"win_rate": 66.67
}
},
"by_bookmaker": {
"sportsbet": {
"bets": 5,
"stake": 250,
"profit": 60,
"roi": 24,
"win_rate": 66.67
}
},
"streak": {
"current": 2,
"type": "win",
"best_win": 4,
"best_loss": 2
},
"monthly": {
"2026-08": {
"bets": 6,
"stake": 300,
"profit": 70,
"roi": 23.33
}
}
},
"pagination": {
"limit": 50,
"offset": 0
}
}
}GET/api/v1/bookmakersFree tier
Catalog of the AU-facing bookmakers KrokOdds supports — id, display name, region, supported flag, aggregator/exchange flags and bookmaker_type (fixed, tote, exchange or group). Group labels add a brands array naming the individual books inside the group. Prediction-market venues (Kalshi, Polymarket) are catalogued separately under the Prediction Markets group.
Tier detail: free (no tierAllowsFeature gate at all — any valid key of any tier can call it)
The catalog is complete in every response: the roster is never truncated, so meta.truncated is always false and there is no limit parameter — region and provider only narrow what was asked for. Every row carries id, name, region, supported, is_aggregator, is_exchange and bookmaker_type; rows with bookmaker_type "group" additionally carry brands (the individual books inside the group), and those group rows are supported:false. The response exposes only these public roster fields — the internal provider and keying fields used to build the catalog are not returned to callers. 24h Cache-Control (private, max-age=86400, stale-while-revalidate=172800) — deliberately 'private' not 'public' since a shared/CDN cache would leak the auth-gated response to keyless callers (a CDN keys on URL only and ignores X-API-Key). X-Krok-Version: 2 (higher than most other v1 routes, which are version 1). Prediction-market venues (Kalshi, Polymarket) are not bookmakers, so they are excluded from this catalog — see the Prediction Markets group for their contract and resolution feeds. Example captured 2026-10-22 with an api-tier key, so meta.tier and rate_limit.limit reflect api limits (free tier reports limit 100).
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| region | string | No | au | Region filter; only 'au' returns rows — anything else (non-empty) returns an empty array (forward-compat hook, AU is the only region currently supported). |
| provider | string | No | – | Narrows the catalog to books whose pricing provider matches, plus books shared by 'both'. The provider value itself is not part of the public row shape. |
| include_aggregators | boolean ("true"/"false" string) | No | false | When true, also lists the aggregator feeds (deduped clone lines, no arb value) as additional rows marked supported:false — 28 rows instead of 21 on 2026-10-22. |
| fields | string (comma-separated) | No | – | Field projection — returns only the named fields of each row. |
curl \
-H "X-API-Key: YOUR_KEY" \
"https://krokodds.com.au/api/v1/bookmakers?region=au"{
"success": true,
"data": [
{
"id": "sportsbet",
"name": "Sportsbet",
"region": "au",
"supported": true,
"is_aggregator": false,
"is_exchange": false,
"bookmaker_type": "fixed"
},
{
"id": "tab",
"name": "TAB",
"region": "au",
"supported": true,
"is_aggregator": false,
"is_exchange": false,
"bookmaker_type": "tote"
},
{
"id": "betfair_ex_au",
"name": "Betfair",
"region": "au",
"supported": true,
"is_aggregator": false,
"is_exchange": true,
"bookmaker_type": "exchange"
}
],
"meta": {
"count": 21,
"truncated": false,
"tier": "api",
"region": "au",
"provider": "all",
"include_aggregators": false,
"timestamp": "2026-10-22T04:10:50.862Z",
"rate_limit": {
"limit": 500,
"remaining": 439828,
"reset": "2026-11-01T00:00:00.000Z"
}
}
}GET/api/v1/closing-linesAPI plan
Cross-event aggregator of opening vs closing H2H odds (for CLV computation at scale) — flips the single-event odds-history ladder inside out into many events with opens/close/movement.
Tier detail: api (paid) — 'closing_lines' is NOT in the free-tier feature set
Hand-rolled route (not the standard v1 route factory) with its own three-link fallback chain: primary `odds_history` table (has opening_odds), then the pre-computed closing-line analysis (pre-computed, 30K+ rows), then the legacy `gameday_odds_history` shape. The sources use different field casing (commence_time vs commenceTime) which the route normalizes. Cache 150s free / 60s api. Distinct from the single-event ladder at /api/v1/odds-history?event_id=. A failed link is never served as a quiet board: each source whose read threw is named in `meta.failed_sources`; a partial failure serves the rows it has and flags itself `degraded`; every link in the chain failing (nothing readable at all) answers 503 + `Retry-After: 30` with `Cache-Control: no-store` instead of an empty 200, and a degraded read is never written to the cache, so it is retried on the next request rather than held for the TTL.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| sport_key | sport | string | No | – | Filter by sport. |
| since | string (date/ISO) | No | – | Clamped by clampHistoricalFrom() against the key's historicalDays window. |
| until | string (date/ISO) | No | – | Upper bound on commence_time. |
| only_closed | boolean ("true"/"false") | No | true | When true, only events whose commence_time has already passed are returned. |
| limit | integer | No | 50 | Free tier max 50, api tier max 2000. |
| cursor | string (opaque) | No | – | Keyset cursor encoding {v: commence_time ISO, id}. |
| fields | string (comma-separated) | No | – | Field projection. |
curl \
-H "X-API-Key: YOUR_KEY" \
"https://krokodds.com.au/api/v1/closing-lines?sport_key | sport=aussierules_afl"{
"success": true,
"data": [
{
"id": "evt-1",
"event_id": "evt-1",
"sport_key": "basketball_nba",
"home_team": "Lakers",
"away_team": "Celtics",
"commence_time": "2026-09-02T00:00:00.000Z",
"is_closed": true,
"opens": {
"home": 1.9,
"away": 1.95,
"opened_at_ms": null,
"home_implied_prob": 0.5263,
"away_implied_prob": 0.5128
},
"close": {
"home": 1.83,
"away": 2.05,
"home_book": "sportsbet",
"away_book": "sportsbet",
"captured_at_ms": null,
"home_implied_prob": 0.5464,
"away_implied_prob": 0.4878
},
"movement": {
"home_delta": -0.07,
"away_delta": 0.1,
"home_direction": "shorten",
"away_direction": "drift"
},
"snapshot_count": 0,
"updated_at": "2026-09-02T05:00:00.000Z"
}
],
"meta": {
"count": 1,
"tier": "free",
"limit": 50,
"requested_limit": 50,
"sport_key": "basketball_nba",
"since": null,
"until": null,
"only_closed": true,
"next_cursor": null,
"timestamp": "2026-09-03T00:00:00.000Z",
"rate_limit": {
"limit": 50,
"remaining": 999,
"reset": ""
}
}
}GET/api/v1/clv-archive/{eventId}API plan
Fetch the archived closing odds snapshot for a single event by path parameter.
Tier detail: api (paid) — 'closing_lines' is NOT in the free-tier feature set, the same gate as /api/v1/odds-feed/clv
API-key authenticated (X-API-Key header, apikey/api_key query param) on the 'closing_lines' feature with the credit cost 5 — the same gate as /api/v1/odds-feed/clv and /api/v1/closing-lines. It USED to call dashboard-session auth() (dashboard session cookie), so every API key got 401: measured 2026-10-15 in the documented-route sweep, this was the only route that 401'd on a valid key. Reads the primary store `clv_archive` table by doc id (a doc read); 404 if the event isn't archived, 400 if eventId is empty after trim. There is no secondary fallback in the current code.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| eventId | string (path param) | Yes | – | Dynamic route segment [eventId]. Accepts the bare hex event id (e467621918b702d29d87d82e2c464628) and the sport-key-prefixed form (basketball_nbl_e467621918b702d29d87d82e2c464628); a known <sport_key>_ prefix is stripped before the clv_archive lookup, so both forms resolve the same document. 400 if empty after trim. |
curl \
-H "X-API-Key: YOUR_KEY" \
"https://krokodds.com.au/api/v1/clv-archive/evt_abc123"{
"success": true,
"data": {
"event_id": "evt-1",
"sport": "basketball_nba",
"sport_title": "NBA",
"home_team": "Lakers",
"away_team": "Celtics",
"commence_time": "2026-09-02T00:00:00.000Z",
"bookmakers": [],
"archived_at": "2026-09-02T05:00:00.000Z",
"expire_at": null
}
}GET/api/v1/ev-hit-ratesFree tier
Historical hit-rate / calibration stats for +EV bets, aggregated by market category (total_bets, hit_count, hit_rate, avg_clv, avg_ev).
Tier detail: free (positive_ev is in the free-tier feature set)
Hand-rolled route with a THREE-tier fallback chain: the primary store (ev_hit_rates table) -> point-get on the secondary store the hit-rate store if category set -> cold archive mirror -> full full secondary-store scan (capped at min(limit,500)). Cache TTL 3600s free / 600s api. Per memory note 'ev-records-empty' class issues have hit similar tables historically — verify live data before citing exact numbers in docs.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| category | string | No | – | Market category id — when set, does a point-get by doc id (category) instead of a scan. |
| limit | integer | No | 100 | Free tier max 100, api tier max 5000. |
curl \
-H "X-API-Key: YOUR_KEY" \
"https://krokodds.com.au/api/v1/ev-hit-rates?category=value"{
"success": true,
"data": [
{
"category": "h2h",
"total_bets": 542,
"hit_count": 301,
"hit_rate": 0.5554,
"avg_clv": 1.8,
"avg_ev": 4.2,
"updated_at": "2026-09-01T00:00:00.000Z"
}
],
"meta": {
"count": 1,
"tier": "free",
"limit": 100,
"requested_limit": 100,
"category": null,
"timestamp": "2026-09-03T00:00:00.000Z",
"rate_limit": {
"limit": 100,
"remaining": 999,
"reset": ""
}
}
}GET/api/v1/exchangeFree tier
KrokOdds Exchange pricing suite multiplexed by ?source=: settled racing BSP, race results, sports match-odds, scorer props, and live pre-jump snapshots.
Tier detail: free (the exchange feature is in the free-tier feature set — the exchange filter in api-tier-gate.ts is now a no-op, exchange pricing is available on all tiers)
Built on the standard v1 route factory, primary store only (no cold archive). Cache TTL 600s free / 300s api. Underlying writers are the exchange-feed writers. Per the engineering notes, this exchange endpoint is distinct from the max-fidelity the odds archive/the racing odds archive firehose tables (migration 056) which are internal-only, not exposed via this API.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| source | string | Yes | – | One of: bsp, results, match_odds, scorer_props, snapshots. 400 if not in this list. |
| raceKey | string | No | – | Racing filter for bsp/results, keyed `${venueSlug}_${raceNumber}`. |
| venueSlug | marketType | string | No | – | Additional bsp filters (in-memory, post-fetch). |
| sport | string | No | – | Equality filter (indexed path) for match_odds/scorer_props/snapshots sources. |
| eventId | string | No | – | In-memory filter for match_odds/scorer_props/snapshots. |
| limit | integer | No | 100 | Free tier max 100, api tier max 500. |
curl \
-H "X-API-Key: YOUR_KEY" \
"https://krokodds.com.au/api/v1/exchange?source=value"{
"success": true,
"data": [
{
"id": "flem_r5",
"raceKey": "flemington_5",
"venueSlug": "flemington",
"marketType": "WIN",
"bsp": 4.6,
"runnerName": "Fast Horse"
}
],
"meta": {
"count": 1,
"tier": "free",
"limit": 100,
"requested_limit": 100,
"source": "bsp",
"filters": {
"raceKey": "flemington_5"
},
"timestamp": "2026-09-03T00:00:00.000Z",
"rate_limit": {
"limit": 100,
"remaining": 999,
"reset": ""
}
}
}GET/api/v1/extended-marketsFree tier
Derivative/extended odds markets excluded from the core h2h/spreads/totals feed — half/period/inning splits, soccer corners+cards, AFL/NRL winning_margin_bands, team_winning_margin, alt-totals — from our proprietary collection of AU bookmakers. Key market keys: winning_margin_bands (NRL 1-12/13+), team_winning_margin (Soccer Win By 2+/3+/4+), total_points_odd_even, ht_ft, highest_scoring_half.
Tier detail: free (advanced_stats is in the free-tier feature set, so free keys reach this data too — the licence boundary here is Terms 6.1, not the tier gate)
Every row carries `source` ('scraped') and `redistributable` (bool) — licensed aggregator rows are FORBIDDEN_RESALE tier (b) under Terms 6.1, internal-use-only, never redistributable; KrokOdds' own scrape is redistributable. The three core markets (h2h, spreads, totals) are deliberately excluded — those live at /api/v1/odds. `meta.provider` field is @deprecated in favor of per-event `source`. Cache 120s free / 60s api; the two source tiers are fetched via Promise.allSettled so one failing doesn't take the other down.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| sport_key | sport | string | No | – | Sport filter (used when event_id absent). |
| event_id | string | No | – | Point-read a single event across both tiers instead of a windowed scan. |
| market | string | No | – | Post-merge market-key filter, applied after both tiers are combined. |
| source | string | No | all | all | scraped. 'scraped' returns only the redistributable KrokOdds-owned tier. |
| limit | integer | No | 50 | Free tier max 50, api tier max 2000; applied per-tier before merge. |
curl \
-H "X-API-Key: YOUR_KEY" \
"https://krokodds.com.au/api/v1/extended-markets?sport_key | sport=aussierules_afl"{
"success": true,
"data": [
{
"event_id": "evt-1",
"event_key": null,
"sport_key": "soccer_epl",
"sport_title": "EPL",
"home_team": "Arsenal",
"away_team": "Chelsea",
"commence_time": "2026-09-05T00:00:00.000Z",
"bookmakers": [
{
"key": "sportsbet",
"title": "Sportsbet",
"last_update": "2026-09-03T00:00:00.000Z",
"markets": [
{
"key": "corners_over_under",
"single_sided": false,
"outcomes": [
{
"name": "Over",
"description": "9.5",
"price": 1.9,
"point": 9.5,
"bet_link": "https://..."
}
]
}
]
}
],
"source": "scraped",
"redistributable": true
}
],
"meta": {
"count": 1,
"tier": "free",
"limit": 50,
"requested_limit": 50,
"sport_key": "soccer_epl",
"event_id": null,
"market": null,
"source": "all",
"provider": "scraped",
"providers": [
"scraped"
],
"timestamp": "2026-09-03T00:00:00.000Z",
"rate_limit": {
"limit": 50,
"remaining": 999,
"reset": ""
}
}
}GET/api/v1/statusFree tier
Public health/sync-freshness check — reports whether the odds-sync writer is alive (its own heartbeat) plus the newest-row age per racing table.
Tier detail: none — the only public, unauthenticated /v1 endpoint
force-dynamic route, no API key check at all. Rate-limited as tier 'free' under identifier 'anonymous' (shared bucket across all anonymous callers). 'degraded' means the odds-sync WRITER has not stamped its heartbeat for 15 minutes: the probe reads stats/global_stats.data.lastUpdated, which /api/odds/sync rewrites every cycle (measured 478 fires/24 h, worst gap 6.2 min, so 15 min is 2.4× the worst observed gap). If the heartbeat cannot be read the route falls back to the newest arbs/snipes row with a 35-minute threshold — those are OUTCOME tables that only move when an arb changes (median gap 15 min over 72 h), so a 5-minute threshold reported 'degraded' on a healthy platform about two-thirds of the time. 'data_freshness' reports the newest-row age per racing table (odds-store / the race-card store / tote); each probe is bounded (24× that table's threshold, which keeps the partitioned odds-archive probe index-ordered instead of sorting every partition) and individually guarded: a failing probe reports status 'error' with a normalised code in the error field ('timeout' or 'query_failed'), never 'unavailable' (a probe we could not read is not a table with nothing in it), and it never takes the route down. A table inside its own threshold reads 'green'; one older than its threshold reads 'stale', keeping the real age in last_write_age_seconds; one that has never been written reads 'unavailable'. The three sync probes (the writer heartbeat, then the arbs/snipes outcome fallback) are guarded the same way: when none of them answers, data.status is 'unknown' and data.sync carries probe_failed: true with last_sync_seconds_ago null, instead of a 500 or a 'degraded' claim about a writer that could not be read. status is therefore one of 'operational', 'degraded' or 'unknown'. A failure to build the payload at all answers 503 with Retry-After, not 500. Cached 30s via getCached().
curl \
"https://krokodds.com.au/api/v1/status"{
"success": true,
"data": {
"status": "operational",
"sync": {
"healthy": true,
"last_sync_seconds_ago": 42,
"probe_failed": false
},
"data_freshness": {
"as_of": "2026-10-15T03:50:20.000Z",
"tables": {
"racing_odds": {
"last_write_age_seconds": 51,
"status": "green"
},
"racing_cards": {
"last_write_age_seconds": 6,
"status": "green"
},
"tote": {
"last_write_age_seconds": 209,
"status": "green"
}
}
},
"timestamp": "2026-10-15T03:50:20.000Z"
},
"meta": {
"truncated": false
}
}GET/api/v1/stream/opportunitiesFree tier
Server-Sent Events (SSE) stream of live opportunity deltas (arbs/snipes/middles/low_holds) — initial full snapshot per type, then add/remove/update deltas on a 5s poll, with 15s keepalive heartbeats.
Tier detail: free (streaming is in the free-tier feature set)
SSE, not a normal JSON envelope. Hard-caps the connection at 10 minutes wall-clock (the 10-minute stream cap) then sends a 'bye' event and closes — clients should reconnect via EventSource's built-in retry. limit is fixed per tier (free=100, api=500), not client-adjustable. Uses polling (not the secondary store’s live listeners) deliberately: Admin onSnapshot listeners can't be safely torn down across cold-start boundaries in the App Router serverless runtime. Rows older than 12h past commence_time (COMMENCE_CUTOFF_MS) are filtered out of every snapshot/delta, as are rows whose `price_observed_at` (price-read time) is older than 90 minutes, rows with no price-read stamp at all, or rows whose quoted odds are out of sane bounds (the price-freshness check/the odds sanity check — the same gate the /api/v1/opportunities/* REST routes apply). Reads the primary store (a primary-store doc read) when the primary-store flag, else the secondary store with server-side orderBy/where. A read that fails is never served as an empty board: if every requested type fails its first read the stream answers 503 + Retry-After (meta.failed_sources names them) instead of a 200 that streams nothing; a partial failure streams the healthy types and names the missing ones in a 'degraded' frame, with no empty snapshot frame for them; a mid-stream read failure emits 'degraded' rather than a delta that removes everything, and 'recovered' follows when the reads succeed again.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| types | string (comma-separated) | No | arbs,snipes,middles,low_holds | Subset of arbs|snipes|middles|low_holds to stream. Invalid/empty result -> 400. |
| sport_key | string | No | – | Filter events by sport. |
| min_value | number | No | 0 | Minimum edge value filter, ignored for the low_holds type (which sorts by holdPct instead). |
curl \
-H "X-API-Key: YOUR_KEY" \
"https://krokodds.com.au/api/v1/stream/opportunities?types=arbs%2Csnipes%2Cmiddles%2Clow_holds"{
"note": "This is an SSE stream (Content-Type: text/event-stream), not a single JSON response. Example frames:",
"frames": [
"event: ready\ndata: {\"types\":[\"arbs\",\"snipes\",\"middles\",\"low_holds\"],\"sport_key\":null,\"min_value\":0,\"poll_ms\":5000}\n\n",
"event: snapshot\ndata: {\"type\":\"arbs\",\"rows\":[{\"id\":\"a1\",\"value\":3.2,\"sport_key\":\"basketball_nba\"}]}\n\n",
"event: delta\ndata: {\"type\":\"arbs\",\"added\":[],\"removed\":[\"a2\"],\"updated\":[{\"id\":\"a1\",\"value\":3.5}]}\n\n",
"event: degraded\ndata: {\"failed_sources\":[\"snipes\"],\"meta\":{\"degraded\":true,\"failed_sources\":[\"snipes\"]}}\n\n",
"event: recovered\ndata: {\"failed_sources\":[],\"meta\":{}}\n\n",
": keepalive 1735689600000\n\n",
"event: bye\ndata: {\"reason\":\"max_stream_duration\"}\n\n"
]
}Other categories
Per-event head-to-head odds, alternate lines, best prices across books, player props, live state and AI-generated summaries.
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.
The full Australian racing stack — thoroughbred, harness and greyhound. Meetings and runner fields, form, sectionals, ratings, market movers, exchange arbs, tote pools and results.
Raw per-bookmaker prices as scraped, before any analysis layer. `/scraped-odds/*` paths are aliases of the equivalent `/odds-feed/*` route.
Sport reference data, team form guides, league standings, injury reports and weather-impact analysis.
AI-generated betting tips with confidence scores and historical accuracy tracking.
Ranked leaderboards built on top of the stat feeds — per-sport player leaders for a chosen stat, season and view.
ATP/WTA rankings, upcoming fixtures, match results, Elo ratings and surface-specific stats.
Deep archives — player and team game logs, shot charts, AFL/NRL historical data. Paid API plan only.
Static lookup data for building your own UI — players, venues, headshots, team logos and sport/league metadata.
Cricket, cycling, esports, Formula 1, golf, MMA, boxing, NFL and soccer coverage beyond the AU staples.
Odds history, steam-move detection, player prop stats/results, injuries, weather and other supporting feeds.
Polymarket and Kalshi sentiment plus a universal prediction-market catalogue and resolutions feed.