Opportunities API 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.
7 of 7 endpoints · all categories · get a free API key
GET/api/v1/opportunitiesFree tier
Umbrella/legacy endpoint that dispatches to the same underlying data as the dedicated opportunity-type routes, selected via `type`.
Legacy/umbrella wrapper around the opportunities loader — response shape per row depends on `type` and is not normalized to one schema across types. Response includes a static `endpoints` hint block pointing to the racing endpoints (`/api/v1/racing/arbs`, `/api/v1/racing/movers`), which do not correspond to any of the 14 files audited here. Rate-limit headers use `limit` (the row cap) rather than the usual free/api tier request cap seen on other routes. Sports rows are gated on the 12h commence cutoff plus the same 90-min detection-age / sane-odds gate as the dedicated routes; `racing_arbs`/`racing_movers`/`racing` are gated on a 10-min detection age (two cycles of their 5-min writers).
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| type | enum(all|positive-ev|snipes|middles|racing|playerprops) | No | all | Which opportunity category to fetch; `positive-ev` is aliased internally to `snipes`. Determines the feature-gate applied (snipes→positive_ev, middles→middles, racing→racing, playerprops→player_props; `all` has no per-type gate). |
| sport_key | string | No | – | Filter by sport key. Lowercased. |
| sport | string | No | – | Filter by sport name; uppercased before matching. |
| minvalue | number | No | 0 | Minimum opportunity value/edge threshold (also accepts `min_value`). |
| min_value | number | No | 0 | Alias for minvalue. |
| limit | number | No | 100 | Max rows returned. Clamped to 100 (free) / 500 (api). |
curl \
-H "X-API-Key: YOUR_KEY" \
"https://krokodds.com.au/api/v1/opportunities?type=all"{
"success": true,
"data": [
{
"id": "snipe_abc123",
"sport": "NBA",
"sport_key": "basketball_nba",
"event": "Boston Celtics vs Los Angeles Lakers",
"market": "h2h",
"selection1": "Boston Celtics",
"bookmaker1": "sportsbet",
"odds1": 2.1,
"value": 6.4,
"commence_time": "2026-09-03T23:10:00Z"
}
],
"meta": {
"tier": "free",
"limit": 100,
"requested_limit": 100,
"timestamp": "2026-09-03T10:06:00Z"
},
"endpoints": [
{
"path": "/api/v1/racing/arbs",
"description": "Australian racing arbitrage opportunities",
"parameters": [
"venue",
"race_type (T/H/G)",
"minedge",
"limit"
]
},
{
"path": "/api/v1/racing/movers",
"description": "Racing steamers and drifters (significant market movers)",
"parameters": [
"venue",
"race_type (T/H/G)",
"movement_type (steamer|drifter)",
"min_movement",
"limit"
]
}
]
}GET/api/v1/opportunities/arbitrageFree tier
Cross-bookmaker arbitrage (surebet) opportunities — two legs that lock in guaranteed profit regardless of outcome.
Supports keyset pagination via cursor/next_cursor and field projection via `fields`. Drops arbs for events that started >12h ago and rows whose `price_observed_at` (the price-read time, not the write time) is >90min old, rows carrying no price-read stamp at all, or rows with insane odds (the price-freshness check/the odds sanity check). exchange legs are stripped for hobby-equivalent (free) tier via the exchange filter. `status` (live/upcoming) is derived from commence_time, not stored. Cache 300s both tiers.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| sport_key | string | No | – | Filter by sport key. Lowercased. |
| sport | string | No | – | Filter by sport (also accepts sport_key as fallback); uppercased. |
| minvalue | number | No | 0 | Minimum arb value/edge % (also accepts min_value). |
| min_value | number | No | 0 | Alias for minvalue. |
| bookmaker | string | No | – | Substring match against either leg's bookmaker (case-insensitive). |
| limit | number | No | 100 | Max rows per page. Clamped to 100 (free) / 10000 (api). |
| cursor | string | No | – | Opaque keyset-pagination cursor from a prior response's meta.next_cursor. |
| fields | string (comma-separated) | No | – | Field projection — restrict each row to only the named fields. |
curl \
-H "X-API-Key: YOUR_KEY" \
"https://krokodds.com.au/api/v1/opportunities/arbitrage?sport_key=aussierules_afl"{
"success": true,
"data": [
{
"id": "arb_9f2c1a",
"event": "Boston Celtics vs Los Angeles Lakers",
"home_team": "Boston Celtics",
"away_team": "Los Angeles Lakers",
"sport": "NBA",
"sport_key": "basketball_nba",
"market": "h2h",
"selection1": "Boston Celtics",
"selection2": "Los Angeles Lakers",
"bookmaker1": "sportsbet",
"bookmaker2": "ladbrokes",
"odds1": 2.05,
"odds2": 2.15,
"bet_link1": "https://www.sportsbet.com.au/...",
"bet_link2": "https://www.ladbrokes.com.au/...",
"line": null,
"value": 2.35,
"instructions": "Stake $511.20 on Boston Celtics @ sportsbet, $488.80 on Los Angeles Lakers @ ladbrokes",
"tool_type": "surebet",
"status": "upcoming",
"commence_time": "2026-09-03T23:10:00Z",
"updated_at": "2026-09-03T09:55:00Z"
}
],
"meta": {
"count": 1,
"tier": "free",
"limit": 100,
"requested_limit": 100,
"next_cursor": "eyJ2IjoyLjM1LCJpZCI6ImFyYl85ZjJjMWEifQ==",
"filters": {
"sport": null,
"sport_key": null,
"bookmaker": null,
"min_value": null
},
"timestamp": "2026-09-03T10:06:00Z",
"rate_limit": {
"limit": 100,
"remaining": 940,
"reset": "2026-10-01T00:00:00Z"
}
}
}GET/api/v1/opportunities/low-holdsFree tier
Two-way markets where the bookmaker overround (vig) across two books is unusually low, near break-even for bettors.
Fastest-refreshing opportunities route: cache 60s both tiers vs 300s for arbitrage/middles/positive-ev. Sorted ascending by hold percentage (lowest hold first). Same staleness (12h commence cutoff) and price-freshness (90min) filtering, plus the exchange filter, as arbitrage. Keyset pagination + field projection supported.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| sport_key | string | No | – | Filter by sport key. Lowercased. |
| sport | string | No | – | Filter by sport (also accepts sport_key fallback); uppercased. |
| minvalue | number | No | 0 | Minimum value threshold (also accepts min_value); checks value/holdPct alias chain. |
| min_value | number | No | 0 | Alias for minvalue. |
| max_hold | number | No | – | Upper bound on hold percentage (holdPct or value). |
| bookmaker | string | No | – | Substring match against either leg's bookmaker. |
| limit | number | No | 100 | Max rows per page. Clamped to 100 (free) / 10000 (api). |
| cursor | string | No | – | Opaque keyset-pagination cursor. |
| fields | string (comma-separated) | No | – | Field projection list. |
curl \
-H "X-API-Key: YOUR_KEY" \
"https://krokodds.com.au/api/v1/opportunities/low-holds?sport_key=aussierules_afl"{
"success": true,
"data": [
{
"id": "lowhold_7ab391",
"sport": "AFL",
"sport_key": "aussierules_afl",
"event": "Collingwood vs Essendon",
"home_team": "Collingwood",
"away_team": "Essendon",
"market": "h2h",
"commence_time": "2026-09-03T08:40:00Z",
"bookmaker1": "betright",
"selection1": "Collingwood",
"odds1": 1.87,
"bookmaker2": "tab",
"selection2": "Essendon",
"odds2": 2.05,
"bet_link1": "https://www.betright.com.au/...",
"bet_link2": "https://www.tab.com.au/...",
"line": null,
"value": 1.8,
"hold_pct": 1.8,
"profit_percent": 1.8,
"instructions": "Combined hold across both books is 1.8% — near break-even.",
"tool_type": "low_hold",
"status": "upcoming",
"updated_at": "2026-09-03T09:50:00Z"
}
],
"meta": {
"count": 1,
"tier": "free",
"limit": 100,
"requested_limit": 100,
"next_cursor": null,
"filters": {
"sport": "AFL",
"sport_key": null,
"bookmaker": null,
"min_value": null,
"max_hold": null
},
"timestamp": "2026-09-03T10:06:00Z",
"rate_limit": {
"limit": 100,
"remaining": 940,
"reset": "2026-10-01T00:00:00Z"
}
}
}GET/api/v1/opportunities/middlesFree tier
Middle opportunities — two different lines/points across bookmakers where both bets can win if the result lands between them.
Only middles route without a `filters` block in meta (present on arbitrage/low-holds/positive-ev but not middles). Same 12h staleness + 90min freshness filtering as arbitrage. No the exchange filter call here (unlike arbitrage/low-holds), so exchange legs are NOT stripped for free tier on this route. Cache 300s both tiers.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| sport_key | string | No | – | Filter by sport key. Lowercased. |
| sport | string | No | – | Filter by sport (also accepts sport_key fallback); uppercased. |
| minvalue | number | No | 0 | Minimum middle value (also accepts min_value). |
| min_value | number | No | 0 | Alias for minvalue. |
| limit | number | No | 100 | Max rows per page. Clamped to 100 (free) / 10000 (api). |
| cursor | string | No | – | Opaque keyset-pagination cursor. |
| fields | string (comma-separated) | No | – | Field projection list. |
curl \
-H "X-API-Key: YOUR_KEY" \
"https://krokodds.com.au/api/v1/opportunities/middles?sport_key=aussierules_afl"{
"success": true,
"data": [
{
"id": "middle_44d1c9",
"event": "Sydney Swans vs Geelong Cats",
"home_team": "Sydney Swans",
"away_team": "Geelong Cats",
"sport": "AFL",
"sport_key": "aussierules_afl",
"market": "spreads",
"selection1": "Sydney Swans -6.5",
"selection2": "Geelong Cats +9.5",
"bookmaker1": "sportsbet",
"bookmaker2": "pointsbet",
"odds1": 1.91,
"odds2": 1.95,
"bet_link1": "https://www.sportsbet.com.au/...",
"bet_link2": "https://www.pointsbet.com.au/...",
"value": 4.2,
"line1": -6.5,
"line2": 9.5,
"middle_window": 3,
"worst_case_loss": -12.5,
"best_case_profit": 87.5,
"instructions": "Bet Sydney -6.5 @ sportsbet and Geelong +9.5 @ pointsbet.",
"tool_type": "middle",
"status": "upcoming",
"commence_time": "2026-09-03T08:40:00Z",
"updated_at": "2026-09-03T09:52:00Z"
}
],
"meta": {
"count": 1,
"tier": "free",
"limit": 100,
"requested_limit": 100,
"next_cursor": null,
"timestamp": "2026-09-03T10:06:00Z",
"rate_limit": {
"limit": 100,
"remaining": 940,
"reset": "2026-10-01T00:00:00Z"
}
}
}GET/api/v1/opportunities/player-propsFree tier
Individual player-prop bets with a positive expected-value edge, optionally enriched with historical hit-rate stats.
`historical_stats` is only populated when include_stats=true AND tier !== 'free' — free-tier requests get `historical_stats: undefined` (omitted key) even if include_stats=true. Underlying query scans a 12h commence_time window (max 5000 rows) then sorts by the edge percentage in memory rather than at the DB level, because there's no index for a global the edge percentage sort (avoids Postgres statement timeout). No cursor/fields projection support (unlike arbitrage/low-holds/middles/positive-ev). Filters out rows where player_name looks like a market/selection label (regex-based artifact cleanup). Cache 60s both tiers. **No `bet_link` is served.** Every row includes `price_age_seconds` (integer seconds since its first usable price stamp, using explicit capture/update/detection fields before the fallback `timestamp`; future stamps report 0) and `stale` (boolean, true at 90 minutes or older). Quotes are retained through exactly 24 hours and excluded only beyond 24 hours; the event kickoff cutoff still applies. Missing or unparseable stamps fail open with `price_age_seconds: null` and `stale: false` (unknown age). Age and stale status are recalculated at request time. **`commence_time` is served** as ISO 8601 from the row's stored event kickoff (the `commence_time` column, falling back to the JSONB `commencetime`); it is `null` only when the row carries none of those keys — the per-capture scraper `timestamp` is not treated as the kickoff. With the value present the 12h stale window now applies as intended (a `null` kickoff previously made that comparison a no-op).
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| sport_key | string | No | – | Filter by sport key. Lowercased. |
| sport | string | No | – | Filter by sport (also accepts sport_key fallback); lowercased (unlike other opportunities routes which uppercase). |
| market | string | No | – | Exact match against market_key, e.g. `player_points`. |
| player | string | No | – | Substring match against player_name (case-insensitive). |
| bookmaker | string | No | – | Substring match against bookmaker. |
| min_ev | number | No | 0 | Minimum the edge percentage threshold. |
| include_stats | boolean | No | false | When `true` (and tier is not free), joins in historical_stats (hit rates, streaks) per prop from the prop-stats store. |
| limit | number | No | 100 | Max rows returned. Clamped to 100 (free) / 10000 (api). |
curl \
-H "X-API-Key: YOUR_KEY" \
"https://krokodds.com.au/api/v1/opportunities/player-props?sport_key=aussierules_afl"{
"success": true,
"data": [
{
"id": "prop_e8a112",
"event_id": "basketball_nba_20260903_lal_bos",
"event": "Boston Celtics vs Los Angeles Lakers",
"sport": "basketball_nba",
"sport_key": "basketball_nba",
"market_key": "player_points",
"player_name": "Jayson Tatum",
"line": 27.5,
"side": "over",
"odds": 1.95,
"bookmaker": "sportsbet",
"ev_percentage": 7.8,
"commence_time": "2026-09-03T23:10:00Z",
"price_age_seconds": 18000,
"stale": true,
"historical_stats": {
"sample_size": 22,
"hit_rate_over": 0.68,
"hit_rate_under": 0.32,
"last_5_results": [
true,
true,
false,
true,
true
],
"last_5_hit_rate": 0.8,
"streak": "over_2",
"line_bucket": "25-30",
"updated_at": "2026-09-02T22:00:00Z"
}
}
],
"meta": {
"count": 1,
"tier": "api",
"limit": 100,
"requested_limit": 100,
"filters": {
"sport": null,
"sport_key": "basketball_nba",
"player": null,
"bookmaker": null,
"market": null,
"min_ev": null
},
"timestamp": "2026-09-03T10:06:00Z",
"rate_limit": {
"limit": 500,
"remaining": 940,
"reset": "2026-10-01T00:00:00Z"
}
}
}GET/api/v1/opportunities/positive-evFree tier
Single-leg bets priced favorably against a sharp reference price (e.g. exchange fair odds) — positive expected value snipes.
`bookmaker2` falls back to the synthetic string 'Betfair Fair' (a reference price, not a bettable book) when no second real book is quoted; bet_link2 is only populated when a genuine second bookmaker exists. `value_indicator` (overpriced/underpriced/fair) is computed by comparing odds1 to sharpPrice. Same 12h staleness + 90min freshness filtering as arbitrage/middles. No the exchange filter applied. Cache 300s both tiers.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| sport_key | string | No | – | Filter by sport key. Lowercased. |
| sport | string | No | – | Filter by sport (also accepts sport_key fallback); uppercased. |
| minvalue | number | No | 0 | Minimum EV value (also accepts min_value). |
| min_value | number | No | 0 | Alias for minvalue. |
| bookmaker | string | No | – | Substring match against either leg's bookmaker. |
| min_odds | number | No | 0 | Minimum odds1 threshold (also accepts minOdds). |
| minOdds | number | No | 0 | Alias for min_odds. |
| limit | number | No | 100 | Max rows per page. Clamped to 100 (free) / 10000 (api). |
| cursor | string | No | – | Opaque keyset-pagination cursor. |
| fields | string (comma-separated) | No | – | Field projection list. |
curl \
-H "X-API-Key: YOUR_KEY" \
"https://krokodds.com.au/api/v1/opportunities/positive-ev?sport_key=aussierules_afl"{
"success": true,
"data": [
{
"id": "snipe_5c02af",
"event": "Boston Celtics vs Los Angeles Lakers",
"home_team": "Boston Celtics",
"away_team": "Los Angeles Lakers",
"sport": "NBA",
"sport_key": "basketball_nba",
"market": "h2h",
"selection1": "Boston Celtics",
"bookmaker1": "sportsbet",
"bookmaker2": "Betfair Fair",
"odds1": 1.95,
"odds2": null,
"bet_link1": "https://www.sportsbet.com.au/...",
"bet_link2": null,
"line": null,
"value": 5.6,
"value_indicator": "underpriced",
"sharp_price": 1.85,
"confidence": "high",
"instructions": "Back Boston Celtics @ sportsbet 1.95, sharp fair price implies 1.85.",
"tool_type": "positive_ev",
"status": "upcoming",
"commence_time": "2026-09-03T23:10:00Z",
"updated_at": "2026-09-03T09:58:00Z"
}
],
"meta": {
"count": 1,
"tier": "free",
"limit": 100,
"requested_limit": 100,
"next_cursor": null,
"filters": {
"sport": null,
"sport_key": "basketball_nba",
"bookmaker": null,
"min_value": null,
"min_odds": null
},
"timestamp": "2026-09-03T10:06:00Z",
"rate_limit": {
"limit": 100,
"remaining": 940,
"reset": "2026-10-01T00:00:00Z"
}
}
}GET/api/v1/opportunities/sgm-picksFree tier
AI-generated Same-Game-Multi (SGM) leg combinations with confidence tier, fair/minimum odds, and combined win probability.
Only opportunities route that reads via the secondary store (the secondary store) as its primary path, with an the primary-store flag feature-flagged the primary store attempt first that falls back to the secondary store on failure — returns 503 'Database unavailable' if the secondary store admin isn't initialized, even if the primary store would have worked (503 check happens before the primary store attempt). Only future events are returned (commenceTime >= now); no staleness/freshness filtering like the other opportunities routes since these are pre-game AI picks, not live odds edges. Cache 60s both tiers. No meta.limit/requested_limit/next_cursor fields in the response (unlike arbitrage/middles/etc). `legs[]` is passed through **verbatim from the suggestion document** (37 optional keys per leg as stored) — there is no per-leg `market`/`odds` field.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| sport | string | No | – | Filter by sport key (also accepts sport_key). Lowercased. |
| sport_key | string | No | – | Alias for sport. |
| tier | enum(safe|value|longshot) | No | – | Filter to a risk tier; any other value is ignored (no filter). |
| min_confidence | number | No | 1 | Minimum confidence score, clamped to 1-5. |
| limit | number | No | 20 | Max rows returned. Clamped to 50 (free) / 1000 (api). |
curl \
-H "X-API-Key: YOUR_KEY" \
"https://krokodds.com.au/api/v1/opportunities/sgm-picks?sport=aussierules_afl"{
"success": true,
"data": [
{
"id": "sgm_pick_a91cd0",
"event_id": "basketball_nba_20260903_lal_bos",
"event": "Boston Celtics vs Los Angeles Lakers",
"sport": "NBA",
"sport_key": "basketball_nba",
"commence_time": "2026-09-03T23:10:00Z",
"tier": "value",
"confidence": 4,
"confidence_label": "High",
"legs": [
{
"marketKey": "alternate_totals",
"marketLabel": "Alt Total",
"selection": "Over +3.5",
"line": 3.5,
"openingLine": 3.5,
"bestOdds": 1.15,
"openingBestOdds": 1.12,
"bestBookmaker": "Unibet",
"bookCount": 4,
"impliedProbability": 0.87,
"marketImpliedProbability": 0.85,
"evPercent": null,
"hasEvEdge": false,
"hitRate": null,
"projection": null,
"team": null,
"playerName": null,
"probSource": "implied",
"signals": [
{
"type": "high_probability",
"label": "85% fair implied",
"strength": 3
}
],
"lockReason": null,
"lineHistory": []
}
],
"fair_odds_conservative": 2.9,
"minimum_acceptable_odds": 2.6,
"combined_probability": 0.365,
"suggested_bookmaker": "sportsbet",
"bet_link": "https://www.sportsbet.com.au/...",
"resolution": "pending"
}
],
"meta": {
"count": 1,
"tier": "free",
"timestamp": "2026-09-03T10:06:00Z",
"rate_limit": {
"limit": 50,
"remaining": 940,
"reset": "2026-10-01T00:00:00Z"
}
}
}Other categories
Per-event head-to-head odds, alternate lines, best prices across books, player props, live state and AI-generated summaries.
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.
Account and billing endpoints, bulk export, SSE streaming, webhook management and API health.
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.