Big Ball Sports API API

Unified sports data API (REST + GraphQL + WebSocket) aggregating 19+

Operations 163

GET /v1/sports List supported sports
GET /v1/leagues List leagues, optionally filtered by sport + country
GET /v1/leagues/{id}/top-scorers Domestic-league top scorers (season aggregates)
GET /v1/leagues/{id}/xg-leaders Domestic-league expected-goals leaderboard (season aggregates)
GET /v1/matches List matches with filters
GET /v1/scores Live scores for one sport or league
GET /v1/matches/{id} Single match
GET /v1/matches/{id}/odds Odds for a match (aggregated across bookmakers)
GET /v1/matches/{id}/events Play-by-play / match events
GET /v1/matches/{id}/statistics Per-team post-match team statistics (soccer)
GET /v1/teams/{id} Team profile + stored record
GET /v1/players/{id} Player profile + career stats
GET /v1/players/{id}/club-form Football player club-season form
GET /v1/players/{id}/stats Player stats for a season + league
GET /v1/players/{id}/gamelog NBA player game log (flat per-game box lines)
GET /v1/standings League standings table
GET /v1/coverage What we hold, per sport and league (the discovery catalogue)
GET /v1/as-of/odds The odds board as it stood at an instant #
GET /v1/as-of/player-props The player-prop board as it stood at an instant #
GET /v1/as-of/injuries Availability designations as they stood at an instant #
GET /v1/as-of/coverage Which point-in-time windows we actually observed, per relation #
GET /v1/injuries Active injury reports
GET /v1/webhooks List your registered webhooks
POST /v1/webhooks Register a webhook subscription (Edge+)
DELETE /v1/webhooks/{id} Deregister a webhook
POST /v1/webhooks/{id}/unpark Re-enable a parked webhook after fixing its endpoint
GET /v1/players List players (filterable)
GET /v1/teams/{id}/form Recent finished matches with derived result (W/D/L from this team's perspective)
GET /v1/teams/{id}/stats Aggregated team stats (W/D/L, goals, BTTS, O/U, form string)
GET /v1/teams/{id}/elo Team Elo rating, league rank, and league context (all rated leagues)
GET /v1/nba/games NBA games list (legacy v1 shape, 1946-2025)
GET /v1/ncaab/tournament NCAA tournament bracket (men's + women's)
GET /v1/nba/games/{id} Single NBA game with embedded linescore
GET /v1/nba/games/{id}/plays Play-by-play for an NBA game
GET /v1/nba/games/{id}/availability Who played in an NBA game (box-score derived availability)
GET /v1/nba/players/{id}/stats Career stats aggregated from NBA PBP (1996-2023)
GET /v1/nba/teams/{id}/season-averages NBA team season averages, by category
GET /v1/nba/players/{id}/season-averages NBA per-game season averages
GET /v1/nba/players/{id}/advanced-stats NBA advanced season stats and league ranks
GET /v1/nba/players/{id}/advanced-game-stats NBA advanced and tracking measures, per game
GET /v1/nba/players/{id}/contracts NBA player career contract totals (Edge and above)
GET /v1/nba/players/{id}/contract-seasons NBA player per-season cap hit and salary (Edge and above)
GET /v1/nfl/games NFL schedule rows (historical)
GET /v1/nfl/games/{id} Single NFL game by league-standard game id
GET /v1/nfl/games/{id}/plays NFL play-by-play with EPA + Win Probability
GET /v1/nfl/injuries NFL injuries - weekly report, practice participation and roster status
GET /v1/nfl/depth-charts NFL depth charts, current or as at a past instant
GET /v1/nfl/team-stats NFL team-week totals - offence, defence, kicking, returns and fumbles
GET /v1/nfl/rosters NFL season rosters
GET /v1/nfl/players/{id}/stats Weekly NFL player stats
GET /v1/live-stats/{sport}/{matchId}/players Live per-player boxscore
GET /v1/live-stats/american_football/{matchId}/plays Live NFL/NCAAF play-by-play
GET /v1/f1/sessions/{id}/results Finishing positions for a Formula 1 session
GET /v1/games Cross-sport games strip with live scores + odds
GET /v1/games/{id}/books Multi-book line shopping for one match (sport-agnostic)
GET /v1/games/{id}/line-movement 5-minute snapshot timeline of moneyline + spread + total
GET /v1/wc2026/standings WC2026 group-stage standings with Elo + advancement probability
GET /v1/wc2026/matches All 72 WC2026 group-stage matches with Elo prediction + odds
GET /v1/wc2026/matches/{id} WC2026 match-detail intelligence endpoint (lineups, shots+xG, momentum, etc.)
GET /v1/wc2026/teams/{id} WC2026 nation profile (Elo, rank, recent form, group matches, squad)
GET /v1/nba/games/{id}/matchup NBA matchup intelligence endpoint (season aggregates, last-5, rosters, H2H)
GET /v1/nba/games/{id}/lineups NBA game lineups (home/away players with position + starter flag)
GET /v1/nba/games/{id}/box NBA per-game player box score (home/away, full stat line)
GET /v1/nba/players/{id}/props NBA player prop hit-rate + current line across books
GET /v1/nhl/players/{player_id}/props NHL player prop hit-rate + latest line across books
GET /v1/nhl/props/freshness Dataset-wide freshness for NHL player props
GET /v1/intelligence/value-props Player-prop value finder (model hit-rate vs market) with backtest
GET /v1/intelligence/value-finder Match-level value finder (Elo win prob vs implied odds)
GET /v1/intelligence/trending Hot/cold player movers (last-5 vs season average)
GET /v1/odds Tournament-wide odds list (WC2026)
GET /v1/predictions Match predictions (Elo win/draw/loss + value bet)
GET /v1/odds/historical Historical odds snapshots for a match (CLV)
GET /v1/odds/player-props/historical NHL player-prop archive: every stored observation for a game or a player
GET /v1/odds/player-props/coverage What the NHL player-prop archive holds, per season
GET /v1/odds/closing-lines Closing lines across a sport and date range
GET /v1/odds/divergences Cross-provider odds divergences (two-source verification)
GET /v1/wc2026/golden-boot WC2026 Golden Boot leaderboard
GET /v1/wc2026/leaders WC2026 top scorers (with games played)
GET /v1/wc2026/stat-leaders WC2026 stat leaders (aggregated from our match stats)
GET /v1/wc2026/players/{id}/tournament One player's World Cup so far (aggregate + event timeline)
GET /v1/wc2026/group/{group}/advancement WC2026 group advancement probabilities (Monte Carlo)
GET /v1/wc2026/teams/{id}/squad WC2026 team squad (26 players)
GET /v1/teams/{id}/world-cup-history National team World Cup history (2002-2022)
GET /v1/teams/{id}/elo/history Team Elo rating history (trend line)
GET /v1/players/{id}/career Player career averages (season-by-season)
GET /v1/players/{id}/transfers Player transfer history (football)
GET /v1/players/{id}/trophies Player trophy history (football)
GET /v1/teams/{id}/playoffs NBA team playoff record (per season)
GET /v1/wc2026/groups WC2026 group tables (all 12 groups)
GET /v1/wc2026/matches/{game_id}/line-movement WC2026 match line movement (odds history)
GET /v1/cricket/matches List cricket matches
GET /v1/cricket/matches/{id} Single cricket match
GET /v1/cricket/matches/{id}/deliveries Cricket ball-by-ball deliveries
GET /v1/cricket/matches/{id}/scorecard Cricket match scorecard
GET /v1/cricket/matches/{id}/state Cricket live match state
GET /v1/cricket/series List cricket series
GET /v1/cricket/players/{id} Cricket player career profile
GET /v1/fight-cards List fight cards (MMA / boxing)
GET /v1/fight-cards/{id} Single fight card
GET /v1/bouts/{id} Single bout
GET /v1/athletes/{id} Single athlete (fighter / boxer)
GET /v1/athletes/{id}/record Athlete fight record
GET /v1/stored/matches List stored matches (DB-backed)
GET /v1/stored/matches/{id} Single stored match with latest odds
GET /v1/stored/matches/{id}/h2h Head-to-head history for a match's team pair
GET /v1/stored/matches/{id}/special-teams-toi Per-player special-teams time on ice for a match
GET /v1/stored/matches/{id}/lineups Stored lineups for a match
GET /v1/stored/matches/{id}/plays Play-by-play for a stored match
GET /v1/stored/matches/{id}/stats Stored team + player stats for a match
GET /v1/stored/players List stored players (DB-backed)
GET /v1/stored/standings Stored standings, grouped by league
GET /v1/stored/sports Sports present in the stored match table, with counts
GET /v1/assets/{id} Fetch an image we host
GET /v1/teams List teams
GET /v1/teams/{id}/matches Matches for a team
GET /v1/teams/{id}/ats Against-the-spread record
GET /v1/teams/{id}/ats-record Against-the-spread record (alias of /v1/teams/{id}/ats)
GET /v1/teams/{id}/trends Betting + performance trends for a team
GET /v1/teams/{id}/schedule-context Rest + schedule context for a team
GET /v1/teams/{id}/h2h-intelligence Head-to-head intelligence vs one opponent
GET /v1/teams/{id}/season Team season summary
GET /v1/teams/{id}/depth-chart Depth chart inferred from playing time
GET /v1/players/{id}/profile Extended player profile
GET /v1/players/{id}/game-log Filterable per-game log for a player
GET /v1/players/{id}/rolling-stats Rolling averages over a window
GET /v1/players/{id}/season-projection Season projection for a player
GET /v1/players/{id}/streak Active streaks for a player
GET /v1/players/{id}/birthday-splits Performance splits around a player's birthday
GET /v1/players/{id}/tier Player quality tier
GET /v1/nba/leaders NBA statistical leaders
GET /v1/nba/players/{id}/splits NBA player splits
GET /v1/nhl/games/{game_id}/matchup NHL game matchup
GET /v1/nhl/games/{game_id}/plays NHL play-by-play
GET /v1/nhl/games/{game_id}/box-scores NHL per-game player box score
GET /v1/nhl/leaders NHL statistical leaders
GET /v1/nhl/team-leaders NHL team rate leaders
GET /v1/nhl/xg-leaders NHL skater expected-goals leaderboard (our own model)
GET /v1/nhl/goalies/gsax NHL goalie goals-saved-above-expected (our own model)
GET /v1/nhl/skaters/xg-splits NHL skater expected goals by on-ice strength, 2014-2025 (Edge)
GET /v1/nhl/goalies/xg-splits NHL goalie expected goals by strength and shot danger, 2014-2025 (Edge)
GET /v1/mlb/leaders MLB batting/pitching season leaderboard
GET /v1/nfl/leaders NFL season leaderboard
GET /v1/nfl/surfaces Catalogue of available NFL snapshot surfaces
GET /v1/nfl/surfaces/{surface} Rows of one NFL snapshot surface
GET /v1/ncaaf/leaders NCAAF season fantasy/statistical leaderboard
GET /v1/wc2026/matches/{game_id}/live-probability Live win probability for a World Cup match
GET /v1/formulas/wc2026/group-standings World Cup group-standings formula
GET /v1/formulas/wc2026/progression World Cup progression formula
GET /v1/nfl/players/{id}/game-log NFL player game log for a season
GET /v1/matches/{id}/weather Conditions at kickoff
GET /v1/leagues/{id} Single league by canonical key
GET /v1/health Service health
GET /v1/user/me Your account
GET /v1/athletes List boxing and MMA athlete profiles
GET /v1/players/{id}/shots NBA player shot coordinates
GET /v1/nba/teams/{id}/lineup-combos NBA five-player starting lineup combinations
GET /v1/wc2026/groups/{group} One World Cup 2026 group table and advancement model
GET /v1/predictions/{id} Latest prediction for a match
GET /v1/predictions/match/{id} Latest prediction for a match (original URL)
GET /v1/players/{id}/injury Current injury status for a player
GET /v1/players/{id}/vs-team/{teamId} Player history against one opponent
GET /v1/nba/players/{id}/season NBA box-score derived season metrics
GET /v1/usage Your usage + remaining quota

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/bigballsdata-com:big-ball-sports-api-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

bigballsdata-com-api-openapi.json Raw ↑
{
  "openapi": "3.1.0",
  "info": {
    "title": "Big Ball Sports API",
    "description": "Unified sports data API (REST + GraphQL + WebSocket) aggregating 19+\nfree-tier upstream sources and 10 MCP scrapers behind a Stripe-style\ngateway. Every response carries a confidence score and source attribution\nso callers can render appropriate caveats.\n\n## Pagination\n\nList endpoints page with `?limit=` and `?offset=`. `?page=N` is an alias\nfor `?offset=(N-1)*limit`, resolved against that endpoint's own `limit`\ndefault, so `?page=2` and `?offset=<limit>` are the same request. When both\nare sent, `?offset=` wins; it is the more specific instruction.\n\nEndpoints that return a single page (leaderboards, one team's form, one\nplayer's stat line) document a `limit` and no `offset`. On those, `?page=`\nis **rejected with a 400** naming `?limit=` rather than accepted and\nignored, so a paging attempt can never silently look like it worked.\n",
    "version": "1.0.0",
    "contact": {
      "email": "support@bigballsdata.com"
    }
  },
  "servers": [
    {
      "url": "https://api.bigballsdata.com",
      "description": "Production"
    },
    {
      "url": "https://bbsgateway-production.up.railway.app",
      "description": "Production (Railway direct, fallback)"
    },
    {
      "url": "http://localhost:3000",
      "description": "Local development"
    }
  ],
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "apiKey",
        "in": "header",
        "name": "x-api-key",
        "description": "Big Ball Sports API key. Or pass via `Authorization: Bearer <key>`."
      },
      "bearer": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "bbs_<env>_<32hex>"
      }
    },
    "parameters": {
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": false,
        "schema": {
          "type": "string",
          "maxLength": 255
        },
        "description": "Stripe-style idempotency token. Retries with the same key within\n24h replay the original response without re-running the handler.\n"
      }
    },
    "responses": {
      "RateLimited": {
        "description": "Rate limited (`429`). Two causes share `error.code: rate_limited` and are\ntold apart by `error.message` and headers:\n  - **Quota exhaustion** (per-minute or per-day cap). The envelope is\n    enriched: `suggested_fix` names the next tier and its cap, and\n    `meta.current_usage` + `meta.upgrade_path` carry live usage and the\n    upgrade target. `X-RateLimit-Limit/-Remaining/-Reset` are present.\n  - **4xx circuit breaker** (sustained high 4xx rate: malformed\n    requests, or ignoring Retry-After and hammering past quota; key in\n    cooldown). Carries `X-RateLimit-4xx-Cooldown`; no `upgrade_path`.\nAdditive: existing parsers keying on `error.code`, `error.message`,\n`docs_url`, `support`, `meta.request_id` are unaffected.\n`X-RateLimit-Limit/-Remaining/-Reset` are returned on every authenticated\nresponse (2xx/4xx/5xx), not only on 429s, so clients can self-throttle.\n",
        "headers": {
          "Retry-After": {
            "description": "Seconds until the limit (or 4xx cooldown) resets.",
            "schema": {
              "type": "integer"
            }
          },
          "X-RateLimit-Limit": {
            "description": "Effective request cap (tighter bucket).",
            "schema": {
              "type": "string"
            }
          },
          "X-RateLimit-Remaining": {
            "description": "Requests remaining in the tighter bucket.",
            "schema": {
              "type": "string"
            }
          },
          "X-RateLimit-Reset": {
            "description": "Unix seconds at which the tighter bucket resets.",
            "schema": {
              "type": "integer"
            }
          },
          "X-RateLimit-4xx-Cooldown": {
            "description": "Seconds remaining on the 4xx breaker cooldown (only on breaker 429s).",
            "schema": {
              "type": "integer"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "error": {
                  "type": "object",
                  "properties": {
                    "code": {
                      "type": "string",
                      "example": "rate_limited"
                    },
                    "message": {
                      "type": "string",
                      "example": "Daily quota exhausted (250 of 250 requests used)"
                    }
                  }
                },
                "suggested_fix": {
                  "type": "string",
                  "description": "Quota 429 only, tier-aware upgrade guidance.",
                  "example": "Pace requests using the Retry-After and X-RateLimit-* response headers. Handling guide: https://bigballsdata.com/docs/rate-limits. If you're regularly at this ceiling, the solo plan is $19/month and raises your daily limit to 10,000/day: https://bigballsdata.com/dashboard/billing?plan=solo."
                },
                "docs_url": {
                  "type": "string"
                },
                "support": {
                  "type": "object"
                },
                "meta": {
                  "type": "object",
                  "properties": {
                    "request_id": {
                      "type": "string"
                    },
                    "timestamp": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "limiting_bucket": {
                      "type": "string",
                      "enum": [
                        "minute",
                        "day"
                      ],
                      "description": "Quota 429 only. WHICH of the two buckets refused this\nrequest, and therefore which cap `error.message` and\n`suggested_fix` are about. `current_usage` reports both\nbuckets without saying which one was binding; this says.\n\nUse it for backoff: a `minute` refusal clears within\nseconds, a `day` refusal not until the UTC rollover.\n`Retry-After` already carries the right wait in seconds.\n",
                      "example": "minute"
                    },
                    "current_usage": {
                      "type": "object",
                      "description": "Quota 429 only, live per-bucket usage.",
                      "properties": {
                        "minute": {
                          "type": "object",
                          "properties": {
                            "used": {
                              "type": "integer"
                            },
                            "limit": {
                              "type": "integer"
                            },
                            "resets_at": {
                              "type": "string",
                              "format": "date-time"
                            }
                          }
                        },
                        "day": {
                          "type": "object",
                          "properties": {
                            "used": {
                              "type": "integer"
                            },
                            "limit": {
                              "type": "integer"
                            },
                            "resets_at": {
                              "type": "string",
                              "format": "date-time"
                            }
                          }
                        }
                      }
                    },
                    "upgrade_path": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/UpgradePath"
                        }
                      ],
                      "description": "The upgrade target for THIS 429: the next tier up the\nladder, because the caller's problem is volume.\n`recommended_tier` is null at the top self-serve tier.\n"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "ServiceUnavailable": {
        "description": "Service temporarily unavailable (`503`). Returned when a backing query\nexceeds the endpoint's `statement_timeout` (Postgres cancels it) instead\nof holding the connection for seconds. Transient under heavy load;\nretry after a short delay. Standard error envelope with\n`error.code = upstream_unavailable`.\n",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "error": {
                  "type": "object",
                  "properties": {
                    "code": {
                      "type": "string",
                      "example": "upstream_unavailable"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                },
                "docs_url": {
                  "type": "string"
                },
                "support": {
                  "type": "object"
                },
                "meta": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "schemas": {
      "NbaBoxScoreEntry": {
        "type": "object",
        "description": "One player's line in an NBA game box score. Counting stats are integers and percentages are decimal strings as stored. `minutes` is WHOLE minutes, rounded; `seconds_played` carries the exact total when the feed supplies seconds and is null when it supplied only a whole minute. Corrected 2026-10-23: `minutes` was declared a string here, but the column is a smallint and the served value has always been a JSON number (verified against production). Rows written before that date read `seconds_played` null and carry a `minutes` value that was truncated rather than rounded, so it can read one minute low until the game is re-ingested.",
        "properties": {
          "player_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "Null when the upstream player is not yet bridged to a BigBalls player"
          },
          "name": {
            "type": "string",
            "nullable": true
          },
          "starter": {
            "type": "boolean",
            "nullable": true,
            "description": "Null for games with no pre-tip lineup coverage"
          },
          "minutes": {
            "type": "integer",
            "nullable": true,
            "description": "Whole minutes played, ROUNDED (half up) from the exact value. Agrees with /v1/stored/matches/{id}/stats, which is authoritative for minutes."
          },
          "seconds_played": {
            "type": "integer",
            "nullable": true,
            "description": "Exact total seconds played, as supplied. Null means the feed gave a whole minute and nothing finer, NOT that the player played no seconds; it is never minutes x 60."
          },
          "points": {
            "type": "integer",
            "nullable": true
          },
          "rebounds": {
            "type": "integer",
            "nullable": true
          },
          "offensive_rebounds": {
            "type": "integer",
            "nullable": true
          },
          "defensive_rebounds": {
            "type": "integer",
            "nullable": true
          },
          "assists": {
            "type": "integer",
            "nullable": true
          },
          "steals": {
            "type": "integer",
            "nullable": true
          },
          "blocks": {
            "type": "integer",
            "nullable": true
          },
          "turnovers": {
            "type": "integer",
            "nullable": true
          },
          "fouls": {
            "type": "integer",
            "nullable": true
          },
          "plus_minus": {
            "type": "integer",
            "nullable": true
          },
          "field_goals_made": {
            "type": "integer",
            "nullable": true
          },
          "field_goals_attempted": {
            "type": "integer",
            "nullable": true
          },
          "field_goal_pct": {
            "type": "string",
            "nullable": true
          },
          "three_pointers_made": {
            "type": "integer",
            "nullable": true
          },
          "three_pointers_attempted": {
            "type": "integer",
            "nullable": true
          },
          "three_point_pct": {
            "type": "string",
            "nullable": true
          },
          "free_throws_made": {
            "type": "integer",
            "nullable": true
          },
          "free_throws_attempted": {
            "type": "integer",
            "nullable": true
          },
          "free_throw_pct": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "SingleMatchPrediction": {
        "type": "object",
        "description": "Latest stored prediction for one match. Check valid_until and expired before use.",
        "required": [
          "match_id",
          "prediction_type",
          "model_version",
          "valid_until",
          "expired",
          "generated_at"
        ],
        "properties": {
          "match_id": {
            "type": "string",
            "format": "uuid"
          },
          "prediction_type": {
            "type": "string",
            "enum": [
              "match_result",
              "value_prop"
            ]
          },
          "home_team": {
            "type": "string",
            "nullable": true
          },
          "away_team": {
            "type": "string",
            "nullable": true
          },
          "home_win_probability": {
            "type": "number",
            "nullable": true
          },
          "draw_probability": {
            "type": "number",
            "nullable": true
          },
          "away_win_probability": {
            "type": "number",
            "nullable": true
          },
          "home_elo": {
            "type": "number",
            "nullable": true
          },
          "away_elo": {
            "type": "number",
            "nullable": true
          },
          "model_version": {
            "type": "string"
          },
          "confidence": {
            "type": "number",
            "nullable": true
          },
          "valid_until": {
            "type": "string",
            "nullable": true,
            "format": "date-time"
          },
          "expired": {
            "type": "boolean",
            "nullable": true,
            "description": "Null means the prediction has no stated expiry."
          },
          "expired_days": {
            "type": "number",
            "nullable": true
          },
          "generated_at": {
            "type": "string",
            "format": "date-time"
          },
          "kickoff_utc": {
            "type": "string",
            "nullable": true,
            "format": "date-time"
          },
          "group": {
            "type": "string",
            "nullable": true
          },
          "value_bet": {
            "type": "boolean",
            "nullable": true
          },
          "best_side": {
            "type": "string",
            "nullable": true
          },
          "elo_prob": {
            "type": "number",
            "nullable": true
          },
          "market_prob": {
            "type": "number",
            "nullable": true
          },
          "edge": {
            "type": "number",
            "nullable": true
          }
        }
      },
      "SingleMatchPredictionResponse": {
        "type": "object",
        "required": [
          "data",
          "meta"
        ],
        "properties": {
          "data": {
            "$ref": "#/components/schemas/SingleMatchPrediction"
          },
          "meta": {
            "type": "object",
            "required": [
              "returned",
              "expired",
              "expiry_unknown",
              "as_of"
            ],
            "properties": {
              "returned": {
                "type": "integer",
                "enum": [
                  1
                ]
              },
              "expired": {
                "type": "integer"
              },
              "expiry_unknown": {
                "type": "integer"
              },
              "as_of": {
                "type": "string",
                "format": "date-time"
              },
              "stale": {
                "type": "boolean",
                "description": "Present when the prediction is expired."
              },
              "message": {
                "type": "string",
                "description": "Present when the prediction is expired."
              }
            }
          }
        }
      },
      "UnifiedNbaTeamGame": {
        "type": "object",
        "required": [
          "id",
          "archive_team_id",
          "name",
          "short_name",
          "logo_url"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Canonical team UUID."
          },
          "archive_team_id": {
            "type": "string",
            "nullable": true,
            "description": "Original archive participant ID; null on match-only rows."
          },
          "name": {
            "type": "string"
          },
          "short_name": {
            "type": "string",
            "nullable": true
          },
          "logo_url": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "UnifiedNbaMatch": {
        "type": "object",
        "description": "NBA's additive v1 match shape. It preserves the ordinary Match fields, adds archive identity/statistics, and preserves a date-only historical game as midnight UTC plus time_precision=date. The marker explicitly says that the time component is not a known tip-off.",
        "required": [
          "id",
          "sport",
          "league",
          "season",
          "game_date",
          "kickoff_utc",
          "time_precision",
          "status",
          "home",
          "away"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "sport": {
            "type": "string",
            "enum": [
              "basketball"
            ]
          },
          "league": {
            "type": "string",
            "enum": [
              "NBA"
            ]
          },
          "archive_game_id": {
            "type": "string",
            "nullable": true
          },
          "match_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "season": {
            "type": "integer"
          },
          "season_type": {
            "type": "string"
          },
          "game_date": {
            "type": "string",
            "format": "date"
          },
          "kickoff_utc": {
            "type": "string",
            "format": "date-time"
          },
          "time_precision": {
            "type": "string",
            "enum": [
              "date",
              "timestamp"
            ]
          },
          "status": {
            "$ref": "#/components/schemas/MatchStatus"
          },
          "home": {
            "$ref": "#/components/schemas/UnifiedNbaTeamGame"
          },
          "away": {
            "$ref": "#/components/schemas/UnifiedNbaTeamGame"
          },
          "score": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/MatchScore"
              },
              {
                "type": "null"
              }
            ]
          },
          "linescore": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Linescore"
              },
              {
                "type": "null"
              }
            ]
          },
          "attendance": {
            "type": [
              "integer",
              "null"
            ]
          },
          "broadcast": {
            "type": [
              "string",
              "null"
            ]
          },
          "round": {
            "type": [
              "string",
              "null"
            ]
          },
          "has_odds": {
            "type": "boolean"
          },
          "archive_stats": {
            "type": "object",
            "nullable": true,
            "additionalProperties": true
          }
        }
      },
      "NbaGameTeamBox": {
        "type": "object",
        "description": "One team's box score on an NBA game row. `oreb` and `dreb` are stored team totals and always satisfy `oreb + dreb = reb` when present; they are not derived from the play-by-play log.",
        "properties": {
          "team_id": {
            "type": "string",
            "nullable": true,
            "description": "Upstream numeric team id, stable across seasons. Not a BBS team UUID."
          },
          "abbreviation": {
            "type": "string",
            "nullable": true
          },
          "name": {
            "type": "string",
            "nullable": true
          },
          "matchup": {
            "type": "string",
            "nullable": true
          },
          "wl": {
            "type": "string",
            "nullable": true,
            "enum": [
              "W",
              "L",
              null
            ]
          },
          "pts": {
            "type": "integer",
            "nullable": true
          },
          "fgm": {
            "type": "integer",
            "nullable": true
          },
          "fga": {
            "type": "integer",
            "nullable": true
          },
          "fg_pct": {
            "oneOf": [
              {
                "type": "number"
              },
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Exact decimal ratio. PostgreSQL numeric values may serialize as JSON strings (for example, '0.455')."
          },
          "fg3m": {
            "type": "integer",
            "nullable": true
          },
          "fg3a": {
            "type": "integer",
            "nullable": true
          },
          "fg3_pct": {
            "oneOf": [
              {
                "type": "number"
              },
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Exact decimal ratio. PostgreSQL numeric values may serialize as a JSON string."
          },
          "ftm": {
            "type": "integer",
            "nullable": true
          },
          "fta": {
            "type": "integer",
            "nullable": true
          },
          "ft_pct": {
            "oneOf": [
              {
                "type": "number"
              },
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Exact decimal ratio. PostgreSQL numeric values may serialize as a JSON string."
          },
          "reb": {
            "type": "integer",
            "nullable": true,
            "description": "Total rebounds."
          },
          "oreb": {
            "type": "integer",
            "nullable": true,
            "description": "Offensive rebounds. Complete for the 1985-86 season onward; null for 1984-85 and earlier, where the split was not recorded. A 0 is a real zero, not a missing value."
          },
          "dreb": {
            "type": "integer",
            "nullable": true,
            "description": "Defensive rebounds. Same coverage boundary as `oreb`."
          },
          "ast": {
            "type": "integer",
            "nullable": true
          },
          "stl": {
            "type": "integer",
            "nullable": true
          },
          "blk": {
            "type": "integer",
            "nullable": true
          },
          "tov": {
            "type": "integer",
            "nullable": true
          }
        }
      },
      "NbaReboundSplitMeta": {
        "type": "object",
        "description": "Self-teaching disclosure of what `oreb`/`dreb` are and where they stop, returned on every /v1/nba/games response.",
        "properties": {
          "data_type": {
            "type": "string",
            "example": "stored_team_totals"
          },
          "coverage": {
            "type": "string",
            "description": "Which seasons carry the split."
          },
          "semantics": {
            "type": "string",
            "description": "What the numbers mean and how they were NOT produced."
          },
          "known_gap": {
            "type": "string",
            "description": "A season absent from this relation on every field, not just rebounds."
          }
        }
      },
      "NbaTeamSeasonAverage": {
        "type": "object",
        "description": "One team's season averages for ONE SLICE of the team cube. The grain is\n(season, season_type, category, stat_type), not one row per season: the\nsame team-season appears once per populated slice.\n\n`stats` is an OPEN OBJECT on purpose. The key set differs per\n(category, stat_type) because the upstream dashboards differ - 486\ndistinct keys across the 1,290 rows we hold - so flattening them into a\nfixed shape would invent nulls for every combination that does not carry\nthem. Values are JSON numbers except `play_type` and `type_grouping`,\nwhich are strings. Vendor-internal join ids are removed before the\nresponse is built.\n",
        "properties": {
          "team_id": {
            "type": "string",
            "format": "uuid",
            "description": "BigBalls team id."
          },
          "season": {
            "type": "integer",
            "example": 2025,
            "description": "Season LABEL: the year the season starts in."
          },
          "season_type": {
            "type": "string",
            "example": "regular",
            "description": "`regular` or `playoffs`. Only `regular` is populated today."
          },
          "category": {
            "type": "string",
            "example": "tracking",
            "description": "Cube dimension: general, clutch, tracking, playtype, shooting, shotdashboard or hustle."
          },
          "stat_type": {
            "type": "string",
            "example": "drives",
            "description": "Cube dimension inside the category. The EMPTY STRING is a real value, and it is the only one the `hustle` category uses, so `?stat_type=` selects that slice.\n"
          },
          "stats": {
            "type": "object",
            "additionalProperties": true,
            "description": "The slice's statistics. Key set varies by (category, stat_type)."
          }
        }
      },
      "NbaPlayerSeasonAverage": {
        "type": "object",
        "description": "One player's per-game averages for one regular season, as the vendor\npublishes them. Numeric fields are JSON numbers. Every column is\npopulated on every row we hold, so a `0` means the player really did\nrecord none of that stat per game; it is not a placeholder for\n\"unknown\".\n",
        "properties": {
          "player_id": {
            "type": "string",
            "format": "uuid",
            "description": "BigBalls player id."
          },
          "season": {
            "type": "integer",
            "example": 2025,
            "description": "Season LABEL: the year the season starts in."
          },
          "games_played": {
            "type": "integer",
            "nullable": true
          },
          "min": {
            "type": "string",
            "nullable": true,
            "example": "36:12",
            "description": "Average minutes per game as \"MM:SS\". This is a STRING because the vendor writes it as one; we do not convert it to a decimal, because that would be a number we invented published under the vendor's name.\n"
          },
          "pts": {
            "type": "number",
            "nullable": true,
            "description": "Points per game."
          },
          "ast": {
            "type": "number",
            "nullable": true,
            "description": "Assists per game."
          },
          "reb": {
            "type": "number",
            "nullable": true,
            "description": "Total rebounds per game."
          },
          "oreb": {
            "type": "number",
            "nullable": true,
            "description": "Offensive rebounds per game."
          },
          "dreb": {
            "type": "number",
            "nullable": true,
            "description": "Defensive rebounds per game."
          },
          "stl": {
            "type": "number",
            "nullable": true,
            "description": "Steals per game."
          },
          "blk": {
            "type": "number",
            "nullable": true,
            "description": "Blocks per game."
          },
          "turnover": {
            "type": "number",
            "nullable": true,
            "description": "Turnovers per game."
          },
          "pf": {
            "type": "number",
            "nullable": true,
            "description": "Personal fouls per game."
          },
          "fga": {
            "type": "number",
            "nullable": true
          },
          "fgm": {
            "type": "number",
            "nullable": true
          },
          "fg_pct": {
            "type": "number",
            "nullable": true,
            "description": "Field goal percentage as a ratio (0.576), not a percentage."
          },
          "fg3a": {
            "type": "number",
            "nullable": true
          },
          "fg3m": {
            "type": "number",
            "nullable": true
          },
          "fg3_pct": {
            "type": "number",
            "nullable": true,
            "description": "Three-point percentage as a ratio."
          },
          "fta": {
            "type": "number",
            "nullable": true
          },
          "ftm": {
            "type": "number",
            "nullable": true
          },
          "ft_pct": {
            "type": "number",
            "nullable": true,
            "description": "Free throw percentage as a ratio."
          }
        }
      },
      "NbaPlayerAdvancedSeason": {
        "type": "object",
        "description": "One player's advanced aggregates for one season and season type.\nPercentage-style fields are ratios (0.663), not percentages, except the\nturnover rates and `ast_ratio`, which the vendor publishes on a 0-100\nscale. `e_*` variants are the vendor's estimated counterparts to the\ncolumn of the same name.\n",
        "properties": {
          "player_id": {
            "type": "string",
            "format": "uuid"
          },
          "season": {
            "type": "integer",
            "example": 2025
          },
          "season_type": {
            "type": "string",
            "enum": [
              "regular",
              "playoffs"
            ]
          },
          "games_played": {
            "type": "integer",
            "nullable": true
          },
          "wins": {
            "type": "integer",
            "nullable": true
          },
          "losses": {
            "type": "integer",
            "nullable": true
          },
          "w_pct": {
            "type": "number",
            "nullable": true
          },
          "min": {
            "type": "number",
            "nullable": true,
            "description": "Average minutes as a decimal. Note this differs from `min` on NbaPlayerSeasonAverage, which is an \"MM:SS\" string; the two relations store it differently and neither is reshaped here."
          },
          "poss": {
            "type": "number",
            "nullable": true,
            "description": "Possessions."
          },
     

# --- truncated at 32 KB (817 KB total) ---
# Full source: https://artifacts.apis.io/bigballsdata-com/openapi/_original/bigballsdata-com-api-openapi.json