Infrastructure — Account, usage & bulk export
The account, usage & bulk export half of the Infrastructure reference — every route with its parameters, credit cost and a worked example.
5 of 15 endpoints · all categories · get a free API key
GET/api/v1/bulkAPI plan
Enterprise cursor-paginated JSON bulk export of the historical archive across 13 datasets (team/player game logs, prop results, game results, MLB/soccer/NCAAF/NFL/NHL/NRL advanced stats, exchange BSP/results). No ?dataset= returns the dataset manifest.
Tier detail: api (bulk_export is paid-only, not in the free-tier feature set)
the credit cost: 25 (enterprise-weighted, set explicitly in the standard v1 route factory config). Cache TTL 3600s both tiers (archive is immutable so cached hard). Cursor pagination via keyset pagination by id (`.order(id).gt(id, cursor).limit(n)`) — index-cheap and resumable. Companion CSV/JSON export at /api/v1/export shares the same bulk_export gate and 25-credit cost.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| dataset | string | No | – | One of: the team game-log store, the player game-log store, player_history, prop_results, game_results, mlb_advanced, soccer_xg, ncaaf_player_games, the NFL tracking store, nhl_skaters, nrl_player_games, exchange_bsp, exchange_results. Omitted → returns manifest (list of {dataset, description}) with no DB read. |
| sport_key | sport | string | No | – | Only supported for the player game-log store and nrl_player_games datasets; 400 otherwise (message points caller to /api/v1/historical/player-game-log). |
| cursor | string | No | – | Opaque id cursor for resumable pagination — pass back `next_cursor` from the previous response until it's null. |
| limit | integer | No | 100 | Row cap. Free tier max 100, api tier max 500 (though the feature itself is api-tier gated). |
curl \
-H "X-API-Key: YOUR_KEY" \
"https://krokodds.com.au/api/v1/bulk?dataset=value"{
"success": true,
"data": [
{
"id": "0001",
"sportKey": "americanfootball_nfl",
"team": "KC",
"gameDate": "2026-08-30"
}
],
"meta": {
"count": 100,
"tier": "api",
"limit": 100,
"requested_limit": 100,
"dataset": "team_game_logs",
"collection": "team_game_log",
"next_cursor": "0100",
"has_more": true,
"timestamp": "2026-09-03T00:00:00.000Z",
"rate_limit": {
"limit": 100,
"remaining": 49975,
"reset": "2026-10-01T00:00:00.000Z"
}
}
}GET/api/v1/exportAPI plan
Enterprise bulk data export as downloadable CSV (or JSON) from the cold archive mirror — companion to the JSON-only /api/v1/bulk endpoint.
Tier detail: api (bulk_export is paid-only)
Uses the standard auth guard(request, 'bulk_export', 25) — same 25-credit weighting as /api/v1/bulk. Reads the Postgres archive by default (ordered by id, so the page is stable); the cold-archive mirror is used only when the cold-archive flag is on. 503 'Dataset not yet available' when the dataset's table is missing. CSV response sets filename `krok-{datasetKey}-{YYYY-MM-DD}.csv` and header X-Krok-Export-Rows with the actual row count. Cache 1800s for the json format variant.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| dataset | string | Yes | – | One of: game_results, player_props_results, model_track_record, gameday_odds_history, clv_archive, ev_hit_rates. 400 if missing/unknown. |
| limit | integer | No | 5000 | Capped at 5000 for both free and api tier (LIMIT_BY_TIER). |
| format | string | No | csv | csv (file download, Content-Disposition attachment) or json (standard v1 envelope via v1Success). |
curl \
-H "X-API-Key: YOUR_KEY" \
"https://krokodds.com.au/api/v1/export?dataset=value"{
"success": true,
"data": [
{
"id": "g1",
"sport_key": "americanfootball_nfl",
"home_score": 24,
"away_score": 17,
"completed": true
}
],
"meta": {
"dataset": "game_results",
"label": "Settled game results",
"limit": 5000,
"requested_limit": 5000
}
}GET/api/v1/meFree tier
API key introspection — returns the caller's tier, monthly usage/limit/remaining, per-request rate limit window, reset date, and a masked key preview.
Tier detail: free (no feature/tier gate at all — just requires a valid, active key)
CORRECTION vs. task brief: this route is API-KEY auth (X-API-Key header / apikey / api_key query param -> API-key verification), NOT dashboard session auth (dashboard-session auth) — it does not import server-auth at all. Only webhooks/route.ts and clv-archive/[eventId]/route.ts use session auth in this batch of 14 (clv-archive/[eventId] has since moved to API-key auth on 'closing_lines' — see its entry). PER_TIER_REQUEST_LIMITS here: free=100, api=10000 (note: api's 10000 differs from the 500-ish caps seen on most other free/api routes).
curl \
-H "X-API-Key: YOUR_KEY" \
"https://krokodds.com.au/api/v1/me"{
"success": true,
"data": {
"tier": "free",
"monthly_limit": 1000,
"current_usage": 42,
"remaining": 958,
"usage_pct": 4.2,
"reset_date": "2026-10-01T00:00:00.000Z",
"per_request_limit": 100,
"rate_limit_window": {
"limit": 60,
"remaining": 55,
"reset": "2026-09-03T00:05:00.000Z"
},
"api_key_preview": "krok...ab12"
},
"meta": {
"timestamp": "2026-09-03T00:00:00.000Z"
}
}GET/api/v1/me/usageFree tier
Credit and rate-limit usage for the calling key — tier, credits used/remaining/limit, usage percentage, the reset date and the live rate-limit window.
Tier detail: free (no feature/tier gate — only requires a valid, active key)
Measured 2026-10-22 against prod. Same counters as /api/v1/me but under different field names — `credits_used`/`credits_remaining`/`credits_limit` here vs `current_usage`/`remaining`/`monthly_limit` there; `rate_limit` here vs `rate_limit_window` there. /me additionally returns `api_key_preview` and `per_request_limit`. No `x-credits-cost` header (a read costs 1 credit). `Cache-Control: private, no-store`. Keyless → 401 `API key required`. Response headers carry the same numbers: `x-credits-limit`, `x-credits-remaining`, `x-credits-reset`, `x-ratelimit-limit`, `x-ratelimit-remaining`, `x-ratelimit-reset`, `x-ratelimit-tier`.
curl \
-H "X-API-Key: YOUR_KEY" \
"https://krokodds.com.au/api/v1/me/usage"{
"success": true,
"data": {
"tier": "api",
"credits_used": 62240,
"credits_remaining": 437760,
"credits_limit": 500000,
"usage_pct": 12.45,
"reset_date": "2026-11-01T00:00:00.000Z",
"rate_limit": {
"limit": 500,
"remaining": 462,
"reset": "2026-10-22T07:58:00.000Z"
}
},
"meta": {
"truncated": false,
"timestamp": "2026-10-22T07:57:25.241Z"
}
}GETPOSTDELETE/api/v1/webhooksFree tier
CRUD for outbound webhook subscriptions: GET lists the caller's subscriptions (secret never returned), POST registers a new one and answers its whsec_ signing secret once (max 10 per account), DELETE removes one by id.
Tier detail: all plans — webhooks is in the free-tier feature set. The route verifies the X-API-Key presented on the request (401 without one) and applies no tier gate.
API-key auth (X-API-Key header) — no key answers 401 {error:'API key required'}, an invalid one 401. No tier gate: 'webhooks' sits in the free-tier feature set, so free and api keys behave identically here (measured 2026-10-23: free key 200, api key 200, no key 401). GET reads `api_webhooks` (limit 500) and keeps rows whose keyHash or userId matches the caller, capped at 50, returning {success,data:[{id,url,events,active,created_at}],meta:{count,truncated}} — the secret is never in this response. POST validates url + a non-empty events array against the 14 accepted event names, runs the URL through checkWebhookUrl() (SSRF guard), refuses an 11th subscription ('Maximum 10 webhooks per account'), and upserts {userId,keyHash,url,events,label,active,secret,createdAt} into `api_webhooks`. DELETE requires ?id=, 404s when the row is missing, 403s when it belongs to another caller, then hard-deletes the row (supabaseDelete — not a deactivate). This route mints the signing secret too: POST stores whsec_<32-hex> on the row and returns it once in the create response, exactly as the dashboard route /api/api-keys/webhooks does; GET never returns it. Responses carry account-level X-Credits-* headers only — the route has no per-minute limiter — plus Cache-Control: private, no-store and CORS, with OPTIONS answering 204. The route does not consult the per-route tier cap map: `limit` is not a parameter and the list is fixed at 50.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| url (POST) | string | Yes | – | Destination HTTPS URL. Passed through checkWebhookUrl() SSRF guard (rejects internal/private-network targets) before being accepted. |
| events (POST) | string[] | Yes | – | Event types to subscribe to. Required and must be non-empty (400 'events array is required' otherwise); each value must be one of arb_created, snipe_created, middle_created, low_hold_created, steam_move_created, result.settled, racing.meeting.posted, racing.result.settled, racing.odds.movement, racing.betfair.update, sports.match.started, sports.result.settled, sports.odds.movement, sports.player.prop.update — anything else is 400. |
| label (POST) | string | No | – | Free-text label stored with the subscription. |
| id (DELETE) | string | Yes | – | Subscription id to remove. 404 if it does not exist; 403 if it belongs to someone else. The row is hard-deleted, not deactivated. |
curl \
-H "X-API-Key: YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"url (POST)":"value","events (POST)":"evt_abc123","label (POST)":"value","id (DELETE)":"abc123"}' \
"https://krokodds.com.au/api/v1/webhooks"{
"GET": {
"success": true,
"data": [
{
"id": "wh_1758700800000_ab12cd",
"url": "https://example.com/hook",
"events": [
"arb_created",
"middle_created"
],
"active": true,
"created_at": "2026-09-01T00:00:00.000Z"
}
],
"meta": {
"count": 1,
"truncated": false
}
},
"POST": {
"success": true,
"data": {
"id": "wh_1758700900000_ef34gh",
"url": "https://example.com/hook2",
"events": [
"arb_created"
],
"label": "",
"active": true,
"secret": "whsec_<32-hex>"
}
},
"DELETE": {
"success": true,
"deleted": "wh_1758700900000_ef34gh"
}
}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.