Krok Odds

Additional Data — Player props & player profiles

The player props & player profiles half of the Additional Data reference — every route with its parameters, credit cost and a worked example.

8 of 19 endpoints · all categories · get a free API key

GET/api/v1/player-props-resultsFree tier

Settled player-prop outcomes (hit/miss/actual value) per event.

Truth source is `player_props_results.outcomes[]` per repo memory (the prop backtest model). event_id path is a single doc fetch keyed on eventId (slashes replaced with underscores, capped at 1500 chars); without event_id it's a settled_at-ordered scan.

Query parameters
NameTypeRequiredDefaultDescription
sport_keystringNo–Post-filtered in memory (sportKey has no extracted column).
event_idstringNo–If set, does a direct doc lookup by id instead of a scan.
player_namestringNo–Case-insensitive partial match, filters within each event's outcomes array.
marketstringNo–Exact match (lowercased) on outcome market_key.
sincestring (ISO date)No–Clamped by clampHistoricalFrom(); filters/orders on the indexed settled_at column.
limitintegerNo50Capped at 50 on free tier, 2000 on api tier.
Example request
curl \
  -H "X-API-Key: YOUR_KEY" \
  "https://krokodds.com.au/api/v1/player-props-results?sport_key=aussierules_afl"
Example response
{
  "success": true,
  "data": [
    {
      "event_id": "b7c3d9",
      "sport_key": "basketball_nba",
      "event_name": "Boston Celtics vs Denver Nuggets",
      "commence_time": "2026-09-02T00:10:00Z",
      "completed_at": "2026-09-02T02:45:00Z",
      "outcomes": [
        {
          "market_key": "player_points",
          "player_name": "Jayson Tatum",
          "line": 27.5,
          "side": "over",
          "hit": true,
          "actual_value": 31
        }
      ],
      "outcome_count": 1,
      "updated_at": "2026-09-02T03:00:00.000Z"
    }
  ],
  "meta": {
    "count": 1,
    "tier": "free",
    "limit": 50,
    "requested_limit": 50,
    "sport_key": null,
    "event_id": null,
    "player_name": null,
    "market": null,
    "since": null,
    "timestamp": "2026-09-03T04:00:00.000Z",
    "rate_limit": {
      "limit": 50,
      "remaining": 998,
      "reset": "2026-10-01T00:00:00.000Z"
    }
  }
}
GET/api/v1/player-props-statsFree tier

Aggregate hit/miss/push counts and hit rate per player/market/line/side.

Backed by the primary store collection the prop-stats store (same table used by /api/v1/players/compare for its per-player prop breakdown). hit_rate = hits / (hits+misses), pushes excluded from denominator.

Query parameters
NameTypeRequiredDefaultDescription
sport_keystringNo–
player_slugstringNo–Exact match, post-filtered in memory after fetch.
player_canonicalstringNo–
market_keystringNo–
sidestringNo–Must be "over" or "under" or it is ignored.
limitintegerNo50Capped at 50 on free tier, 2000 on api tier.
Example request
curl \
  -H "X-API-Key: YOUR_KEY" \
  "https://krokodds.com.au/api/v1/player-props-stats?sport_key=aussierules_afl"
Example response
{
  "success": true,
  "data": [
    {
      "id": "player_props_stats-jayson-tatum-player_points-27.5-over",
      "sport_key": "basketball_nba",
      "market_key": "player_points",
      "player_name": "Jayson Tatum",
      "player_slug": "jayson-tatum",
      "canonical_key": "nba_jayson-tatum",
      "line": 27.5,
      "side": "over",
      "hits": 34,
      "misses": 21,
      "pushes": 2,
      "total": 57,
      "decided": 55,
      "hit_rate": 0.6182,
      "last_result_at": "2026-09-02T03:00:00.000Z",
      "updated_at": "2026-09-02T03:05:00.000Z"
    }
  ],
  "meta": {
    "count": 1,
    "tier": "free",
    "limit": 50,
    "requested_limit": 50,
    "sport_key": null,
    "player_slug": null,
    "player_canonical": null,
    "market_key": null,
    "side": null,
    "timestamp": "2026-09-03T04:00:00.000Z",
    "rate_limit": {
      "limit": 50,
      "remaining": 998,
      "reset": "2026-10-01T00:00:00.000Z"
    }
  }
}
GET/api/v1/player-props/projectionsFree tier

AFL player projections branded as Krok Odds Projections, sourced from an advanced statistical model feed.

Route comment mandates branding this 'Krok Odds Projections' — never the underlying source name. Backed by the primary store collection the player-stats store (AFL only). `confidence` is a derived field = min(1, |Rating|/10), not a true model confidence. Cache TTL fixed at 3600s regardless of tier (not tier-scaled like most other v1 routes).

Query parameters
NameTypeRequiredDefaultDescription
seasonintegerNocurrent yearSeason year filter; 400 if non-numeric.
roundintegerNo–Round number filter; 400 if non-numeric.
teamstringNo–Exact match, case-insensitive, on team.
playerstringNo–Case-insensitive partial match on player name.
limitintegerNo100Clamped 1-500 (MAX_LIMIT).
Example request
curl \
  -H "X-API-Key: YOUR_KEY" \
  "https://krokodds.com.au/api/v1/player-props/projections?season=current%20year"
Example response
{
  "success": true,
  "data": [
    {
      "id": "fryzigg_2026_r23_marcus-bontempelli",
      "player_name": "Marcus Bontempelli",
      "team": "Western Bulldogs",
      "season": 2026,
      "round": 23,
      "game": "WB_vs_GEE_R23",
      "date": "2026-08-30",
      "opposition": "Geelong",
      "kicks": 18,
      "marks": 6,
      "handballs": 12,
      "tackles": 5,
      "goals": 1,
      "behinds": 0,
      "hit_outs": 0,
      "inside_50s": 4,
      "clearances": 7,
      "clangers": 2,
      "rebound_50s": 1,
      "frees_for": 2,
      "frees_against": 1,
      "time_on_ground_pct": 88,
      "rating": 132.4,
      "disposal_rating": 27.1,
      "confidence": 1
    }
  ],
  "meta": {
    "count": 1,
    "tier": "free",
    "license": "krokodds-derived",
    "season": 2026,
    "round": null,
    "team": null,
    "player": null,
    "source": "Krok Odds Projections",
    "timestamp": "2026-09-03T04:00:00.000Z",
    "rate_limit": {
      "limit": 500,
      "remaining": 999,
      "reset": "2026-10-01T00:00:00.000Z"
    }
  }
}
GET/api/v1/players/compareFree tier

Compare 2-5 players' prop hit rates and season stats side by side; adds tennis H2H record and Elo when both players are ATP/WTA.

H2H + Elo enrichment only fires when sport_key contains 'tennis' AND exactly 2 players given; Elo is looked up only for the first player's slug in the current code (second player's Elo (_eloB) is fetched but discarded/unused). Elo source table is the tennis player store, tried under id `atp_{slug}` then `wta_{slug}`. Uses the standard auth guard/v1Success pattern.

Query parameters
NameTypeRequiredDefaultDescription
playersstring (comma-separated slugs)Yes–Min 2, max 5 slugs/canonical keys; 400 if <2 or >5.
sport_keystringNo–
seasonstringNo–Accepted but currently unused inside loadPlayerStats (param name is _season).
metricsstring (comma-separated)No–Documented in the file header comment but not read/implemented in the current GET handler.
limitintegerNo20Max prop rows fetched per player, clamped 1-100.
Example request
curl \
  -H "X-API-Key: YOUR_KEY" \
  "https://krokodds.com.au/api/v1/players/compare?players=lebron-james"
Example response
{
  "success": true,
  "data": [
    {
      "slug": "novak-djokovic",
      "name": "Novak Djokovic",
      "sport_key": "tennis_atp",
      "props": [
        {
          "market_key": "total_games_over_under",
          "line": 21.5,
          "side": "over",
          "hit_rate": 62.5,
          "sample": 16,
          "hits": 10,
          "misses": 6
        }
      ],
      "season_stats": {
        "wins": 38,
        "losses": 6
      },
      "h2h": {
        "opponent_slug": "carlos-alcaraz",
        "opponent_name": "Carlos Alcaraz",
        "wins": 6,
        "losses": 5,
        "meetings": [
          {
            "date": "2026-07-14",
            "winner": "Carlos Alcaraz",
            "loser": "Novak Djokovic",
            "score": "6-4 6-7 7-6",
            "surface": "grass"
          }
        ],
        "surface_split": {
          "hard": {
            "a": 3,
            "b": 3
          },
          "clay": {
            "a": 2,
            "b": 1
          },
          "grass": {
            "a": 1,
            "b": 1
          }
        },
        "elo": {
          "overall": 2231,
          "hard": 2240,
          "clay": 2180,
          "grass": 2260
        }
      }
    },
    {
      "slug": "carlos-alcaraz",
      "name": "Carlos Alcaraz",
      "sport_key": "tennis_atp",
      "props": [
        {
          "market_key": "total_games_over_under",
          "line": 21.5,
          "side": "over",
          "hit_rate": 58.3,
          "sample": 12,
          "hits": 7,
          "misses": 5
        }
      ],
      "season_stats": {
        "wins": 41,
        "losses": 5
      }
    }
  ],
  "meta": {
    "count": 2,
    "tier": "free",
    "players": [
      "novak-djokovic",
      "carlos-alcaraz"
    ],
    "sport_key": "tennis_atp",
    "season": null,
    "limit": 20,
    "timestamp": "2026-09-03T04:00:00.000Z",
    "rate_limit": {
      "limit": 20,
      "remaining": 999,
      "reset": "2026-10-01T00:00:00.000Z"
    }
  }
}
GET/api/v1/players/{sport}/{id}/profileAPI plan

One player-season profile: identity, position, coverage, form badges, season stats, game log, line history, matchup, splits and similar players in a single payload.

Tier detail: api (paid)

The richest of the three player routes and the only one that returns camelCase keys - `sportKey`, `playerSlug`, `seasonStats`, `gameLog`, `lineHistory`, `altLines`, `formTable`, `splitRows`, `quickReads` - where `/frame` and `/hit-rates` return snake_case. Cost is 5 credits, `Cache-Control: private, no-store` with `x-cache-age: 600` and `x-ratelimit-tier: api`. An unlinked or empty player-season still answers **200**, not 404: `playerName` is null, `identity.state` is `pending` with a `note`, `analyticsOpen` is false and `coverage` is all zeros, so treat `identity.state`/`coverage.graded` as the presence signal rather than the status code. Section-level `state` values are `ready` or `building` with a per-section `n`. A read that fails behind the route answers 400/500 (`frame unavailable`) rather than an empty page. The example is the live, unmodified response for `rugbyleague_nrl` / `latrell_mitchell`, trimmed to two rows per array; it names no data source.

Path parameters
NameTypeRequiredDefaultDescription
sportstringYes–Sport key, lower-cased before matching. An unknown key is a 404 (`unknown sport`), and a key that exists but has no frame contract yet answers **200** with a single `{status: 'pending', sport_key, sport_state, reason}` row instead of data.
idstringYes–Player slug from any slug-carrying route (e.g. `meta.player_slug` on `/api/v1/insights/leaders`), lower-cased before matching and validated against `^[a-z0-9][a-z0-9_-]{0,119}$`. A slug that fails that pattern is a 400 (`invalid player id`). A well-formed slug for a player we have no rows for is **not** a 404 - see the notes.
Query parameters
NameTypeRequiredDefaultDescription
X-API-KeystringYes–API key. Without one the route is a 401 (`API key required`) - unlike `/frame` it has no open teaser.
seasonstringNo–Four-digit year, default the current calendar year. This filter is real, not decorative: `rugbyleague_nrl` / `latrell_mitchell` returns 85 rows, 13 graded and 10 markets for 2026 but 66 rows, 0 graded and 6 markets for 2025. Anything that is not a 4-digit year is a 400 (`season must be a 4-digit year`).
Example request
curl \
  -H "X-API-Key: YOUR_KEY" \
  "https://krokodds.com.au/api/v1/players/aussierules_afl/abc123/profile?X-API-Key=value"
Example response
{
  "success": true,
  "data": [
    {
      "sportKey": "rugbyleague_nrl",
      "sportState": "configured",
      "playerSlug": "latrell_mitchell",
      "playerName": "Latrell Mitchell",
      "identity": {
        "state": "ready",
        "canonicalId": "4703",
        "note": ""
      },
      "position": {
        "state": "pending",
        "value": null,
        "n": 3,
        "source": null,
        "note": "roster lists 2 positions (C, Centre)"
      },
      "analyticsOpen": false,
      "season": 2026,
      "builtAt": "2026-10-22T07:40:35.959Z",
      "minSample": 8,
      "coverage": {
        "rows": 85,
        "statValue": 85,
        "graded": 13,
        "events": 16,
        "markets": 10
      },
      "headerBadges": [
        {
          "key": "form",
          "label": "Form",
          "value": "15%",
          "n": 13,
          "state": "ready"
        },
        {
          "key": "dvp",
          "label": "DvP",
          "value": null,
          "n": 0,
          "state": "building",
          "note": "position and DvP source not on the frame yet"
        },
        {
          "key": "team",
          "label": "Team",
          "value": "South Sydney Rabbitohs",
          "n": 3,
          "state": "ready"
        }
      ],
      "seasonStats": [
        {
          "statKey": "runs",
          "label": "Runs",
          "mean": 10.833333333333334,
          "median": 11,
          "rollingSd": 3.8987177379235853,
          "floorP25": 9,
          "ceilingP75": 13.25,
          "n": 12,
          "band": null,
          "state": "ready"
        },
        {
          "statKey": "run_metres",
          "label": "Run Metres",
          "mean": 116.16666666666667,
          "median": 108.5,
          "rollingSd": 41.781574886545386,
          "floorP25": 91.5,
          "ceilingP75": 146.75,
          "n": 12,
          "band": null,
          "state": "ready"
        }
      ],
      "gameLog": [
        {
          "eventId": "56e233da2c77b9d81506e5a412d1d43b",
          "date": "2026-06-21T13:17:36.842+00:00",
          "value": 0,
          "line": 0.5,
          "hit": "miss",
          "opponent": null,
          "homeAway": null,
          "opponentLogoUrl": null,
          "note": null
        },
        {
          "eventId": "5133ad82356e44e66dee3264a7e15405",
          "date": "2026-09-03T04:31:49.211+00:00",
          "value": 0,
          "line": 0.5,
          "hit": "miss",
          "opponent": null,
          "homeAway": null,
          "opponentLogoUrl": null,
          "note": null
        }
      ],
      "gameChart": {
        "marketLine": 0.5,
        "projection": {
          "value": null,
          "n": 0,
          "state": "pending",
          "note": "opens when this sport passes the 95% player identity gate"
        },
        "notes": {
          "state": "building",
          "n": 0,
          "note": "injury listings not read for this player"
        }
      },
      "lineHistory": {
        "title": "Line history",
        "state": "building",
        "note": "opening, current and close prices not on the frame yet",
        "n": 13,
        "paywalled": true
      },
      "matchup": {
        "title": "Defence vs position",
        "state": "building",
        "note": "position and DvP not on the frame yet",
        "n": 0,
        "paywalled": true
      },
      "altLines": {
        "title": "Alternate lines",
        "state": "building",
        "note": "book prices per rung not on the frame yet",
        "n": 13,
        "paywalled": false
      },
      "tabs": [
        {
          "key": "form",
          "label": "Form",
          "state": "ready"
        },
        {
          "key": "game-logs",
          "label": "Game logs",
          "state": "ready"
        },
        {
          "key": "splits",
          "label": "Splits",
          "state": "building"
        }
      ],
      "insights": [
        {
          "key": "coverage",
          "text": "85 value rows across 16 fixtures and 10 markets",
          "n": 85,
          "score": 85
        },
        {
          "key": "overall-hit",
          "text": "Cleared the line in 15% of 13 graded props",
          "n": 13,
          "score": 13
        },
        {
          "key": "sd:runs",
          "text": "Runs: rolling-5 SD 3.9 against a mean of 10.8",
          "n": 12,
          "score": 12
        }
      ],
      "quickReads": {
        "reads": [],
        "note": "Not enough data: best sample n=0, rules need n≥8",
        "bestN": 0
      },
      "formTable": {
        "windows": [
          {
            "key": "l3",
            "label": "L3",
            "games": 3,
            "mean": 27.333333333333332,
            "median": 2,
            "band": null
          },
          {
            "key": "l5",
            "label": "L5",
            "games": 5,
            "mean": 38.8,
            "median": 5,
            "band": null
          },
          {
            "key": "l10",
            "label": "L10",
            "games": 10,
            "mean": 29.7,
            "median": 7,
            "band": null
          },
          {
            "key": "l15",
            "label": "L15",
            "games": 15,
            "mean": 32.733333333333334,
            "median": 9,
            "band": null
          },
          {
            "key": "season",
            "label": "Season",
            "games": 85,
            "mean": 30.235294117647058,
            "median": 8,
            "band": {
              "low": 0.043258178358081205,
              "point": 0.15384615384615385,
              "high": 0.4223463101948253,
              "n": 13,
              "hits": 2
            }
          }
        ],
        "byStat": {
          "runs": {
            "l3": 9.333333333333334,
            "l5": 9.8,
            "l10": 10.6,
            "l15": 10.833333333333334,
            "season": 10.833333333333334
          },
          "run_metres": {
            "l3": 117.33333333333333,
            "l5": 110.2,
            "l10": 120.3,
            "l15": 116.16666666666667,
            "season": 116.16666666666667
          },
          "tackles": {
            "l3": 9.666666666666666,
            "l5": 9.6,
            "l10": 8.7,
            "l15": 8.666666666666666,
            "season": 8.666666666666666
          },
          "try_assists": {
            "l3": 0.6666666666666666,
            "l5": 0.4,
            "l10": 0.8,
            "l15": 0.8333333333333334,
            "season": 0.8333333333333334
          },
          "line_breaks": {
            "l3": 1.3333333333333333,
            "l5": 1,
            "l10": 1,
            "l15": 0.9166666666666666,
            "season": 0.9166666666666666
          }
        },
        "medianRow": {
          "runs": 11,
          "run_metres": 108.5,
          "tackles": 8.5,
          "try_assists": 1,
          "line_breaks": 1
        },
        "sdRow": {
          "runs": 2.588435821108957,
          "run_metres": 34.17162565638339,
          "tackles": 3.8470768123342687,
          "try_assists": 0.894427190999916,
          "line_breaks": 0.7071067811865476
        },
        "state": "ready",
        "note": ""
      },
      "splits": {
        "title": "Splits",
        "state": "building",
        "note": "home/away, started/sub and minutes bands not on the frame yet",
        "n": 0,
        "paywalled": false
      },
      "splitRows": [],
      "similar": {
        "title": "Similar players",
        "state": "pending",
        "note": "similarity model not built",
        "n": 0,
        "paywalled": false
      },
      "compare": {
        "state": "ready",
        "note": "Compare any two players on the same stat and filters"
      }
    }
  ]
}
GET/api/v1/players/{sport}/{id}/frameAPI plan

One player-season frame with an open teaser: keyless and free-tier callers get the summary, coverage, markets and game-log headline, paid keys get the rows too.

Tier detail: api (paid depth; free and keyless get the teaser)

Cheapest route in the family at 1 credit and the only one with an unauthenticated path. Keyless and teaser responses set `data` to a single **object** (not a one-element array), `teaser: true` and `meta.teaser: true`, ship a shared-cache header (`Cache-Control: public, s-maxage=600, stale-while-revalidate=600`) and carry no credit or rate-limit headers; the keyed response is `data: [...]` with `teaser: false`, `Cache-Control: private, no-store`, `x-credits-cost: 1` and `x-ratelimit-tier`. The teaser is a real redaction, not a page size: the same call returns `coverage.rows: 85`, `markets` 10 and `game_log` 4 with `rows: []` keyless, and 85 rows with a key. `frame_status` is `ready`, `empty` or `unavailable` - an unknown-but-well-formed player is `empty` at 200. Unknown sport is still a 404 keyless. The example is the live, unmodified paid response for `rugbyleague_nrl` / `latrell_mitchell`, trimmed; it names no data source.

Path parameters
NameTypeRequiredDefaultDescription
sportstringYes–Sport key, lower-cased before matching. An unknown key is a 404 (`unknown sport`), and a key that exists but has no frame contract yet answers **200** with a single `{status: 'pending', sport_key, sport_state, reason}` row instead of data.
idstringYes–Player slug from any slug-carrying route (e.g. `meta.player_slug` on `/api/v1/insights/leaders`), lower-cased before matching and validated against `^[a-z0-9][a-z0-9_-]{0,119}$`. A slug that fails that pattern is a 400 (`invalid player id`). A well-formed slug for a player we have no rows for is **not** a 404 - see the notes.
Query parameters
NameTypeRequiredDefaultDescription
X-API-KeystringNo–Optional - this is the one route in the family that answers without a key. Keyless is the open teaser, a `free` key is the same teaser, an `api` key gets full depth.
seasonstringNo–Four-digit year, default the current calendar year. This filter is real, not decorative: `rugbyleague_nrl` / `latrell_mitchell` returns 85 rows, 13 graded and 10 markets for 2026 but 66 rows, 0 graded and 6 markets for 2025. Anything that is not a 4-digit year is a 400 (`season must be a 4-digit year`).
Example request
curl \
  -H "X-API-Key: YOUR_KEY" \
  "https://krokodds.com.au/api/v1/players/aussierules_afl/abc123/frame?X-API-Key=value"
Example response
{
  "success": true,
  "data": [
    {
      "frame_status": "ready",
      "sport_key": "rugbyleague_nrl",
      "sport_state": "configured",
      "sport_reason": null,
      "player_slug": "latrell_mitchell",
      "player_name": "Latrell Mitchell",
      "season": 2026,
      "built_at": "2026-10-22T07:40:35.959Z",
      "min_sample": 8,
      "summary": {
        "headline": "Latrell Mitchell: NRL player profile",
        "latest": "Latest line: Try Scorer Anytime 0.5 on 21 Sept 2026, result 0 (miss).",
        "projection": "Projection: building (n=0).",
        "coverage": "2026 season: 13 graded lines across 16 games."
      },
      "coverage": {
        "rows": 85,
        "stat_value": 85,
        "graded": 13,
        "events": 16,
        "markets": 10
      },
      "primary_market_key": "player_try_scorer_anytime",
      "markets": [
        {
          "market_key": "player_points_milestones",
          "label": "Points Milestones"
        },
        {
          "market_key": "player_tries_milestones",
          "label": "Tries Milestones"
        }
      ],
      "game_log": [
        {
          "event_id": "56e233da2c77b9d81506e5a412d1d43b",
          "date": "2026-06-21T13:17:36.842+00:00",
          "value": 0,
          "line": 0.5,
          "hit": "miss",
          "opponent": null,
          "home_away": null
        },
        {
          "event_id": "5133ad82356e44e66dee3264a7e15405",
          "date": "2026-09-03T04:31:49.211+00:00",
          "value": 0,
          "line": 0.5,
          "hit": "miss",
          "opponent": null,
          "home_away": null
        }
      ],
      "game_log_locked": 0,
      "teaser": false,
      "season_stats": [
        {
          "stat_key": "runs",
          "label": "Runs",
          "mean": 10.833333333333334,
          "median": 11,
          "rolling_sd": 3.8987177379235853,
          "floor_p25": 9,
          "ceiling_p75": 13.25,
          "n": 12,
          "band": null,
          "state": "ready"
        },
        {
          "stat_key": "run_metres",
          "label": "Run Metres",
          "mean": 116.16666666666667,
          "median": 108.5,
          "rolling_sd": 41.781574886545386,
          "floor_p25": 91.5,
          "ceiling_p75": 146.75,
          "n": 12,
          "band": null,
          "state": "ready"
        }
      ],
      "form_table": {
        "windows": [
          {
            "key": "l3",
            "label": "L3",
            "games": 3,
            "mean": 27.333333333333332,
            "median": 2,
            "band": null
          },
          {
            "key": "l5",
            "label": "L5",
            "games": 5,
            "mean": 38.8,
            "median": 5,
            "band": null
          },
          {
            "key": "l10",
            "label": "L10",
            "games": 10,
            "mean": 29.7,
            "median": 7,
            "band": null
          },
          {
            "key": "l15",
            "label": "L15",
            "games": 15,
            "mean": 32.733333333333334,
            "median": 9,
            "band": null
          },
          {
            "key": "season",
            "label": "Season",
            "games": 85,
            "mean": 30.235294117647058,
            "median": 8,
            "band": {
              "low": 0.043258178358081205,
              "point": 0.15384615384615385,
              "high": 0.4223463101948253,
              "n": 13,
              "hits": 2
            }
          }
        ],
        "by_stat": {
          "runs": {
            "l3": 9.333333333333334,
            "l5": 9.8,
            "l10": 10.6,
            "l15": 10.833333333333334,
            "season": 10.833333333333334
          },
          "run_metres": {
            "l3": 117.33333333333333,
            "l5": 110.2,
            "l10": 120.3,
            "l15": 116.16666666666667,
            "season": 116.16666666666667
          },
          "tackles": {
            "l3": 9.666666666666666,
            "l5": 9.6,
            "l10": 8.7,
            "l15": 8.666666666666666,
            "season": 8.666666666666666
          },
          "try_assists": {
            "l3": 0.6666666666666666,
            "l5": 0.4,
            "l10": 0.8,
            "l15": 0.8333333333333334,
            "season": 0.8333333333333334
          },
          "line_breaks": {
            "l3": 1.3333333333333333,
            "l5": 1,
            "l10": 1,
            "l15": 0.9166666666666666,
            "season": 0.9166666666666666
          }
        },
        "median_row": {
          "runs": 11,
          "run_metres": 108.5,
          "tackles": 8.5,
          "try_assists": 1,
          "line_breaks": 1
        },
        "sd_row": {
          "runs": 2.588435821108957,
          "run_metres": 34.17162565638339,
          "tackles": 3.8470768123342687,
          "try_assists": 0.894427190999916,
          "line_breaks": 0.7071067811865476
        },
        "state": "ready",
        "note": ""
      },
      "volatility": {
        "rolling_sd": 3.8987177379235853,
        "median": 11,
        "mean": 10.833333333333334,
        "label": "volatile",
        "n": 12,
        "state": "ready"
      },
      "splits": [],
      "rows": [
        {
          "sport_key": "rugbyleague_nrl",
          "event_id": "3e6260ba3b7217715e6c4011f56c28cf",
          "player_slug": "latrell_mitchell",
          "player_name": "Latrell Mitchell",
          "commence_time": "2026-09-20T22:58:19.577+00:00",
          "opponent": null,
          "venue": null,
          "home_away": null,
          "market_key": "player_try_scorer_anytime",
          "stat_key": null,
          "stat_value": 0,
          "line": 0.5,
          "side": "over",
          "odds": null,
          "bookmaker": "backfill:ppr",
          "hit_result": "miss",
          "computed_at": "2026-10-11T00:05:38.669+00:00"
        },
        {
          "sport_key": "rugbyleague_nrl",
          "event_id": "3e6260ba3b7217715e6c4011f56c28cf",
          "player_slug": "latrell_mitchell",
          "player_name": "Latrell Mitchell",
          "commence_time": "2026-09-20T22:58:19.577+00:00",
          "opponent": null,
          "venue": null,
          "home_away": null,
          "market_key": "player_try_scorer_over",
          "stat_key": null,
          "stat_value": 0,
          "line": 2.5,
          "side": "over",
          "odds": null,
          "bookmaker": "backfill:ppr",
          "hit_result": "miss",
          "computed_at": "2026-10-11T00:05:38.669+00:00"
        }
      ]
    }
  ]
}
GET/api/v1/players/{sport}/{id}/hit-ratesAPI plan

Overall and per-market hit rates for one player-season, plus a per-stat season band and the sample size behind each number.

Tier detail: api (paid)

The small, stable shape to poll when you only want numbers: `overall` (`value` as a percentage string, `n`, `state`), a `markets` array and `season_stats` with a `label`, `n`, `state` and `band` per stat. `min_sample` (8 on this route) is the threshold below which `state` stays `building` and `value` is null - read `n` and `state` rather than treating null as zero. `primary_market_key` is the market the route would headline and is null when nothing has cleared the sample. Same 5-credit cost, 600s cache and 400/404/401 behaviour as `/profile`, including the 200-with-empty-body answer for an unknown player slug. The example is the live, unmodified response for `rugbyleague_nrl` / `latrell_mitchell`, trimmed to two markets; it names no data source.

Path parameters
NameTypeRequiredDefaultDescription
sportstringYes–Sport key, lower-cased before matching. An unknown key is a 404 (`unknown sport`), and a key that exists but has no frame contract yet answers **200** with a single `{status: 'pending', sport_key, sport_state, reason}` row instead of data.
idstringYes–Player slug from any slug-carrying route (e.g. `meta.player_slug` on `/api/v1/insights/leaders`), lower-cased before matching and validated against `^[a-z0-9][a-z0-9_-]{0,119}$`. A slug that fails that pattern is a 400 (`invalid player id`). A well-formed slug for a player we have no rows for is **not** a 404 - see the notes.
Query parameters
NameTypeRequiredDefaultDescription
X-API-KeystringYes–API key. Without one the route is a 401 (`API key required`).
seasonstringNo–Four-digit year, default the current calendar year. This filter is real, not decorative: `rugbyleague_nrl` / `latrell_mitchell` returns 85 rows, 13 graded and 10 markets for 2026 but 66 rows, 0 graded and 6 markets for 2025. Anything that is not a 4-digit year is a 400 (`season must be a 4-digit year`).
Example request
curl \
  -H "X-API-Key: YOUR_KEY" \
  "https://krokodds.com.au/api/v1/players/aussierules_afl/abc123/hit-rates?X-API-Key=value"
Example response
{
  "success": true,
  "data": [
    {
      "sport_key": "rugbyleague_nrl",
      "player_slug": "latrell_mitchell",
      "player_name": "Latrell Mitchell",
      "season": 2026,
      "min_sample": 8,
      "primary_market_key": "player_try_scorer_anytime",
      "overall": {
        "value": "15%",
        "n": 13,
        "state": "ready"
      },
      "markets": [
        {
          "market_key": "player_points_milestones",
          "label": "Points Milestones",
          "direction": "over",
          "consensus_line": 9.5,
          "consensus_n": 2,
          "hit": {
            "low": 0.09453120573423071,
            "point": 0.5,
            "high": 0.9054687942657693,
            "n": 2,
            "hits": 1
          }
        },
        {
          "market_key": "player_tries_milestones",
          "label": "Tries Milestones",
          "direction": "over",
          "consensus_line": 2.5,
          "consensus_n": 3,
          "hit": {
            "low": 4.8683609171202235e-17,
            "point": 0,
            "high": 0.5614970317550455,
            "n": 3,
            "hits": 0
          }
        }
      ],
      "season_stats": [
        {
          "stat_key": "runs",
          "label": "Runs",
          "n": 12,
          "state": "ready",
          "band": null
        },
        {
          "stat_key": "run_metres",
          "label": "Run Metres",
          "n": 12,
          "state": "ready",
          "band": null
        },
        {
          "stat_key": "tackles",
          "label": "Tackles",
          "n": 12,
          "state": "ready",
          "band": null
        }
      ]
    }
  ]
}
GET/api/v1/players/{sport}/rankerAPI plan

The Finder board for one sport as JSON: upcoming priced player props ranked per player, with the per-window hit-rate columns, the consensus line, the open-line move and the sport's quick-reads block.

Tier detail: api (paid)

Paid, 5 credits, 600s cache, same 401/404 behaviour as `/profile`. One data row: the whole board for the sport. `rows` is the priced-prop list - one entry per player x market x line x book, each carrying `odds`, `implied`, a `consensus` line with a book count, the `season` / `l5` / `l10` / `vsOpp` / `atVenue` hit-rate blocks (`low` / `point` / `high`, `n`, `hits`), a `projection`, a `dvp` block and a `valueScore`. `statKeys` and `bookmakers` say what the board covers; `players.scored` against `players.total` is how many players were ranked, and `notes` carries the truncation line when it bites (`soccer_epl` measured 2026-10-24: 260 rows, 856 quotes read, 60 of 209 players scored, `Scored the first 60 of 209 players by kick-off.`). `joins` and `quickReads` are coverage diagnostics - `joins` counts how many rows picked up the roster / open-line / DvP join, `quickReads.note` reports the sample size the rules need. Empty is a normal answer, not an error: a configured sport with nothing priced in the window returns `rows: []` with `notes: ['No priced props in the next 7 days.']` (`afl` and `rugbyleague_nrl`, measured 2026-10-24). The board is the upcoming window only - it takes no filters, and a `season` parameter is accepted and ignored. No quoted example here on purpose: every live `joins` string names an internal table, so the shape is described above rather than pasted.

Path parameters
NameTypeRequiredDefaultDescription
sportstringYes–Sport key, lower-cased before matching. An unknown key is a 404 (`unknown sport`), and a key that exists but has no frame contract yet answers **200** with a single `{status: 'pending', sport_key, sport_state, reason}` row instead of a board.
Query parameters
NameTypeRequiredDefaultDescription
X-API-KeystringYes–API key. Without one the route is a 401 (`API key required`).
Example request
curl \
  -H "X-API-Key: YOUR_KEY" \
  "https://krokodds.com.au/api/v1/players/aussierules_afl/ranker?X-API-Key=value"

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.

Racing23 endpoints

The full Australian racing stack — thoroughbred, harness and greyhound. Meetings and runner fields, form, sectionals, ratings, market movers, exchange arbs, tote pools and results.

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.

Prediction Markets3 endpoints

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