{"openapi":"3.0.0","info":{"title":"TheStatsAPI","description":"Football data REST API: competitions, seasons, teams, players and matches, with scores, team and player stats, xG, lineups, event timelines and betting odds where available.\n\n**Base URL:** `https://api.thestatsapi.com/api`. **Auth:** send `Authorization: Bearer YOUR_API_KEY` on every request. Get a key at https://www.thestatsapi.com/api-keys. IDs are prefixed strings: `comp_` competition, `sn_` season, `tm_` team, `mt_` match, `pl_` player.\n\n**Building with AI?** Give your coding tool (Claude Code, Codex, Cursor, Lovable) https://api.thestatsapi.com/llms.txt. It is this whole reference, every endpoint, parameter and response shape, as one Markdown file. The OpenAPI spec is at https://api.thestatsapi.com/openapi.json and the guides are at https://www.thestatsapi.com/docs.\n\n**Coverage:** history depth varies by competition. Check `GET /coverage/leagues` to see which seasons and data types each competition has. Data is pull-only: poll the live endpoints during a match (there are no webhooks or streams).\n\n**Rate limits and quota:** every authenticated response, success or error, carries two separate budgets.\n\n- Per minute: `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` (unix seconds at the next minute boundary).\n- Per monthly billing cycle: `X-Monthly-Quota-Limit`, `X-Monthly-Quota-Remaining`, `X-Monthly-Quota-Reset` (unix seconds when the cycle rolls over). Limit and Remaining are `-1` on plans with no monthly cap.\n\nEach `Remaining` value already counts the request being served, so `0` means your next request is rejected. Trial subscriptions are metered at 10% of the plan's limits. Going over either budget returns `429` with a `Retry-After` header: code `RATE_LIMITED` for the per-minute window, `USAGE_LIMIT_EXCEEDED` for the monthly quota. Plans and limits: https://www.thestatsapi.com/#pricing.\n\n**Errors:** every error uses the same envelope, for example `{\"error\": {\"code\": \"NOT_FOUND\", \"message\": \"Match not found\", \"status_code\": 404}}`. Branch on `code`:\n\n| Status | Code | Meaning |\n|--------|------|---------|\n| 400 | `BAD_REQUEST` | A parameter is missing or invalid, e.g. `season_id is required`. |\n| 400 | `UNKNOWN_PARAMETER` | The endpoint doesn't accept a query parameter you sent, e.g. `date` instead of `date_from`. The message suggests the closest name and lists the accepted ones. |\n| 400 | `SEASON_COMPETITION_MISMATCH`, `PAGE_OUT_OF_RANGE` | `season_id` belongs to a different `competition_id`, or `page` is past the last page. |\n| 401 | `UNAUTHORIZED` | The API key is missing or invalid. |\n| 403 | `FORBIDDEN`, `KEY_REVOKED`, `ADDON_REQUIRED` | The key or account is inactive or expired, there is no active plan, or the endpoint needs an add-on. |\n| 404 | `NOT_FOUND`, `STATS_NOT_AVAILABLE_AT_SOURCE` | The ID or route doesn't exist, or detailed stats were never collected for the match. |\n| 409 | `CONFLICT`, `MATCH_IS_LIVE` | A live-only endpoint was called for a match that isn't live, or a post-match endpoint while it is. |\n| 429 | `RATE_LIMITED`, `USAGE_LIMIT_EXCEEDED` | Per-minute limit or monthly quota reached. Wait `Retry-After` seconds. |\n","version":"1.0.0"},"externalDocs":{"description":"Guides and quickstart","url":"https://www.thestatsapi.com/docs"},"servers":[{"url":"https://api.thestatsapi.com/api","description":"API Server"}],"security":[{"BearerAuth":[]}],"tags":[{"name":"Competitions","description":"Leagues, cups and tournaments, with their seasons, groups and standings. Start here to find a `competition_id` and `season_id`."},{"name":"Teams","description":"Teams, squads, season stats, standings history, injuries and suspensions."},{"name":"Matches","description":"Fixtures and results, plus per-match stats, live stats, lineups, timelines, shotmaps, heatmaps and referees."},{"name":"Players","description":"Player profiles, season stats, injuries and suspensions, and season heatmaps."},{"name":"Odds","description":"Pre-match, in-play and player-prop odds for a match from Bet365, Paddy Power, BetMGM UK, Pinnacle and Betfair Exchange."},{"name":"Coverage","description":"Which competitions and seasons have which data types, and how complete they are. Check here before you build."},{"name":"Health","description":"Check that the API is up."}],"paths":{"/health":{"get":{"tags":["Health"],"summary":"Check API health","operationId":"getHealth","description":"Returns `healthy` and the server time when the API is up. No API key is needed and the call doesn't count against your quota.","security":[],"responses":{"200":{"description":"The API is up.","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"healthy"},"timestamp":{"type":"string","format":"date-time"}}}}}}}}},"/football/competitions":{"get":{"tags":["Competitions"],"summary":"List competitions","operationId":"listCompetitions","description":"Returns football competitions (leagues, cups and international tournaments), 20 per page. Use it to find a `competition_id` by name or country. With `search`, the best match comes first.\n\n**Examples**\n\n- Find the Premier League: `GET /football/competitions?search=Premier`\n- English leagues: `GET /football/competitions?country_code=GB-ENG&type=league`\n","parameters":[{"$ref":"#/components/parameters/Page"},{"$ref":"#/components/parameters/PerPage"},{"name":"country","in":"query","required":false,"description":"Country name, case-insensitive, e.g. `England` or `Spain`","schema":{"type":"string"},"example":"England"},{"name":"country_code","in":"query","required":false,"description":"Country code as returned in `country_code` or `subdivision_code`.\n`GB` returns all UK nations; `GB-ENG`, `GB-SCT`, `GB-WLS` and\n`GB-NIR` return one nation. Other countries use ISO codes such as\n`DE` or `ES`\n","schema":{"type":"string"},"example":"GB-ENG"},{"name":"type","in":"query","required":false,"description":"Competition format","schema":{"type":"string","enum":["league","cup","tournament"]},"example":"league"},{"name":"search","in":"query","required":false,"description":"Part of the competition name, case-insensitive. The best match comes first unless you set `sort=name`","schema":{"type":"string"},"example":"Premier"},{"name":"sort","in":"query","required":false,"description":"`relevance` (default) puts the best matches for `search` first.\n`name` sorts A to Z. Without `search`, results are A to Z either\nway\n","schema":{"type":"string","enum":["relevance","name"],"default":"relevance"},"example":"name"}],"responses":{"200":{"description":"A page of competitions.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Competition"}},"meta":{"$ref":"#/components/schemas/PaginationMeta"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/football/competitions/{competition_id}":{"get":{"tags":["Competitions"],"summary":"Get a competition","operationId":"getCompetition","description":"Returns one competition with its `current_season_id` and flags for which data it has (team stats, player stats, odds, xG). Use `current_season_id` to fetch this season's matches, standings and stats.\n\n**Example:** `GET /football/competitions/comp_3039`\n","parameters":[{"name":"competition_id","in":"path","required":true,"description":"Competition ID, e.g. `comp_3039` (Premier League). Find IDs with `GET /football/competitions?search=`","schema":{"type":"string"},"example":"comp_3039"}],"responses":{"200":{"description":"The competition.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CompetitionDetail"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/football/competitions/{competition_id}/seasons":{"get":{"tags":["Competitions"],"summary":"List a competition's seasons","operationId":"listCompetitionSeasons","description":"Returns every season of a competition, newest first, with its `season_id`. The season with `is_current: true` is the same as `current_season_id` on the competition.\n\n**Example:** `GET /football/competitions/comp_3039/seasons`\n","parameters":[{"name":"competition_id","in":"path","required":true,"description":"Competition ID, e.g. `comp_3039` (Premier League). Find IDs with `GET /football/competitions?search=`","schema":{"type":"string"},"example":"comp_3039"}],"responses":{"200":{"description":"The competition's seasons, newest first.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CompetitionSeason"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/football/competitions/{competition_id}/seasons/{season_id}/groups":{"get":{"tags":["Competitions"],"summary":"List groups in a season","operationId":"listSeasonGroups","description":"Returns the groups (A to L) of a tournament season with a group stage, such as the FIFA World Cup, EURO, AFCON or the pre-2024 UEFA Champions League. Returns an empty list for competitions without groups, such as the Premier League. Pass a `group_label` to `/football/matches?group=` or to the standings `group` filter. Returns 400 if `season_id` belongs to a different competition.\n\n**Example:** `GET /football/competitions/comp_6107/seasons/sn_326766/groups` (World Cup 2022, groups A to H)\n","parameters":[{"name":"competition_id","in":"path","required":true,"description":"Competition ID, e.g. `comp_6107` (FIFA World Cup)","schema":{"type":"string"},"example":"comp_6107"},{"name":"season_id","in":"path","required":true,"description":"Season ID from `GET /football/competitions/{competition_id}/seasons`, e.g. `sn_326766` (World Cup 2022)","schema":{"type":"string"},"example":"sn_326766"}],"responses":{"200":{"description":"The season's groups, A to L.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CompetitionSeasonGroup"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/football/competitions/{competition_id}/seasons/{season_id}/standings":{"get":{"tags":["Competitions"],"summary":"Get season standings","operationId":"getSeasonStandings","description":"Returns the league table for one competition season, one row per team, sorted by `position`. Group-stage tournaments return every group's table, sorted by `group_label` and then `position`; add `group` to get one group. Knockout-only cups (such as the FA Cup) return an empty list. Returns 400 if `season_id` belongs to a different competition.\n\n**Examples**\n\n- Premier League 2026/27: `GET /football/competitions/comp_3039/seasons/sn_8406098/standings`\n- World Cup 2022, Group A: `GET /football/competitions/comp_6107/seasons/sn_326766/standings?group=A`\n","parameters":[{"name":"competition_id","in":"path","required":true,"description":"Competition ID, e.g. `comp_3039` (Premier League) or `comp_6107` (FIFA World Cup)","schema":{"type":"string"},"example":"comp_3039"},{"name":"season_id","in":"path","required":true,"description":"Season ID from `GET /football/competitions/{competition_id}/seasons`, e.g. `sn_8406098` (Premier League 2026/27)","schema":{"type":"string"},"example":"sn_8406098"},{"name":"group","in":"query","required":false,"description":"One group's table, A to L (case-insensitive). Only for\ncompetitions with a group stage; for a league it returns an\nempty list\n","schema":{"type":"string","enum":["A","B","C","D","E","F","G","H","I","J","K","L"]},"example":"A"}],"responses":{"200":{"description":"Standings rows.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Standing"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/football/teams":{"get":{"tags":["Teams"],"summary":"List teams","operationId":"listTeams","description":"Returns teams, 20 per page. Use it to find a `team_id` by name, or to list every team in a competition season. With `search`, the best match comes first. Men's and women's sides of a club can share a name, so add `is_mens_team` to tell them apart.\n\n**Examples**\n\n- Arsenal men's team: `GET /football/teams?search=Arsenal&is_mens_team=true`\n- Premier League 2026/27 teams: `GET /football/teams?competition_id=comp_3039&season_id=sn_8406098`\n","parameters":[{"$ref":"#/components/parameters/Page"},{"$ref":"#/components/parameters/PerPage"},{"name":"competition_id","in":"query","required":false,"description":"Only teams that play in this competition. Without `season_id`, uses the competition's latest season that has a table","schema":{"type":"string"},"example":"comp_3039"},{"name":"season_id","in":"query","required":false,"description":"Only teams in this season. Use it with `competition_id`; a season from a different competition returns 400","schema":{"type":"string"},"example":"sn_8406098"},{"name":"country","in":"query","required":false,"description":"Team's country, case-insensitive, e.g. `England`","schema":{"type":"string"},"example":"England"},{"name":"search","in":"query","required":false,"description":"Part of the team name, case-insensitive. The best match comes first unless you set `sort=name`","schema":{"type":"string"},"example":"Arsenal"},{"name":"is_mens_team","in":"query","required":false,"description":"`true` returns men's teams only, `false` women's teams only. Omit\nfor both. Useful when a men's and a women's team share a name\n","schema":{"type":"boolean"},"example":true},{"name":"sort","in":"query","required":false,"description":"`relevance` (default) puts the best matches for `search` first.\n`name` sorts A to Z. Without `search`, results are A to Z either\nway\n","schema":{"type":"string","enum":["relevance","name"],"default":"relevance"},"example":"name"}],"responses":{"200":{"description":"A page of teams.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Team"}},"meta":{"$ref":"#/components/schemas/PaginationMeta"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/football/teams/{team_id}/players":{"get":{"tags":["Teams"],"summary":"Get a team's squad","operationId":"getTeamSquad","description":"Returns the players registered to a team (up to 100), with extra profile fields such as preferred foot, contract end date, market value and national team. For a national team, returns the players who represent it.\n\n**Example:** `GET /football/teams/tm_9145/players` (Arsenal)\n","parameters":[{"name":"team_id","in":"path","required":true,"description":"Team ID, e.g. `tm_9145` (Arsenal). Find IDs with `GET /football/teams?search=`","schema":{"type":"string"},"example":"tm_9145"}],"responses":{"200":{"description":"The squad.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/SquadPlayer"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/football/teams/{team_id}/injuries-suspensions":{"get":{"tags":["Teams"],"summary":"Get a team's injuries and suspensions","operationId":"getTeamInjuriesSuspensions","description":"Returns the team's players who are unavailable now, split into `injuries` and `suspensions`. Set `current=false` to get every record, past ones included (check each record's `active` flag).\n\nBy default a suspension is only listed where it applies: a club sees bans from club competitions and a national team sees bans from national-team competitions (a Champions League ban doesn't rule a player out of a Nations League match). Bans whose competition is unknown are always listed. A national team's list leaves out national-team call-ups (reason `national_team`), because the player is with that team; a club's list keeps them, because the player misses club matches. A ban reported twice is listed once.\n\n**Example:** `GET /football/teams/tm_73673/injuries-suspensions` (Real Madrid)\n","parameters":[{"name":"team_id","in":"path","required":true,"description":"Team ID, e.g. `tm_73673` (Real Madrid). Find IDs with `GET /football/teams?search=`","schema":{"type":"string"},"example":"tm_73673"},{"name":"current","in":"query","required":false,"description":"`true` (default) returns only who is unavailable now. `false` returns every record, including past ones","schema":{"type":"boolean","default":true},"example":false}],"responses":{"200":{"description":"Injuries and suspensions.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PlayerUnavailability"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/football/teams/{team_id}":{"get":{"tags":["Teams"],"summary":"Get a team","operationId":"getTeam","description":"Returns one team with its country, main competition, stadium and `is_mens_team`.\n\n**Example:** `GET /football/teams/tm_9145` (Arsenal)\n","parameters":[{"name":"team_id","in":"path","required":true,"description":"Team ID, e.g. `tm_9145` (Arsenal). Find IDs with `GET /football/teams?search=`","schema":{"type":"string"},"example":"tm_9145"}],"responses":{"200":{"description":"The team.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/TeamDetail"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/football/teams/{team_id}/stats":{"get":{"tags":["Teams"],"summary":"Get a team's season stats","operationId":"getTeamStats","description":"Returns a team's record for one season: matches played, wins, draws, losses, goals, points, table position and recent form. `season_id` is required. Returns 404 when the team has no stats for that season.\n\n**Example:** `GET /football/teams/tm_9145/stats?season_id=sn_6125938` (Arsenal, Premier League 2025/26)\n","parameters":[{"name":"team_id","in":"path","required":true,"description":"Team ID, e.g. `tm_9145` (Arsenal). Find IDs with `GET /football/teams?search=`","schema":{"type":"string"},"example":"tm_9145"},{"name":"season_id","in":"query","required":true,"description":"Season ID (required). Get it from `GET /football/competitions/{competition_id}/seasons` or the competition's `current_season_id`","schema":{"type":"string"},"example":"sn_6125938"}],"responses":{"200":{"description":"The team's season stats.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/TeamStats"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/football/teams/{team_id}/standings":{"get":{"tags":["Teams"],"summary":"List a team's standings history","operationId":"getTeamStandings","description":"Returns every competition where the team has a league table entry, with its seasons nested inside (newest first). Use it to build a team's table history, then fetch a full table from `/football/competitions/{competition_id}/seasons/{season_id}/standings`. Competitions are ordered by their most recent season. Teams that only play knockout cups get an empty list.\n\n**Example:** `GET /football/teams/tm_9145/standings` (Arsenal)\n","parameters":[{"name":"team_id","in":"path","required":true,"description":"Team ID, e.g. `tm_9145` (Arsenal). Find IDs with `GET /football/teams?search=`","schema":{"type":"string"},"example":"tm_9145"}],"responses":{"200":{"description":"Competitions and seasons where the team has standings.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/TeamStandingsCompetition"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/football/matches":{"get":{"tags":["Matches"],"summary":"List matches","operationId":"listMatches","description":"Returns fixtures and results, 20 per page. Combine filters to get a team's next match, a matchday, or every match between two dates.\n\n**Order:** newest kickoff first by default (`sort=-utc_date`). Use `sort=utc_date` for soonest first.\n\n**Examples**\n\n- Next fixture for a team (first row is the next match): `GET /football/matches?team_id=tm_9145&status=scheduled&sort=utc_date&per_page=1`\n- Latest result for a team: `GET /football/matches?team_id=tm_9145&status=finished&per_page=1`\n- One day's Premier League matches: `GET /football/matches?competition_id=comp_3039&date_from=2026-10-10&date_to=2026-10-10&sort=utc_date`\n- One matchday: `GET /football/matches?competition_id=comp_3039&season_id=sn_8406098&matchday=6`\n\nThere is no `date` parameter. For a single day, set `date_from` and `date_to` to the same date.\n","parameters":[{"$ref":"#/components/parameters/Page"},{"$ref":"#/components/parameters/PerPage"},{"name":"competition_id","in":"query","required":false,"description":"Only matches in this competition","schema":{"type":"string"},"example":"comp_3039"},{"name":"season_id","in":"query","required":false,"description":"Only matches in this season. If you also send `competition_id`, the season must belong to it or you get 400 `SEASON_COMPETITION_MISMATCH`","schema":{"type":"string"},"example":"sn_8406098"},{"name":"team_id","in":"query","required":false,"description":"Only matches this team plays in, home or away","schema":{"type":"string"},"example":"tm_9145"},{"name":"date_from","in":"query","required":false,"description":"Kickoff on or after this date, `YYYY-MM-DD`. Days are read in the `utc_offset` time zone (UTC by default)","schema":{"type":"string","format":"date"},"example":"2026-10-01"},{"name":"date_to","in":"query","required":false,"description":"Kickoff on or before the end of this date, `YYYY-MM-DD`. Must not be earlier than `date_from`","schema":{"type":"string","format":"date"},"example":"2026-10-31"},{"name":"utc_offset","in":"query","required":false,"description":"Time zone offset for reading `date_from` and `date_to`, as `+HH:MM`\nor `-HH:MM` (hours up to 14). Defaults to `+00:00` (UTC). Kickoff\ntimes in the response are always UTC\n","schema":{"type":"string","pattern":"^[+-](?:0?\\d|1[0-4]):[0-5]\\d$","default":"+00:00"},"example":"-05:00"},{"name":"matchday","in":"query","required":false,"description":"Round number in a league season (positive integer)","schema":{"type":"integer","minimum":1},"example":6},{"name":"status","in":"query","required":false,"description":"Match status","schema":{"type":"string","enum":["scheduled","live","finished","postponed","cancelled"]},"example":"scheduled"},{"name":"stage","in":"query","required":false,"description":"`regular` returns league and group-stage matches only. `playoff`\nreturns knockout and play-off matches only (for example Champions\nLeague knockout rounds or Championship play-offs). `all` (default)\nreturns both\n","schema":{"type":"string","enum":["regular","playoff","all"],"default":"all"},"example":"regular"},{"name":"group","in":"query","required":false,"description":"Group letter A to L (case-insensitive) for tournaments with a\ngroup stage, such as the FIFA World Cup. Requires\n`competition_id`; without it you get 400\n","schema":{"type":"string","enum":["A","B","C","D","E","F","G","H","I","J","K","L"]},"example":"A"},{"name":"sort","in":"query","required":false,"description":"Kickoff order. `-utc_date` (default) is newest first. `utc_date` is\nsoonest first; use it with `status=scheduled` for upcoming\nfixtures. Matches with the same kickoff are ordered by match ID,\nso pages are stable\n","schema":{"type":"string","enum":["-utc_date","utc_date"],"default":"-utc_date"},"example":"utc_date"}],"responses":{"200":{"description":"A page of matches.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Match"}},"meta":{"$ref":"#/components/schemas/PaginationMeta"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/football/matches/{match_id}":{"get":{"tags":["Matches"],"summary":"Get a match","operationId":"getMatch","description":"Returns one match with its score breakdown, half-time score, venue, referee and managers. Flags such as `odds_available`, `xg_available` and `shotmap_available` tell you which other match endpoints have data, so check them first.\n\n**Example:** `GET /football/matches/mt_838955483`\n","parameters":[{"name":"match_id","in":"path","required":true,"description":"Match ID, e.g. `mt_838955483`. Find IDs with `GET /football/matches`","schema":{"type":"string"},"example":"mt_838955483"}],"responses":{"200":{"description":"The match.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/MatchDetail"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/football/matches/{match_id}/referee":{"get":{"tags":["Matches"],"summary":"Get a match's referee","operationId":"getMatchReferee","description":"Returns the referee assigned to a match, with career totals for games, yellow cards and red cards. `referee` is null when no referee is assigned.\n\n**Example:** `GET /football/matches/mt_838955483/referee`\n","parameters":[{"name":"match_id","in":"path","required":true,"description":"Match ID, e.g. `mt_838955483`. Find IDs with `GET /football/matches`","schema":{"type":"string"},"example":"mt_838955483"}],"responses":{"200":{"description":"The referee, or null.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/MatchRefereeResponse"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/football/matches/{match_id}/stats":{"get":{"tags":["Matches"],"summary":"Get match stats","operationId":"getMatchStats","description":"Returns team stats for a played match: possession, shots, xG, passes, duels, defending and goalkeeping, each split into full match, first half and second half. While the match is live it returns 409 (`MATCH_IS_LIVE`); use `/live-stats` instead.\n\n**Example:** `GET /football/matches/mt_838955483/stats`\n","parameters":[{"name":"match_id","in":"path","required":true,"description":"Match ID, e.g. `mt_838955483`. Find IDs with `GET /football/matches`","schema":{"type":"string"},"example":"mt_838955483"}],"responses":{"200":{"description":"The match stats.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/MatchStats"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/StatsNotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/football/matches/{match_id}/live-stats":{"get":{"tags":["Matches"],"summary":"Get live match stats","operationId":"getMatchLiveStats","description":"Returns running team stats while a match is in play, with the clock and score in `data.meta`. Poll it during the match; responses may be cached for up to 30 seconds.\n\nFor up to 5 minutes after full-time it still returns the final snapshot with `meta.match_status = \"finished\"`. After that, and for matches that haven't started or were postponed or cancelled, it returns 409. Use `/stats` for post-match totals.\n\n**Example:** `GET /football/matches/mt_838955483/live-stats`\n","parameters":[{"name":"match_id","in":"path","required":true,"description":"Match ID of a live match. Find live matches with `GET /football/matches?status=live`","schema":{"type":"string"},"example":"mt_838955483"}],"responses":{"200":{"description":"Live stats.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/LiveMatchStats"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/football/matches/{match_id}/player-stats":{"get":{"tags":["Matches"],"summary":"Get match player stats","operationId":"getMatchPlayerStats","description":"Returns per-player stats for a played match: rating, minutes, passing, shooting (including xG and xA), duels, defending, goalkeeping, fouls and cards. Add `player_ids` to get only some players. While the match is live it returns 409; use `/live-player-stats` instead.\n\n**Example:** `GET /football/matches/mt_838955483/player-stats?player_ids=pl_4089246,pl_08984824` (Virgil van Dijk and Alexis Mac Allister)\n","parameters":[{"name":"match_id","in":"path","required":true,"description":"Match ID, e.g. `mt_838955483`. Find IDs with `GET /football/matches`","schema":{"type":"string"},"example":"mt_838955483"},{"name":"player_ids","in":"query","required":false,"description":"Comma-separated player IDs. Returns only these players","schema":{"type":"string"},"example":"pl_4089246,pl_08984824"}],"responses":{"200":{"description":"One entry per player.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/MatchPlayerStats"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/StatsNotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/football/matches/{match_id}/live-player-stats":{"get":{"tags":["Matches"],"summary":"Get live match player stats","operationId":"getMatchLivePlayerStats","description":"Returns per-player stats while a match is in play, with `meta.match_status = \"live\"`. Poll it during the match. Returns 409 when the match is not live; use `/player-stats` after full-time.\n\n**Example:** `GET /football/matches/mt_838955483/live-player-stats`\n","parameters":[{"name":"match_id","in":"path","required":true,"description":"Match ID of a live match. Find live matches with `GET /football/matches?status=live`","schema":{"type":"string"},"example":"mt_838955483"},{"name":"player_ids","in":"query","required":false,"description":"Comma-separated player IDs. Returns only these players","schema":{"type":"string"},"example":"pl_4089246,pl_08984824"}],"responses":{"200":{"description":"One entry per player.","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/MatchPlayerStats"}},"meta":{"type":"object","required":["match_status"],"properties":{"match_status":{"type":"string","enum":["live"],"example":"live"}}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/football/matches/{match_id}/lineups":{"get":{"tags":["Matches"],"summary":"Get match lineups","operationId":"getMatchLineups","description":"Returns both teams' starting XI, substitutes and formation. Before kickoff you get a predicted XI, replaced by the official team sheet once it's announced (about an hour before kickoff). For started and finished matches you get the XI that played, with every substitute (used and unused). `type` tells you which one you have (`predicted`, `team_sheet` or `played`), and `confirmed` is true only for a team sheet or a played match. Returns 404 when there's no lineup data for the match yet.\n\n**Example:** `GET /football/matches/mt_838955483/lineups`\n","parameters":[{"name":"match_id","in":"path","required":true,"description":"Match ID, e.g. `mt_838955483`. Find IDs with `GET /football/matches`","schema":{"type":"string"},"example":"mt_838955483"}],"responses":{"200":{"description":"The lineups.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Lineups"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/football/matches/{match_id}/players/{player_id}/heatmap":{"get":{"tags":["Matches"],"summary":"Get a player's match heatmap","operationId":"getMatchPlayerHeatmap","description":"Returns the pitch locations where one player touched the ball in one match, as `x` and `y` from 0 to 100. Returns 404 when there's no heatmap, for example for an unused substitute or a match without positional data.\n\n**Example:** `GET /football/matches/mt_404012971/players/pl_84027040/heatmap`\n","parameters":[{"name":"match_id","in":"path","required":true,"description":"Match ID, e.g. `mt_404012971`. Find IDs with `GET /football/matches`","schema":{"type":"string"},"example":"mt_404012971"},{"name":"player_id","in":"path","required":true,"description":"Player ID of someone who played in the match, e.g. `pl_84027040`. Get IDs from the match's `/lineups` or `/player-stats`","schema":{"type":"string"},"example":"pl_84027040"}],"responses":{"200":{"description":"The heatmap points.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/MatchPlayerHeatmap"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/football/matches/{match_id}/shotmap":{"get":{"tags":["Matches"],"summary":"Get match shotmap","operationId":"getMatchShotmap","description":"Returns every shot in a match with its xG, outcome, body part, situation and pitch coordinates, plus non-penalty xG totals for both teams. Add `player_id` to get one player's shots. Check `shotmap_available` on the match first.\n\n**Example:** `GET /football/matches/mt_838955483/shotmap`\n","parameters":[{"name":"match_id","in":"path","required":true,"description":"Match ID, e.g. `mt_838955483`. Find IDs with `GET /football/matches`","schema":{"type":"string"},"example":"mt_838955483"},{"name":"player_id","in":"query","required":false,"description":"Only shots by this player","schema":{"type":"string"},"example":"pl_61849099"}],"responses":{"200":{"description":"The shots and xG summary.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShotmapResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/football/matches/{match_id}/timeline":{"get":{"tags":["Matches"],"summary":"Get match timeline","operationId":"getMatchTimeline","description":"Returns a played match's events in order: goals, shots, cards, fouls, offsides, corners, substitutions, penalties, VAR reviews and period markers, each with its team and player where known. Stoppage time is kept separate, so 45+3' comes back as `\"minute\": 45, \"extra_time\": 3`.\n\nWhen there's no timeline you get 200 with an empty `events` list, `meta.coverage = \"none\"` and a `reason`: `match_not_started`, `no_events` or `not_supported` (the competition isn't covered). 404 means the match doesn't exist. While the match is live it returns 409; use `/live-timeline` instead.\n\n**Example:** `GET /football/matches/mt_838955483/timeline?event_type=goal,penalty_scored,yellow_card`\n","parameters":[{"name":"match_id","in":"path","required":true,"description":"Match ID, e.g. `mt_838955483`. Find IDs with `GET /football/matches`","schema":{"type":"string"},"example":"mt_838955483"},{"name":"event_type","in":"query","required":false,"description":"Comma-separated event types to keep (values of `type`, e.g. `goal`, `yellow_card`, `substitution`). Defaults to all. Unknown values return 400. Penalty goals are `penalty_scored`, not `goal`, so use `goal,penalty_scored` to get every goal; shoot-out kicks are also `penalty_scored` with `period` `penalties`","schema":{"type":"string"},"example":"goal,penalty_scored,yellow_card"},{"name":"period","in":"query","required":false,"description":"Comma-separated periods to keep. Values are `first_half`, `second_half`, `extra_time_first_half`, `extra_time_second_half` and `penalties`","schema":{"type":"string"},"example":"first_half,second_half"},{"name":"team_id","in":"query","required":false,"description":"Only events for this team","schema":{"type":"string"},"example":"tm_0406"}],"responses":{"200":{"description":"The timeline.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MatchTimelineResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/football/matches/{match_id}/live-timeline":{"get":{"tags":["Matches"],"summary":"Get live match timeline","operationId":"getMatchLiveTimeline","description":"Returns the same events as `/timeline` while a match is in play, with `meta.match_status = \"live\"`. Poll it during the match. Returns 409 when the match is not live.\n\n**Example:** `GET /football/matches/mt_838955483/live-timeline?event_type=goal,penalty_scored`\n","parameters":[{"name":"match_id","in":"path","required":true,"description":"Match ID of a live match. Find live matches with `GET /football/matches?status=live`","schema":{"type":"string"},"example":"mt_838955483"},{"name":"event_type","in":"query","required":false,"description":"Comma-separated event types to keep (values of `type`, e.g. `goal`, `yellow_card`, `substitution`). Defaults to all. Unknown values return 400. Penalty goals are `penalty_scored`, not `goal`, so use `goal,penalty_scored` to get every goal; shoot-out kicks are also `penalty_scored` with `period` `penalties`","schema":{"type":"string"},"example":"goal,penalty_scored,yellow_card"},{"name":"period","in":"query","required":false,"description":"Comma-separated periods to keep. Values are `first_half`, `second_half`, `extra_time_first_half`, `extra_time_second_half` and `penalties`","schema":{"type":"string"},"example":"first_half,second_half"},{"name":"team_id","in":"query","required":false,"description":"Only events for this team","schema":{"type":"string"},"example":"tm_0406"}],"responses":{"200":{"description":"The live timeline.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/MatchTimelineResponse"},{"type":"object","properties":{"meta":{"type":"object","required":["match_status"],"example":{"total":10,"coverage":"full","last_updated":"2026-05-15T21:05:49.659Z","match_status":"live"},"properties":{"match_status":{"type":"string","enum":["live"],"example":"live"}}}}}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/football/players":{"get":{"tags":["Players"],"summary":"List players","operationId":"listPlayers","description":"Returns players, 20 per page. Search by name, list a team's players, or fetch several players by ID in one call. With `search`, the best match comes first.\n\n**Examples**\n\n- Find a player: `GET /football/players?search=saka`\n- Several players at once: `GET /football/players?player_ids=pl_45126714,pl_29627593`\n","parameters":[{"$ref":"#/components/parameters/Page"},{"$ref":"#/components/parameters/PerPage"},{"name":"team_id","in":"query","required":false,"description":"Only players currently at this team","schema":{"type":"string"},"example":"tm_9145"},{"name":"position","in":"query","required":false,"description":"`G` (goalkeeper), `D` (defender), `M` (midfielder) or `F` (forward). Full words such as `goalkeeper` or `forward` also work","schema":{"type":"string"},"example":"F"},{"name":"search","in":"query","required":false,"description":"Part of the player's name, at least 3 characters (shorter returns 400). Ignores case and accents (\"odegaard\" finds \"Ødegaard\"), and treats hyphens and spaces the same (\"hudson odoi\" finds \"Hudson-Odoi\"). For searches, `meta.total` is capped at 1000","schema":{"type":"string","minLength":3},"example":"saka"},{"name":"sort","in":"query","required":false,"description":"`relevance` (default) puts the best matches for `search` first.\n`name` sorts A to Z. Without `search`, results are A to Z either\nway\n","schema":{"type":"string","enum":["relevance","name"],"default":"relevance"},"example":"name"},{"name":"player_ids","in":"query","required":false,"description":"Comma-separated player IDs. Returns only these players","schema":{"type":"string"},"example":"pl_45126714,pl_29627593"}],"responses":{"200":{"description":"A page of players.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Player"}},"meta":{"$ref":"#/components/schemas/PaginationMeta"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/football/players/{player_id}":{"get":{"tags":["Players"],"summary":"Get a player","operationId":"getPlayer","description":"Returns one player's profile: name, position, date of birth, age, nationality, height and current team with shirt number.\n\n**Example:** `GET /football/players/pl_45126714` (Bukayo Saka)\n","parameters":[{"name":"player_id","in":"path","required":true,"description":"Player ID, e.g. `pl_45126714` (Bukayo Saka). Find IDs with `GET /football/players?search=`","schema":{"type":"string"},"example":"pl_45126714"}],"responses":{"200":{"description":"The player.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Player"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/football/players/{player_id}/injuries-suspensions":{"get":{"tags":["Players"],"summary":"Get a player's injuries and suspensions","operationId":"getPlayerInjuriesSuspensions","description":"Returns the player's injuries and suspensions in effect now, split into `injuries` and `suspensions`. Set `current=false` to get every record, past ones included (check each record's `active` flag). A suspension only applies in its `competition`. A ban reported twice is listed once, and national-team call-ups listed under an international competition are left out.\n\n**Example:** `GET /football/players/pl_45126714/injuries-suspensions`\n","parameters":[{"name":"player_id","in":"path","required":true,"description":"Player ID, e.g. `pl_45126714` (Bukayo Saka). Find IDs with `GET /football/players?search=`","schema":{"type":"string"},"example":"pl_45126714"},{"name":"current","in":"query","required":false,"description":"`true` (default) returns only records in effect now. `false` returns every record, including past ones","schema":{"type":"boolean","default":true},"example":false}],"responses":{"200":{"description":"Injuries and suspensions.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PlayerUnavailability"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/football/players/{player_id}/stats":{"get":{"tags":["Players"],"summary":"Get a player's season stats","operationId":"getPlayerStats","description":"Returns a player's totals for one season: appearances, starts, minutes, rating, goals, assists, shooting, passing, defending, duels and discipline. `season_id` is required. Add `competition_id` to count one competition only, and `stage` to split league matches from play-offs.\n\n**Example:** `GET /football/players/pl_45126714/stats?season_id=sn_6125938&competition_id=comp_3039` (Bukayo Saka, Premier League 2025/26)\n","parameters":[{"name":"player_id","in":"path","required":true,"description":"Player ID, e.g. `pl_45126714` (Bukayo Saka). Find IDs with `GET /football/players?search=`","schema":{"type":"string"},"example":"pl_45126714"},{"name":"season_id","in":"query","required":true,"description":"Season ID (required). Get it from `GET /football/competitions/{competition_id}/seasons` or the competition's `current_season_id`","schema":{"type":"string"},"example":"sn_6125938"},{"name":"competition_id","in":"query","required":false,"description":"Only count matches in this competition","schema":{"type":"string"},"example":"comp_3039"},{"name":"stage","in":"query","required":false,"description":"`regular` counts league and group-stage matches only. `playoff`\ncounts knockout and play-off matches only. `all` (default) counts\nevery match the player appeared in for the season\n","schema":{"type":"string","enum":["regular","playoff","all"],"default":"all"},"example":"regular"}],"responses":{"200":{"description":"The player's season stats.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PlayerStats"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/football/players/{player_id}/competitions/{competition_id}/seasons/{season_id}/heatmap":{"get":{"tags":["Players"],"summary":"Get a player's season heatmap","operationId":"getPlayerSeasonHeatmap","description":"Returns a player's touches across every match they played in one competition season, grouped into pitch cells with a `count`. For tournaments such as the World Cup, group and knockout matches are combined into one map. Returns 400 if `season_id` belongs to a different competition, and 404 when there's no heatmap (for example a season not played yet, or a competition without positional data).\n\n**Example:** `GET /football/players/pl_84027040/competitions/comp_6107/seasons/sn_326766/heatmap` (Dušan Vlahović, World Cup 2022)\n","parameters":[{"name":"player_id","in":"path","required":true,"description":"Player ID, e.g. `pl_84027040` (Dušan Vlahović). Find IDs with `GET /football/players?search=`","schema":{"type":"string"},"example":"pl_84027040"},{"name":"competition_id","in":"path","required":true,"description":"Competition ID, e.g. `comp_6107` (FIFA World Cup)","schema":{"type":"string"},"example":"comp_6107"},{"name":"season_id","in":"path","required":true,"description":"Season ID from `GET /football/competitions/{competition_id}/seasons`, e.g. `sn_326766` (World Cup 2022)","schema":{"type":"string"},"example":"sn_326766"}],"responses":{"200":{"description":"The season heatmap.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PlayerSeasonHeatmap"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/football/matches/{match_id}/odds":{"get":{"tags":["Odds"],"summary":"Get match odds","operationId":"getMatchOdds","description":"Returns pre-match odds for a match, one entry per bookmaker (Bet365, Paddy Power, BetMGM UK, Pinnacle, Betfair Exchange), with the opening and latest price for each selection. Markets include match result (full time and half time), both teams to score, goals, corners and cards lines, Asian and European handicaps, team totals, shots, correct score, to qualify and penalty specials. A market appears only when that bookmaker prices it. Works for upcoming and finished matches where odds were captured; check `odds_available` on the match first. Returns 404 when the match has no odds. Older matches mostly have Bet365 prices only.\n\n**Example:** `GET /football/matches/mt_200199332/odds?bookmaker=bet365,pinnacle`\n","parameters":[{"name":"match_id","in":"path","required":true,"description":"Match ID, e.g. `mt_200199332`. Find IDs with `GET /football/matches`","schema":{"type":"string"},"example":"mt_200199332"},{"name":"bookmaker","in":"query","required":false,"description":"Comma-separated bookmaker slugs: `bet365`, `paddy-power`, `betmgm-uk`, `pinnacle`, `betfair-exchange`. Omit for every bookmaker. Unknown slugs return 400. If the match has odds but none from these bookmakers, you get 200 with an empty `bookmakers` list","schema":{"type":"string"},"example":"bet365,pinnacle"}],"responses":{"200":{"description":"Odds per bookmaker.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/MatchOdds"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/football/matches/{match_id}/odds/live":{"get":{"tags":["Odds"],"summary":"Get live match odds","operationId":"getMatchLiveOdds","description":"Returns in-play odds for a match, one entry per bookmaker quoting in-play prices (Bet365 first, then Paddy Power, BetMGM UK, Betfair Exchange). Every market a bookmaker quotes is included: match result, totals (with quarter lines), both teams to score, corners, Asian and European handicaps, double chance, draw no bet, correct score, team totals, to qualify and more. Bookmakers and markets change as the match goes on and books suspend and reopen. Check `live_odds_available` on the match first, and poll during the match. Returns 404 when there are no live odds for the match.\n\n**Example:** `GET /football/matches/mt_200199332/odds/live`\n","parameters":[{"name":"match_id","in":"path","required":true,"description":"Match ID of a live match. Find live matches with `GET /football/matches?status=live`","schema":{"type":"string"},"example":"mt_200199332"}],"responses":{"200":{"description":"Live odds per bookmaker.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/LiveMatchOdds"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/football/matches/{match_id}/odds/players":{"get":{"deprecated":true,"tags":["Odds"],"summary":"[DEPRECATED] Get player-prop odds (v1)","operationId":"getMatchPlayerOddsV1","description":"**Deprecated since 2026-07-25. Use `/v2/football/matches/{match_id}/odds/players` instead.** v2 has more markets and more bookmakers.\n\nReturns the latest player-prop odds for a match from Bet365 only, for 5 markets: `anytime_goalscorer`, `first_goalscorer`, `player_shots`, `player_shots_on_target` and `player_assists`. Every entry has `line` and `market_type`; they are null where a market has no line (for example `first_goalscorer`). Within each market, entries are sorted by line, then `Over` before `Under`, then shortest odds first, then name. Markets with no prices are left out.\n\n**Example:** `GET /football/matches/mt_200199332/odds/players?markets=anytime_goalscorer,player_shots`\n","parameters":[{"name":"match_id","in":"path","required":true,"description":"Match ID, e.g. `mt_200199332`. Find IDs with `GET /football/matches`","schema":{"type":"string"},"example":"mt_200199332"},{"name":"markets","in":"query","required":false,"description":"Comma-separated market keys: `anytime_goalscorer`, `first_goalscorer`, `player_shots`, `player_shots_on_target`, `player_assists`. Defaults to all 5. Unknown values return 400","schema":{"type":"string"},"example":"anytime_goalscorer,player_shots"}],"responses":{"200":{"description":"Bet365 player-prop odds.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/MatchPlayerOddsV1"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/v2/football/matches/{match_id}/odds/players":{"get":{"tags":["Odds"],"summary":"Get player-prop odds","operationId":"getMatchPlayerOdds","description":"Returns the latest player-prop odds for a match, grouped by bookmaker (Bet365 first) and then by market. A bookmaker appears only if it prices at least one player prop for the match. Markets cover goalscorers, shots, assists, cards, tackles, fouls, passes, goalkeeper saves and player of the match; which ones you get depends on the bookmaker and the match. Markets with no prices are left out.\n\nLine markets (`player_shots`, `player_shots_on_target`, `player_shots_on_target_outside_box`, `player_headed_shots_on_target`, `player_tackles`, `player_fouls_committed`, `player_to_be_fouled`, `player_passes`, `goalkeeper_saves`) have one entry per player, line and direction, so a player priced at Over 0.5, 1.5 and 2.5 gives three entries. Other markets (goalscorers, `player_assists`, `score_or_assist`, cards, `player_of_the_match`) have no `line` or `market_type` field. `id` is null when a priced player can't be matched to a player ID. Within each market, entries are sorted by line, then `Over` before `Under`, then shortest odds first, then name.\n\n**Example:** `GET /v2/football/matches/mt_200199332/odds/players?bookmaker=bet365`\n","parameters":[{"name":"match_id","in":"path","required":true,"description":"Match ID, e.g. `mt_200199332`. Find IDs with `GET /football/matches`","schema":{"type":"string"},"example":"mt_200199332"},{"name":"bookmaker","in":"query","required":false,"description":"Comma-separated bookmaker slugs, same as on `/odds`: `bet365`, `paddy-power`, `betmgm-uk`, `pinnacle`, `betfair-exchange`. Omit for every bookmaker. Unknown values return 400","schema":{"type":"string"},"example":"bet365,paddy-power"}],"responses":{"200":{"description":"Player-prop odds per bookmaker.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/MatchPlayerOdds"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/coverage/leagues":{"get":{"tags":["Coverage"],"summary":"List coverage by competition","operationId":"listCoverageLeagues","description":"Returns one row per competition: how many seasons are loaded, which data types it has (fixtures, team stats, xG, odds, lineups, player stats, standings), the latest season's status and the number of finished matches. Use it to check what's available before you build. Add `data_type` to list only competitions that have that data. Counts only include finished matches, and responses are cached for several hours.\n\n**Example:** `GET /coverage/leagues?data_type=xg&search=Premier`\n","parameters":[{"$ref":"#/components/parameters/Page"},{"name":"per_page","in":"query","required":false,"description":"Results per page (default 100, max 200)","schema":{"type":"integer","default":100,"maximum":200},"example":50},{"name":"data_type","in":"query","required":false,"description":"Only competitions that have this data type","schema":{"type":"string","enum":["fixtures","team_stats","xg","odds","opening_odds","closing_odds","lineups","player_stats","standings"]},"example":"xg"},{"name":"search","in":"query","required":false,"description":"Part of the competition name, case-insensitive","schema":{"type":"string"},"example":"Premier"}],"responses":{"200":{"description":"A page of competitions with coverage.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CoverageLeague"}},"meta":{"$ref":"#/components/schemas/PaginationMeta"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/coverage/leagues/{competition_id}":{"get":{"tags":["Coverage"],"summary":"Get coverage for a competition","operationId":"getCoverageLeague","description":"Returns season-by-season coverage for one competition: finished and total matches, a season status, and how many finished matches have each data type. The list includes every season we know of, so check `status` (`not_loaded` means no matches are loaded for that season). Seasons are not sorted by date. New data type keys may be added, so ignore keys you don't recognise.\n\n**Example:** `GET /coverage/leagues/comp_3039` (Premier League)\n","parameters":[{"name":"competition_id","in":"path","required":true,"description":"Competition ID, e.g. `comp_3039` (Premier League). Find IDs with `GET /football/competitions?search=`","schema":{"type":"string"},"example":"comp_3039"}],"responses":{"200":{"description":"The competition's coverage by season.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CoverageLeagueDetail"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/coverage/summary":{"get":{"tags":["Coverage"],"summary":"Get coverage totals","operationId":"getCoverageSummary","description":"Returns headline totals across all competitions: number of competitions, seasons and finished matches, and seasons by status.\n\n**Example:** `GET /coverage/summary`\n","responses":{"200":{"description":"Coverage totals.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CoverageSummary"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}}},"components":{"securitySchemes":{"BearerAuth":{"type":"http","scheme":"bearer","description":"Your API key, sent as `Authorization: Bearer YOUR_API_KEY`. Get one at https://www.thestatsapi.com/api-keys"}},"headers":{"XRateLimitLimit":{"description":"Maximum requests allowed in the current one-minute window for this API key and product.","schema":{"type":"integer"}},"XRateLimitRemaining":{"description":"Requests remaining in the current one-minute window, counting the request being served.\n","schema":{"type":"integer"}},"XRateLimitReset":{"description":"Unix timestamp, in seconds, when the current rate-limit window resets.","schema":{"type":"integer"}},"XMonthlyQuotaLimit":{"description":"Requests allowed in the current monthly billing cycle for this user and product. `-1` on plans with no monthly cap.\n","schema":{"type":"integer"}},"XMonthlyQuotaRemaining":{"description":"Requests remaining in the current monthly billing cycle, counting the request being served. `-1` on plans with no monthly cap.\n","schema":{"type":"integer"}},"XMonthlyQuotaReset":{"description":"Unix timestamp, in seconds, when the monthly billing cycle rolls over.","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying. Sent only on `429` responses, and always the real time until the budget that rejected you clears, so it agrees with the matching reset header: waiting exactly this long lands on that timestamp.\n\nOn `RATE_LIMITED` it is the time left in the current one-minute window (`X-RateLimit-Reset`) and can be as little as 1 second near a boundary. On `USAGE_LIMIT_EXCEEDED` it is the time until the billing cycle rolls over (`X-Monthly-Quota-Reset`) and is therefore large — days, not seconds. Retrying sooner than that returns `429` again for the rest of the cycle; upgrade the plan instead.\n","schema":{"type":"integer"}}},"parameters":{"Page":{"name":"page","in":"query","description":"Page number, starting at 1. `meta.total_pages` tells you how many there are","schema":{"type":"integer","minimum":1,"default":1},"example":1},"PerPage":{"name":"per_page","in":"query","description":"Results per page (default 20, max 100). The older name `limit` also\nworks the same way\n","schema":{"type":"integer","minimum":1,"maximum":100,"default":20},"example":50}},"schemas":{"Competition":{"type":"object","required":["id","name","country","country_code","subdivision_code","confederation","type","has_team_stats","has_player_stats"],"properties":{"id":{"type":"string","example":"comp_3039"},"name":{"type":"string","example":"Premier League"},"country":{"type":"string","nullable":true,"description":"Country name. Null for international / continental competitions\n(UEFA Champions League, Copa Libertadores, …) — see\n`confederation`.\n","example":"England"},"country_code":{"type":"string","nullable":true,"description":"ISO-3166 alpha-2 country code (e.g. `GB`). Subdivisions are\nexposed via `subdivision_code` (`GB-ENG`, `GB-SCT`). Null for\ninternational competitions.\n","example":"GB"},"subdivision_code":{"type":"string","nullable":true,"description":"ISO-3166-2 subdivision code where applicable. Set for English,\nScottish, Welsh and Northern-Irish competitions (`GB-ENG`,\n`GB-SCT`, `GB-WLS`, `GB-NIR`); null otherwise.\n","example":"GB-ENG"},"confederation":{"type":"string","nullable":true,"description":"Confederation tag for international competitions (`UEFA`,\n`CONMEBOL`, `FIFA`, `CAF`, `AFC`, `CONCACAF`, `OFC`). Null for\ndomestic competitions.\n","example":null},"type":{"type":"string","enum":["league","cup","tournament"],"example":"league"},"has_team_stats":{"type":"boolean","description":"Indicates if team statistics are available for this competition","example":true},"has_player_stats":{"type":"boolean","description":"Indicates if player statistics are available for this competition","example":true},"odds_available":{"type":"boolean","description":"Indicates if betting odds are available for matches in this\ncompetition (prematch and historical closing lines where stored).\n","example":true},"live_odds_available":{"type":"boolean","description":"Indicates if live (in-play) odds are currently available for at least one match in this competition","example":false},"xg_available":{"type":"boolean","description":"Indicates if expected goals (xG) data is available with at least\none non-zero value for this competition.\n","example":true}}},"CompetitionDetail":{"allOf":[{"$ref":"#/components/schemas/Competition"},{"type":"object","properties":{"current_season_id":{"type":"string","nullable":true,"description":"Latest season ID for this competition. Null for dormant or\nretired competitions where no current season is known.\n","example":"sn_8406098"},"total_teams":{"type":"integer","example":20}}}]},"CompetitionSeason":{"type":"object","required":["id","name","year","start_year","end_year","is_current"],"properties":{"id":{"type":"string","example":"sn_6125938"},"name":{"type":"string","example":"Premier League 25/26"},"year":{"type":"string","description":"Display string (e.g. `2022` or `25/26`).","example":"25/26"},"start_year":{"type":"integer","nullable":true,"description":"First calendar year of the season (e.g. 2025 for `25/26`). Null when the year can't be read.","example":2025},"end_year":{"type":"integer","nullable":true,"description":"Last calendar year of the season (e.g. 2026 for `25/26`).","example":2026},"is_current":{"type":"boolean","description":"True for the competition's latest season (same ID as `current_season_id` on the competition).","example":false}}},"CompetitionSeasonGroup":{"type":"object","required":["group_label","name"],"properties":{"group_label":{"type":"string","description":"Group letter (A–L). Can be passed back into\n`/football/matches?group=` to filter the group's fixtures.\n","example":"A"},"name":{"type":"string","description":"Ready-to-display group name (English).","example":"Group A"}}},"TeamStandingsCompetition":{"type":"object","required":["competition","seasons"],"properties":{"competition":{"type":"object","required":["id","name"],"properties":{"id":{"type":"string","example":"comp_7739"},"name":{"type":"string","example":"UEFA Europa League"}}},"seasons":{"type":"array","description":"Seasons (newest first) where this team has a standings row\nin this competition. `group_label` is the trailing letter\n(A–L) for group-stage tables, null for linear leagues.\n","items":{"type":"object","required":["id","name"],"properties":{"id":{"type":"string","example":"sn_706199"},"name":{"type":"string","example":"UEFA Europa League 22/23"},"group_label":{"type":"string","nullable":true,"example":"A"}}}}}},"Standing":{"type":"object","required":["team","matches_played","wins","draws","losses","goals_for","goals_against","goal_difference","points"],"properties":{"team":{"type":"object","required":["id","name"],"properties":{"id":{"type":"string","example":"tm_9145"},"name":{"type":"string","example":"Arsenal"}}},"position":{"type":"integer","nullable":true,"description":"1-indexed rank within the league or group. Null if not yet\nassigned (e.g. pre-tournament group rows).\n","example":1},"matches_played":{"type":"integer","example":38},"wins":{"type":"integer","example":26},"draws":{"type":"integer","example":7},"losses":{"type":"integer","example":5},"goals_for":{"type":"integer","example":71},"goals_against":{"type":"integer","example":27},"goal_difference":{"type":"integer","description":"Derived as `goals_for - goals_against`.","example":44},"points":{"type":"integer","example":85},"group_label":{"type":"string","nullable":true,"description":"Group letter (A–L) for group-stage competitions; null for\nlinear leagues. Pass back into `?group=` on this endpoint\nto filter to one group.\n","example":null}}},"Team":{"type":"object","required":["id","name","short_name","country"],"properties":{"id":{"type":"string","example":"tm_0406"},"name":{"type":"string","example":"Liverpool"},"short_name":{"type":"string","example":"Liverpool"},"country":{"type":"string","example":"England"},"is_mens_team":{"type":"boolean","nullable":true,"description":"true for a men's team, false for a women's team, null when unknown. Men's and women's sides of a club can share the same name; use this (or the `is_mens_team` filter on /football/teams) to tell them apart.","example":true},"primary_competition":{"type":"object","nullable":true,"properties":{"id":{"type":"string","example":"comp_3039"},"name":{"type":"string","example":"Premier League"}}}}},"TeamDetail":{"allOf":[{"$ref":"#/components/schemas/Team"},{"type":"object","properties":{"stadium":{"type":"object","properties":{"name":{"type":"string","example":"Anfield"},"capacity":{"type":"integer","example":61276},"city":{"type":"string","example":"Liverpool"}}}}}]},"TeamStats":{"type":"object","required":["team_id","season_id","competition_id","matches_played","wins","draws","losses","points","position","goals_for","goals_against","goal_difference","form"],"properties":{"team_id":{"type":"string","example":"tm_9145"},"season_id":{"type":"string","example":"sn_6125938"},"competition_id":{"type":"string","example":"comp_3039"},"matches_played":{"type":"integer","example":38},"wins":{"type":"integer","example":26},"draws":{"type":"integer","example":7},"losses":{"type":"integer","example":5},"points":{"type":"integer","example":85},"position":{"type":"integer","example":1},"goals_for":{"type":"integer","example":71},"goals_against":{"type":"integer","example":27},"goal_difference":{"type":"integer","example":44},"form":{"type":"string","description":"Results of the team's most recent finished matches in this season, up to five letters (`W`, `D` or `L`).","example":"WWWWW"},"expected_goals_for":{"type":"number","nullable":true,"example":18.42,"description":"Season xG for, summed over the matches counted in `xg_matches`. Null when no match in scope has xG (competitions without xG coverage)."},"expected_goals_against":{"type":"number","nullable":true,"example":11.07,"description":"Season xG against, summed over the matches counted in `xg_matches`. Null when no match in scope has xG."},"xg_matches":{"type":"integer","example":9,"description":"Number of matches with xG that the season totals are summed over. Can be lower than `matches_played` when some matches have no xG."}}},"Match":{"type":"object","required":["id","competition_id","season_id","matchday","stage_name","status","utc_date","home_team","away_team","score"],"properties":{"id":{"type":"string","example":"mt_838955483"},"competition_id":{"type":"string","example":"comp_3039"},"season_id":{"type":"string","example":"sn_6125938"},"matchday":{"type":"integer","nullable":true,"description":"League matchday / round. Null for cup or knockout fixtures where\nthe value is unavailable. Use `stage_name` for those instead.\n","example":37},"stage_name":{"type":"string","nullable":true,"description":"Named round for cup / knockout fixtures: `final`, `semi_final`,\n`quarter_final`, `round_of_16`, `round_of_32`, `round_of_64`,\n`knockout_playoff`, `group_stage`, `qualifying`. Null when the row is\na regular league fixture.\n","example":null},"group_label":{"type":"string","nullable":true,"description":"Group letter (A–L) for group-stage fixtures in competitions that\nsplit the group stage across tournament rows — FIFA World Cup,\nEURO, AFCON, pre-2024 UEFA Champions League group stage. Null\nwhen the match's tournament name does not match a group pattern\n(regular league fixtures, knockout rounds, etc.).\n","example":null},"status":{"type":"string","enum":["scheduled","live","finished","postponed","cancelled"],"example":"finished"},"utc_date":{"type":"string","format":"date-time","example":"2026-05-15T19:00:00.000Z"},"home_team":{"type":"object","properties":{"id":{"type":"string","example":"tm_1002"},"name":{"type":"string","example":"Aston Villa"}}},"away_team":{"type":"object","properties":{"id":{"type":"string","example":"tm_0406"},"name":{"type":"string","example":"Liverpool"}}},"score":{"type":"object","description":"Score for the fixture. `home`/`away` use normal-time goals for\nfinished matches and are null for matches that have not been\nplayed (`scheduled`, `postponed`, `cancelled`).\n\nFor matches that went beyond 90 minutes, read the explicit\nbreakdown: `regulation` (score at 90'), `after_extra_time`\n(score at 120'), `penalty_shootout` (shootout kicks), plus the\n`went_to_extra_time` / `went_to_penalties` flags and `winner`.\n","properties":{"home":{"type":"integer","nullable":true,"example":4},"away":{"type":"integer","nullable":true,"example":2},"final_score":{"type":"object","nullable":true,"example":null,"description":"Aggregate score, present when it differs from normal time.\nFor shootout matches it includes successful shootout kicks —\nsee `regulation` / `after_extra_time` / `penalty_shootout`\nfor the explicit breakdown.\n","properties":{"home":{"type":"integer"},"away":{"type":"integer"}}},"regulation":{"type":"object","nullable":true,"description":"Score at the end of regulation (90 minutes). Null until the\nmatch has finished.\n","properties":{"home":{"type":"integer","example":4},"away":{"type":"integer","example":2}}},"after_extra_time":{"type":"object","nullable":true,"example":null,"description":"Score after 120 minutes (goals only — shootout kicks are not\nincluded). Null when the match did not go to extra time.\n","properties":{"home":{"type":"integer"},"away":{"type":"integer"}}},"penalty_shootout":{"type":"object","nullable":true,"example":null,"description":"Penalty shootout result (successful kicks per side). Null\nwhen the match was not decided by a shootout.\n","properties":{"home":{"type":"integer"},"away":{"type":"integer"}}},"went_to_extra_time":{"type":"boolean","nullable":true,"description":"Whether extra time was played. False for straight-to-penalties\nformats (e.g. EFL Trophy). Null until the match has finished,\nor when it ended on penalties but coverage cannot confirm\nwhether extra time was played.\n","example":false},"went_to_penalties":{"type":"boolean","nullable":true,"description":"Null until the match has finished.","example":false},"winner":{"type":"string","nullable":true,"enum":["home","away","draw"],"description":"Overall winner including extra time / penalty shootout.\nNull until the match has finished.\n","example":"home"}}},"is_neutral":{"type":"boolean","nullable":true,"description":"True when the match is played at neither side's home ground — cup\nfinals (Community Shield, FA Cup final at Wembley), World Cup and\nother tournament fixtures, and one-off relocations.\n\n`home_team` / `away_team` remain pure side labels for scores, odds\nand stats and are never reordered, so read `is_neutral: true` as\n\"do not assume home advantage\".\n\nNull when the neutral status cannot be determined for the fixture.\nTreat it as unknown, never as `false`.\n","example":false},"live":{"allOf":[{"$ref":"#/components/schemas/MatchLiveClock"}],"type":"object","nullable":true,"description":"In-play clock. Null unless `status` is `live`.","example":null},"home_manager":{"allOf":[{"$ref":"#/components/schemas/MatchManager"}],"type":"object","nullable":true,"description":"Manager (head coach) recorded for the home side at this match.\nNull when none is recorded for the fixture.\n","example":{"id":"mgr_990303","name":"Unai Emery"}},"away_manager":{"allOf":[{"$ref":"#/components/schemas/MatchManager"}],"type":"object","nullable":true,"description":"Manager (head coach) recorded for the away side at this match.\nNull when none is recorded for the fixture.\n","example":{"id":"mgr_96180711","name":"Arne Slot"}},"odds_available":{"type":"boolean","description":"True when pre-match odds are available for this match. Older\nmatches usually have closing prices only. Always `false` for\npostponed and cancelled matches.\n","example":true},"live_odds_available":{"type":"boolean","description":"Indicates if live (in-play) odds are currently available for this\nmatch. Always `false` for `postponed` / `cancelled` matches.\n","example":false},"xg_available":{"type":"boolean","description":"True when the match has expected goals (xG) data. Use\n`shotmap_available` to know whether per-shot xG exists.\n","example":true},"shotmap_available":{"type":"boolean","description":"`true` only when `/shotmap` has at least one shot with a non-zero\nxG value. Can be `false` while `xg_available` is `true` (match-level\nxG exists but no per-shot data was provided).\n","example":true},"xg_quality":{"type":"string","enum":["full","none"],"description":"Quality of per-shot xG for this match.\n- `full`: shots carry per-shot xG values.\n- `none`: no per-shot xG for this match (`shotmap_available` is\n  `false`). Shots may still be listed in `/shotmap` with\n  `expected_goals: null`.\nNew values may be added in future; treat unknown values as `none`.\n","example":"full"}}},"MatchManager":{"type":"object","required":["id","name"],"properties":{"id":{"type":"string","description":"Stable manager id (`mgr_` prefix).","example":"mgr_990303"},"name":{"type":"string","example":"Unai Emery"}}},"MatchDetail":{"type":"object","properties":{"id":{"type":"string","example":"mt_838955483"},"competition_id":{"type":"string","example":"comp_3039"},"competition_name":{"type":"string","example":"Premier League"},"season_id":{"type":"string","example":"sn_6125938"},"matchday":{"type":"integer","nullable":true,"description":"League matchday / round. Null for cup or knockout fixtures.\n","example":37},"stage_name":{"type":"string","nullable":true,"description":"Named round for cup / knockout fixtures (`final`, `semi_final`,\n`quarter_final`, `round_of_16`, `round_of_32`, `round_of_64`,\n`knockout_playoff`, `group_stage`, `qualifying`). Null when the row is\na regular league fixture.\n","example":null},"group_label":{"type":"string","nullable":true,"description":"Group letter (A–L) for group-stage fixtures in competitions that\nsplit the group stage across tournament rows — FIFA World Cup,\nEURO, AFCON, pre-2024 UEFA Champions League group stage. Null\notherwise.\n","example":null},"status":{"type":"string","enum":["scheduled","live","finished","postponed","cancelled"],"example":"finished"},"utc_date":{"type":"string","format":"date-time","example":"2026-05-15T19:00:00.000Z"},"home_team":{"type":"object","properties":{"id":{"type":"string","example":"tm_1002"},"name":{"type":"string","example":"Aston Villa"}}},"away_team":{"type":"object","properties":{"id":{"type":"string","example":"tm_0406"},"name":{"type":"string","example":"Liverpool"}}},"score":{"type":"object","description":"Score; null fields for matches that have not been played. For\nmatches that went beyond 90 minutes, read the explicit breakdown:\n`regulation`, `after_extra_time`, `penalty_shootout`,\n`went_to_extra_time` / `went_to_penalties` and `winner` — same\nsemantics as `Match.score`.\n","properties":{"home":{"type":"integer","nullable":true,"example":4},"away":{"type":"integer","nullable":true,"example":2},"final_score":{"type":"object","nullable":true,"example":null,"description":"Aggregate score, present when it differs from normal time\n(includes successful shootout kicks) — see the explicit\nbreakdown fields for the split.\n","properties":{"home":{"type":"integer"},"away":{"type":"integer"}}},"regulation":{"type":"object","nullable":true,"description":"Score at 90 minutes. Null until finished.","properties":{"home":{"type":"integer","example":4},"away":{"type":"integer","example":2}}},"after_extra_time":{"type":"object","nullable":true,"example":null,"description":"Score after 120 minutes (goals only). Null when the match\ndid not go to extra time.\n","properties":{"home":{"type":"integer"},"away":{"type":"integer"}}},"penalty_shootout":{"type":"object","nullable":true,"example":null,"description":"Penalty shootout result (successful kicks per side). Null\nwhen the match was not decided by a shootout.\n","properties":{"home":{"type":"integer"},"away":{"type":"integer"}}},"went_to_extra_time":{"type":"boolean","nullable":true,"description":"Whether extra time was played. False for straight-to-penalties\nformats (e.g. EFL Trophy). Null until the match has finished,\nor when it ended on penalties but coverage cannot confirm\nwhether extra time was played.\n","example":false},"went_to_penalties":{"type":"boolean","nullable":true,"description":"Null until the match has finished.","example":false},"winner":{"type":"string","nullable":true,"enum":["home","away","draw"],"description":"Overall winner including extra time / penalty shootout.\nNull until the match has finished.\n","example":"home"},"half_time_home":{"type":"integer","nullable":true,"example":1},"half_time_away":{"type":"integer","nullable":true,"example":0}}},"venue":{"type":"object","nullable":true,"description":"Null for cancelled matches with no assigned venue.","properties":{"name":{"type":"string","example":"Villa Park"},"city":{"type":"string","example":"Birmingham"}}},"is_neutral":{"type":"boolean","nullable":true,"description":"True when the match is played at neither side's home ground — cup\nfinals (Community Shield, FA Cup final at Wembley), World Cup and\nother tournament fixtures, and one-off relocations.\n\n`home_team` / `away_team` remain pure side labels for scores, odds\nand stats and are never reordered, so read `is_neutral: true` as\n\"do not assume home advantage\".\n\nNull when the neutral status cannot be determined for the fixture.\nTreat it as unknown, never as `false`.\n","example":false},"referee":{"type":"object","nullable":true,"description":"Null when no referee is assigned to the match.","properties":{"id":{"type":"string","example":"ref_7264609"},"name":{"type":"string","example":"Chris Kavanagh"}}},"home_manager":{"allOf":[{"$ref":"#/components/schemas/MatchManager"}],"type":"object","nullable":true,"description":"Manager (head coach) recorded for the home side at this match.\nNull when none is recorded for the fixture.\n","example":{"id":"mgr_990303","name":"Unai Emery"}},"away_manager":{"allOf":[{"$ref":"#/components/schemas/MatchManager"}],"type":"object","nullable":true,"description":"Manager (head coach) recorded for the away side at this match.\nNull when none is recorded for the fixture.\n","example":{"id":"mgr_96180711","name":"Arne Slot"}},"odds_available":{"type":"boolean","description":"True when pre-match odds are available for this match. Older matches usually have closing prices only.","example":true},"live_odds_available":{"type":"boolean","description":"Indicates if live (in-play) odds are currently available for this match","example":false},"xg_available":{"type":"boolean","description":"Indicates if expected goals (xG) data is available for this match","example":true},"shotmap_available":{"type":"boolean","description":"`true` only when `/shotmap` has at least one shot with a non-zero\nxG value. Can be `false` while `xg_available` is `true` (match-level\nxG exists but no per-shot data was provided).\n","example":true},"xg_quality":{"type":"string","enum":["full","none"],"description":"Quality of per-shot xG for this match.\n- `full`: shots carry per-shot xG values.\n- `none`: no per-shot xG for this match (`shotmap_available` is\n  `false`). Shots may still be listed in `/shotmap` with\n  `expected_goals: null`.\nNew values may be added in future; treat unknown values as `none`.\n","example":"full"}}},"MatchRefereeResponse":{"type":"object","required":["match_id","referee"],"properties":{"match_id":{"type":"string","example":"mt_838955483"},"referee":{"type":"object","nullable":true,"description":"Null when no referee is assigned to the match","properties":{"id":{"type":"string","example":"ref_7264609"},"name":{"type":"string","example":"Chris Kavanagh"},"slug":{"type":"string","nullable":true,"example":"chris-kavanagh-ref_7264609"},"country":{"type":"string","example":"England"},"country_code":{"type":"string","nullable":true,"description":"ISO alpha-2 when available"},"country_slug":{"type":"string","nullable":true,"example":"england"},"career":{"type":"object","properties":{"games":{"type":"integer","example":322},"yellow_cards":{"type":"integer","example":1176},"red_cards":{"type":"integer","example":28},"yellow_red_cards":{"type":"integer","example":12}}}}}}},"StatValue":{"type":"object","properties":{"home":{"type":"number"},"away":{"type":"number"}}},"MatchStatItem":{"type":"object","description":"Stat for the full match and each half. `all`, `first_half` and\n`second_half` are `null` when no value was recorded for that period,\nso you never see a made-up `0`. A real 0–0 still appears as\n`{home: 0, away: 0}`.\n","properties":{"all":{"type":"object","nullable":true,"description":"Full-match total. `null` when no full-match value was recorded for this stat.","allOf":[{"$ref":"#/components/schemas/StatValue"}]},"first_half":{"type":"object","nullable":true,"allOf":[{"$ref":"#/components/schemas/StatValue"}]},"second_half":{"type":"object","nullable":true,"allOf":[{"$ref":"#/components/schemas/StatValue"}]}}},"MatchStats":{"type":"object","properties":{"match_id":{"type":"string","example":"mt_838955483"},"overview":{"type":"object","description":"Key match overview statistics","properties":{"ball_possession":{"$ref":"#/components/schemas/MatchStatItem"},"expected_goals":{"type":"object","nullable":true,"description":"Expected goals. `null` when the match has no xG data, never a made-up `0` (matches `xg_available: false` on the match).","allOf":[{"$ref":"#/components/schemas/MatchStatItem"}]},"big_chances":{"$ref":"#/components/schemas/MatchStatItem"},"total_shots":{"$ref":"#/components/schemas/MatchStatItem"},"shots_on_target":{"$ref":"#/components/schemas/MatchStatItem"},"goalkeeper_saves":{"$ref":"#/components/schemas/MatchStatItem"},"corner_kicks":{"$ref":"#/components/schemas/MatchStatItem"},"fouls":{"$ref":"#/components/schemas/MatchStatItem"},"yellow_cards":{"$ref":"#/components/schemas/MatchStatItem"},"red_cards":{"$ref":"#/components/schemas/MatchStatItem"},"passes":{"$ref":"#/components/schemas/MatchStatItem"},"accurate_passes":{"$ref":"#/components/schemas/MatchStatItem"},"tackles":{"$ref":"#/components/schemas/MatchStatItem"},"free_kicks":{"$ref":"#/components/schemas/MatchStatItem"}}},"shots":{"type":"object","description":"Detailed shot statistics","properties":{"total_shots":{"$ref":"#/components/schemas/MatchStatItem"},"shots_on_target":{"$ref":"#/components/schemas/MatchStatItem"},"shots_off_target":{"$ref":"#/components/schemas/MatchStatItem"},"blocked_shots":{"$ref":"#/components/schemas/MatchStatItem"},"shots_inside_box":{"$ref":"#/components/schemas/MatchStatItem"},"shots_outside_box":{"$ref":"#/components/schemas/MatchStatItem"},"hit_woodwork":{"$ref":"#/components/schemas/MatchStatItem"}}},"attack":{"type":"object","description":"Attacking statistics","properties":{"big_chances_missed":{"$ref":"#/components/schemas/MatchStatItem"},"touches_in_penalty_area":{"$ref":"#/components/schemas/MatchStatItem"},"fouled_in_final_third":{"$ref":"#/components/schemas/MatchStatItem"},"offsides":{"$ref":"#/components/schemas/MatchStatItem"}}},"passes":{"type":"object","description":"Passing statistics","properties":{"accurate_passes":{"$ref":"#/components/schemas/MatchStatItem"},"throw_ins":{"$ref":"#/components/schemas/MatchStatItem"},"accurate_crosses":{"$ref":"#/components/schemas/MatchStatItem"},"accurate_long_balls":{"$ref":"#/components/schemas/MatchStatItem"},"final_third_entries":{"$ref":"#/components/schemas/MatchStatItem"}}},"duels":{"type":"object","description":"Duel statistics","properties":{"duels_won_percentage":{"$ref":"#/components/schemas/MatchStatItem"},"dispossessed":{"$ref":"#/components/schemas/MatchStatItem"},"dribbles_percentage":{"$ref":"#/components/schemas/MatchStatItem"},"ground_duels_percentage":{"$ref":"#/components/schemas/MatchStatItem"},"aerial_duels_percentage":{"$ref":"#/components/schemas/MatchStatItem"}}},"defending":{"type":"object","description":"Defending statistics","properties":{"tackles":{"$ref":"#/components/schemas/MatchStatItem"},"tackles_won_percentage":{"$ref":"#/components/schemas/MatchStatItem"},"interceptions":{"$ref":"#/components/schemas/MatchStatItem"},"clearances":{"$ref":"#/components/schemas/MatchStatItem"},"ball_recoveries":{"$ref":"#/components/schemas/MatchStatItem"}}},"goalkeeping":{"type":"object","description":"Goalkeeping statistics","properties":{"saves":{"$ref":"#/components/schemas/MatchStatItem"},"goal_kicks":{"$ref":"#/components/schemas/MatchStatItem"},"goals_prevented":{"$ref":"#/components/schemas/MatchStatItem"},"high_claims":{"$ref":"#/components/schemas/MatchStatItem"}}},"np_expected_goals":{"type":"object","nullable":true,"description":"Non-penalty expected goals. `null` when the match has no non-penalty xG, never a made-up `0`.","allOf":[{"$ref":"#/components/schemas/MatchStatItem"}]}}},"MatchLiveClock":{"type":"object","description":"In-play clock for a live match. It agrees with the clock in\n`/live-stats`.\n\nEvery field is null when the clock isn't known yet. That is different\nfrom `live: null`, which means the match isn't in progress.\n","properties":{"match_status":{"type":"string","nullable":true,"description":"Where the match is right now.","enum":["not_started","first_half","halftime","second_half","extra_time","penalties","finished","suspended","in_progress"],"example":"second_half"},"elapsed_minutes":{"type":"integer","nullable":true,"description":"Minutes played as reported by the live feed. Counts continuously\nfrom kick-off rather than restarting each half, so a reading of\n`55` means the 55th minute of the match. Always a whole number —\nstoppage time is not broken out and there is no `45+3` form.\n","example":55},"period":{"type":"string","nullable":true,"description":"Current phase of the match. Null while `match_status` is\n`extra_time`, `penalties`, `suspended`, `not_started` or\n`in_progress` — read `match_status` for those.\n","enum":["first_half","halftime","second_half","finished"],"example":"second_half"},"updated_at":{"type":"string","format":"date-time","nullable":true,"description":"When the live feed last wrote this clock. Use it to spot a stalled\nfeed rather than assuming the minute is current.\n","example":"2026-09-02T15:45:02.477Z"}}},"LiveMatchStatItem":{"type":"object","description":"Live in-match statistic with full-match running tally.","properties":{"all":{"$ref":"#/components/schemas/StatValue"}}},"LiveMatchStatsMeta":{"type":"object","description":"Clock and score at the time of this snapshot.","properties":{"match_status":{"type":"string","nullable":true,"description":"Where the match is right now. `finished` is returned for up to 5 minutes after full-time, before the endpoint switches to `409`.","enum":["not_started","first_half","halftime","second_half","extra_time","penalties","finished","suspended","in_progress"],"example":"first_half"},"elapsed_minutes":{"type":"integer","nullable":true,"description":"Minutes played as reported by the live feed. Counts continuously\nfrom kick-off rather than restarting each half. Always a whole\nnumber — stoppage time is not broken out. The same value is\navailable per match on `/matches` as `live.elapsed_minutes`.\n","example":35},"home_goals":{"type":"integer","nullable":true,"example":0},"away_goals":{"type":"integer","nullable":true,"example":0},"ht_score":{"type":"string","nullable":true,"example":null},"period":{"type":"string","nullable":true,"description":"Current phase of the match.","enum":["first_half","halftime","second_half","finished"],"example":"first_half"}}},"LiveMatchStats":{"type":"object","description":"Running team stats for a live match. Stat names are the same as on the post-match `/stats` endpoint.","properties":{"match_id":{"type":"string","example":"mt_838955483"},"meta":{"$ref":"#/components/schemas/LiveMatchStatsMeta"},"stats":{"type":"object","description":"Full-match running totals keyed by stat name, e.g. `ball_possession`, `total_shots`, `corner_kicks`, `expected_goals`. Which stats appear depends on the match.","additionalProperties":{"$ref":"#/components/schemas/LiveMatchStatItem"},"example":{"ball_possession":{"all":{"home":47,"away":53}},"total_shots":{"all":{"home":2,"away":2}},"corner_kicks":{"all":{"home":0,"away":1}}}}}},"MatchPlayerStats":{"type":"object","properties":{"player_id":{"type":"string","example":"pl_08984824"},"player_name":{"type":"string","example":"Alexis Mac Allister"},"team_id":{"type":"string","example":"tm_0406"},"position":{"type":"string","enum":["","G","D","M","F"],"description":"Position: `G`, `D`, `M` or `F`, or empty when unknown.","example":"M"},"rating":{"type":"number","nullable":true,"example":6.09},"minutes_played":{"type":"integer","example":90},"started":{"type":"boolean","description":"Whether the player started the match","example":true},"played":{"type":"boolean","description":"Whether the player played any minutes","example":true},"passing":{"type":"object","properties":{"total_passes":{"type":"integer"},"accurate_passes":{"type":"integer"},"key_passes":{"type":"integer"},"assists":{"type":"integer"},"total_crosses":{"type":"integer"},"accurate_crosses":{"type":"integer"},"total_long_balls":{"type":"integer"},"accurate_long_balls":{"type":"integer"}}},"shooting":{"type":"object","properties":{"goals":{"type":"integer"},"total_shots":{"type":"integer"},"shots_on_target":{"type":"integer"},"shots_off_target":{"type":"integer"},"blocked_shots":{"type":"integer"},"big_chances_created":{"type":"integer"},"expected_goals":{"type":"number","nullable":true},"expected_assists":{"type":"number","nullable":true},"np_expected_goals":{"type":"number","nullable":true,"description":"Non-penalty expected goals"}}},"duels":{"type":"object","properties":{"duel_won":{"type":"integer"},"duel_lost":{"type":"integer"},"aerial_won":{"type":"integer"},"challenge_lost":{"type":"integer"},"won_contest":{"type":"integer","description":"Take-ons won"},"dispossessed":{"type":"integer"}}},"defending":{"type":"object","properties":{"tackles":{"type":"integer"},"interceptions":{"type":"integer"},"clearances":{"type":"integer"},"ball_recoveries":{"type":"integer","nullable":true,"description":"Ball recoveries by this player. `null` for older matches where recoveries weren't recorded; `0` means no recoveries. Sums to the team-level `defending.ball_recoveries` in `/stats`."}}},"goalkeeping":{"type":"object","properties":{"saves":{"type":"integer"}}},"general":{"type":"object","properties":{"touches":{"type":"integer"},"fouls":{"type":"integer"},"was_fouled":{"type":"integer"},"offsides":{"type":"integer"},"yellow_cards":{"type":"integer"},"red_cards":{"type":"integer"},"possession_lost":{"type":"integer"},"player_subbed_on":{"type":"string","nullable":true,"description":"Player ID of the player who was substituted on (replacing this player), or null if not subbed off","example":null},"player_subbed_off":{"type":"string","nullable":true,"description":"Player ID of the player who was substituted off (replaced by this player), or null if not a substitute","example":null}}}}},"LineupPlayer":{"type":"object","required":["id","name"],"properties":{"id":{"type":"string","example":"pl_5837699"},"name":{"type":"string","example":"Emiliano Martínez"},"position":{"type":"string","nullable":true,"description":"`G` (goalkeeper), `D` (defender), `M` (midfielder) or `F` (forward). Null when unknown.","example":"G"},"jersey_number":{"type":"integer","nullable":true,"example":23}}},"LineupTeam":{"type":"object","required":["id","name","starting_xi","substitutes"],"properties":{"id":{"type":"string","example":"tm_1002"},"name":{"type":"string","example":"Aston Villa"},"formation":{"type":"string","nullable":true,"description":"Formation, e.g. \"4-3-3\" or \"4-2-3-1\". Null when unknown.","example":"4-2-3-1"},"starting_xi":{"type":"array","description":"Players in the starting XI, sorted goalkeeper, defenders, midfielders, forwards, then by shirt number.","items":{"$ref":"#/components/schemas/LineupPlayer"}},"substitutes":{"type":"array","description":"Bench players, both those who came on and unused substitutes. Can be empty before kickoff, for example with a predicted XI.","items":{"$ref":"#/components/schemas/LineupPlayer"}}}},"Lineups":{"type":"object","required":["match_id","confirmed","type","home","away"],"properties":{"match_id":{"type":"string","example":"mt_838955483"},"confirmed":{"type":"boolean","description":"True for the official team sheet (announced about an hour before kickoff) or the XI of a started match. False for a predicted XI.","example":true},"type":{"type":"string","enum":["predicted","team_sheet","played"],"description":"`predicted`: projected XI before the team sheet is out. `team_sheet`: official pre-match XI. `played`: the XI and substitutes of a started or finished match.","example":"played"},"home":{"allOf":[{"$ref":"#/components/schemas/LineupTeam"}],"example":{"id":"tm_1002","name":"Aston Villa","formation":"4-2-3-1","starting_xi":[{"id":"pl_5837699","name":"Emiliano Martínez","position":"G","jersey_number":23}],"substitutes":[{"id":"pl_1379750","name":"Marco Bizot","position":"G","jersey_number":40}]}},"away":{"allOf":[{"$ref":"#/components/schemas/LineupTeam"}],"example":{"id":"tm_0406","name":"Liverpool","formation":"4-2-3-1","starting_xi":[{"id":"pl_30223540","name":"Giorgi Mamardashvili","position":"G","jersey_number":25}],"substitutes":[{"id":"pl_3847063","name":"Freddie Woodman","position":"G","jersey_number":28}]}}}},"HeatmapPoint":{"type":"object","required":["x","y"],"properties":{"x":{"type":"integer","minimum":0,"maximum":100,"example":41},"y":{"type":"integer","minimum":0,"maximum":100,"example":9}}},"HeatmapTeam":{"type":"object","required":["id","name","side"],"properties":{"id":{"type":"string","example":"tm_37201"},"name":{"type":"string","example":"Juventus"},"side":{"type":"string","enum":["home","away"]}}},"MatchPlayerHeatmap":{"type":"object","required":["match_id","player","team","points"],"properties":{"match_id":{"type":"string","example":"mt_404012971"},"player":{"type":"object","required":["id","name"],"properties":{"id":{"type":"string","example":"pl_84027040"},"name":{"type":"string","example":"Dušan Vlahović"}}},"team":{"$ref":"#/components/schemas/HeatmapTeam"},"points":{"type":"array","description":"Points in capture order. `x` and `y` are integer pitch percentages (0–100).","items":{"$ref":"#/components/schemas/HeatmapPoint"}}}},"SeasonHeatmapPoint":{"type":"object","required":["x","y","count"],"properties":{"x":{"type":"integer","minimum":0,"maximum":100,"example":41},"y":{"type":"integer","minimum":0,"maximum":100,"example":73},"count":{"type":"integer","minimum":1,"example":3,"description":"Number of touches at this grid cell aggregated across the season."}}},"SeasonHeatmapCompetition":{"type":"object","required":["id","name"],"properties":{"id":{"type":"string","example":"comp_6107"},"name":{"type":"string","example":"FIFA World Cup"}}},"SeasonHeatmapSeason":{"type":"object","required":["id","name"],"properties":{"id":{"type":"string","example":"sn_326766"},"name":{"type":"string","example":"World Cup 2022"}}},"PlayerSeasonHeatmap":{"type":"object","required":["player","competition","season","points"],"properties":{"player":{"type":"object","required":["id","name"],"properties":{"id":{"type":"string","example":"pl_84027040"},"name":{"type":"string","example":"Dušan Vlahović"}}},"competition":{"$ref":"#/components/schemas/SeasonHeatmapCompetition"},"season":{"$ref":"#/components/schemas/SeasonHeatmapSeason"},"points":{"type":"array","description":"Aggregated points. `x` / `y` are pitch percentages (0–100); `count` is touches per cell.","items":{"$ref":"#/components/schemas/SeasonHeatmapPoint"}}}},"ShotCoordinates":{"type":"object","properties":{"x":{"type":"number"},"y":{"type":"number"},"z":{"type":"number"}}},"ShotGoalkeeper":{"type":"object","properties":{"id":{"type":"string","example":"pl_30223540"},"name":{"type":"string","example":"Giorgi Mamardashvili"}}},"Shot":{"type":"object","properties":{"id":{"type":"string","example":"sh_613690689"},"player_id":{"type":"string","example":"pl_61849099"},"player_name":{"type":"string","example":"Ollie Watkins"},"team_id":{"type":"string","example":"tm_1002"},"team_name":{"type":"string","example":"Aston Villa"},"x":{"type":"number","description":"Distance from the attacking goal line on a 0–100 scale (0 = goal line the shot is aimed at, 100 = the shooter's own goal line). Every shot is normalised to attack the same goal regardless of team or half. The penalty area edge is at x = 16.5 and the penalty spot at x ≈ 11.","example":16.3},"y":{"type":"number","description":"Position across the pitch width on a 0–100 scale (50 = centre of the goal). The penalty area spans y = 20.5 to 79.5.","example":45.8},"minute":{"type":"integer","example":2},"result":{"type":"string","description":"Shot outcome, such as `goal`, `save`, `miss`, `block` or `post`","example":"save"},"expected_goals":{"type":"number","nullable":true,"description":"Per-shot expected goals (xG). `null` when there is no xG for the\nshot (see the match's `xg_quality`).\n","example":0.118},"situation":{"type":"string","nullable":true,"description":"Phase of play the shot came from. Values: assisted, regular, fast_break, corner, free_kick, set_piece, throw_in_set_piece, penalty, shootout, own_goal. corner, free_kick, set_piece and throw_in_set_piece are dead-ball situations; assisted, regular and fast_break are open play. penalty shots are also flagged by is_penalty and excluded from np_xg_summary.","example":"fast_break"},"body_part":{"type":"string","nullable":true,"example":"right_foot"},"goal_type":{"type":"string","nullable":true,"description":"Set only for goals (e.g. regular, own); null otherwise","example":null},"goal_mouth_location":{"type":"string","nullable":true,"example":"low_centre"},"goal_mouth_coordinates":{"allOf":[{"$ref":"#/components/schemas/ShotCoordinates"}],"type":"object","nullable":true,"example":{"x":0,"y":50.1,"z":5.7}},"block_coordinates":{"allOf":[{"$ref":"#/components/schemas/ShotCoordinates"}],"type":"object","nullable":true,"example":{"x":1.9,"y":50.2,"z":0}},"is_blocked_shot":{"type":"boolean","example":false},"blocked_by_player_id":{"type":"string","nullable":true,"example":null},"goalkeeper":{"allOf":[{"$ref":"#/components/schemas/ShotGoalkeeper"}],"type":"object","nullable":true},"is_goal":{"type":"boolean","example":false},"is_on_target":{"type":"boolean","example":true},"is_headed":{"type":"boolean","example":false},"is_outside_box":{"type":"boolean","description":"True when the shot was taken from outside the penalty area (`x > 16.5`, or `y` outside 20.5–79.5).","example":false},"is_penalty":{"type":"boolean","example":false}}},"NpXgTeamSummary":{"type":"object","properties":{"home_team":{"type":"number","example":2.397},"away_team":{"type":"number","example":1.572}}},"ShotmapResponse":{"type":"object","properties":{"match_id":{"type":"string","example":"mt_838955483"},"event":{"type":"object","properties":{"id":{"type":"string","example":"mt_838955483"},"home_team_id":{"type":"string","example":"tm_1002"},"away_team_id":{"type":"string","example":"tm_0406"}}},"data":{"type":"array","items":{"$ref":"#/components/schemas/Shot"}},"np_xg_summary":{"type":"object","description":"Non-penalty xG per team. `live` adds up the per-shot xG in `data` (penalties left out). `stored` is the match-level figure when one is recorded, otherwise the same as `live`.","properties":{"live":{"$ref":"#/components/schemas/NpXgTeamSummary"},"stored":{"$ref":"#/components/schemas/NpXgTeamSummary"}}},"meta":{"type":"object","properties":{"is_final":{"type":"boolean","description":"`false` while the match is live, so more shots may still be added (an empty `data` list then means \"no shots yet\"). `true` otherwise.","example":true}}}}},"TimelineEntityRef":{"type":"object","nullable":true,"description":"Reference to a team or player inside a timeline event. `id` is null when only the name is known.","properties":{"id":{"type":"string","nullable":true,"example":"pl_61849099"},"name":{"type":"string","example":"Ollie Watkins"},"slug":{"type":"string","nullable":true,"example":"ollie-watkins-pl_61849099"}}},"TimelineEvent":{"type":"object","required":["sequence","minute","extra_time","period","type","team","player"],"properties":{"sequence":{"type":"integer","description":"1-based chronological index. Iterate the array as-is.","example":3},"minute":{"type":"integer","description":"Regulation minute of the period (≤45 for first_half, ≤90 for second_half, etc). Stoppage minutes live in `extra_time`.","example":45},"extra_time":{"type":"integer","description":"Stoppage minutes added on top of `minute`. `0` for in-period events.","example":3},"period":{"type":"string","enum":["first_half","second_half","extra_time_first_half","extra_time_second_half","penalties"],"example":"first_half"},"type":{"type":"string","enum":["goal","shot_on_target","shot_off_target","shot_blocked","shot_saved","yellow_card","red_card","yellow_red_card","foul","offside","corner_kick","substitution","penalty_awarded","penalty_scored","penalty_missed","penalty_saved","var","period_start","period_end","added_time"],"example":"yellow_card"},"team":{"allOf":[{"$ref":"#/components/schemas/TimelineEntityRef"}],"example":{"id":"tm_1002","name":"Aston Villa","slug":"aston-villa-tm_1002"}},"player":{"$ref":"#/components/schemas/TimelineEntityRef"}}},"MatchTimelineResponse":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"object","required":["match_id","coverage","events"],"properties":{"match_id":{"type":"string","example":"mt_838955483"},"coverage":{"type":"string","enum":["full","none"],"example":"full"},"events":{"type":"array","items":{"$ref":"#/components/schemas/TimelineEvent"}}}},"meta":{"type":"object","required":["total","coverage"],"example":{"total":10,"coverage":"full","last_updated":"2026-05-15T21:05:49.659Z"},"properties":{"total":{"type":"integer","example":10},"coverage":{"type":"string","enum":["full","none"],"example":"full"},"reason":{"type":"string","enum":["match_not_started","no_events","not_supported"],"description":"Present only when `coverage` is `none`."},"last_updated":{"type":"string","format":"date-time","nullable":true,"example":"2026-05-15T21:05:49.659Z"}}}}},"Player":{"type":"object","required":["id","name","first_name","last_name","position","nationality","height_cm","current_team"],"properties":{"id":{"type":"string","example":"pl_29627593"},"name":{"type":"string","example":"Alexander Isak"},"first_name":{"type":"string","example":"Alexander"},"last_name":{"type":"string","example":"Isak"},"position":{"type":"string","enum":["","G","D","M","F"],"description":"Field position: `G` (goalkeeper), `D` (defender), `M`\n(midfielder), `F` (forward), or `\"\"` when unknown.\n","example":"F"},"date_of_birth":{"type":"string","format":"date","nullable":true,"description":"Null when the date of birth is unknown.","example":"1999-09-21"},"age":{"type":"integer","nullable":true,"description":"Null when the date of birth is unknown.","example":27},"nationality":{"type":"string","example":"Sweden"},"height_cm":{"type":"integer","nullable":true,"description":"Height in centimetres; `null` when unknown.","example":193},"current_team":{"type":"object","properties":{"id":{"type":"string","example":"tm_0406"},"name":{"type":"string","example":"Liverpool"},"jersey_number":{"type":"integer","nullable":true,"description":"Shirt number; `null` when unknown.","example":9}}}}},"SquadPlayer":{"allOf":[{"$ref":"#/components/schemas/Player"},{"type":"object","required":["slug","short_name","preferred_foot","country_slug","market_value","contract_until","national_team"],"properties":{"slug":{"type":"string","nullable":true,"example":"alexander-isak-pl_29627593"},"short_name":{"type":"string","nullable":true,"example":"A. Isak"},"preferred_foot":{"type":"string","nullable":true,"example":"right"},"country_slug":{"type":"string","nullable":true,"example":"sweden"},"market_value":{"type":"number","nullable":true,"description":"Market value when available","example":88000000},"contract_until":{"type":"string","format":"date","nullable":true,"example":"2031-06-30"},"national_team":{"type":"object","nullable":true,"properties":{"id":{"type":"string","example":"tm_86978"},"name":{"type":"string","example":"Sweden"}}}}}]},"PlayerInjury":{"type":"object","required":["player_id","status","reason","expected_return","start_date","active"],"properties":{"player_id":{"type":"string","example":"pl_40580885"},"status":{"type":"string","nullable":true,"description":"Injury severity (e.g. \"out\", \"day_to_day\").","example":"out"},"reason":{"type":"string","nullable":true,"description":"Injury cause code.","example":"leg_injury"},"start_date":{"type":"string","format":"date-time","nullable":true,"description":"When the player became injured.","example":"2026-09-29T00:00:00.000Z"},"expected_return":{"type":"string","format":"date-time","nullable":true,"description":"Estimated return date; null when open-ended/unknown. An estimate — injuries have no definite end date.","example":"2027-01-10T00:00:00.000Z"},"active":{"type":"boolean","description":"Whether the injury is in effect now. Past injuries, returned with `current=false`, have `active` false.","example":true}}},"PlayerSuspension":{"type":"object","required":["player_id","reason","matches","competition","start_date","end_date","active"],"properties":{"player_id":{"type":"string","example":"pl_01802892"},"reason":{"type":"string","nullable":true,"description":"Suspension cause code. `national_team` means the player is away with their national team and misses club matches; it is not a ban.","example":"red_card_suspension"},"matches":{"type":"integer","nullable":true,"description":"Number of matches banned.","example":1},"competition":{"type":"object","nullable":true,"description":"Competition the suspension applies to. The player is only banned from matches in this competition.","properties":{"id":{"type":"string","example":"comp_8814"},"name":{"type":"string","example":"LaLiga"}}},"start_date":{"type":"string","format":"date-time","nullable":true,"example":"2026-09-20T17:30:17.000Z"},"end_date":{"type":"string","format":"date-time","nullable":true,"example":"2026-10-10T22:00:00.000Z"},"active":{"type":"boolean","description":"Whether the suspension is in effect now. Past suspensions, returned with `current=false`, have `active` false.","example":true}}},"PlayerUnavailability":{"type":"object","description":"Player injuries and suspensions grouped by kind.","required":["injuries","suspensions"],"properties":{"injuries":{"type":"array","items":{"$ref":"#/components/schemas/PlayerInjury"}},"suspensions":{"type":"array","items":{"$ref":"#/components/schemas/PlayerSuspension"}}}},"PlayerStats":{"type":"object","properties":{"player_id":{"type":"string","example":"pl_45126714"},"season_id":{"type":"string","example":"sn_6125938"},"team_id":{"type":"string","example":"tm_9145"},"position":{"type":"string","enum":["","G","D","M","F"],"description":"Position: `G`, `D`, `M` or `F`, or empty when unknown.","example":"F"},"rating":{"type":"number","nullable":true,"example":7.64},"appearances":{"type":"integer","example":31},"starts":{"type":"integer","example":21},"minutes_played":{"type":"integer","example":2225},"scoring":{"type":"object","properties":{"goals":{"type":"integer"},"assists":{"type":"integer"},"goals_assists_sum":{"type":"integer"},"goal_conversion_percentage":{"type":"number"},"penalties_taken":{"type":"integer"},"penalty_goals":{"type":"integer"},"big_chances_created":{"type":"integer"},"big_chances_missed":{"type":"integer"},"expected_goals":{"type":"number","nullable":true,"example":5.31,"description":"Season xG summed over the appearances counted in `xg_matches`. Null when no appearance in scope has xG (competitions without xG coverage)."},"expected_assists":{"type":"number","nullable":true,"example":2.04,"description":"Season xA summed over the appearances counted in `xg_matches`. Null when no appearance in scope has xG."},"xg_matches":{"type":"integer","example":9,"description":"Number of appearances with xG that the season xG/xA are summed over. Can be lower than `appearances`."}}},"shooting":{"type":"object","properties":{"total_shots":{"type":"integer"},"shots_on_target":{"type":"integer"},"shots_off_target":{"type":"integer"}}},"passing":{"type":"object","properties":{"total_passes":{"type":"integer"},"accurate_passes":{"type":"integer"},"inaccurate_passes":{"type":"integer"},"pass_accuracy":{"type":"number","description":"Pass accuracy percentage"},"key_passes":{"type":"integer"},"accurate_crosses":{"type":"integer"},"accurate_crosses_percentage":{"type":"number"},"accurate_own_half_passes":{"type":"integer"},"accurate_opposition_half_passes":{"type":"integer"},"accurate_final_third_passes":{"type":"integer"}}},"defending":{"type":"object","properties":{"tackles":{"type":"integer"},"interceptions":{"type":"integer"}}},"duels":{"type":"object","properties":{"ground_duels_won":{"type":"integer"},"ground_duels_won_percentage":{"type":"number","nullable":true,"description":"Null when no percentage was recorded."},"aerial_duels_won":{"type":"integer"},"aerial_duels_won_percentage":{"type":"number","nullable":true,"description":"Null when no percentage was recorded."},"total_duels_won":{"type":"integer"},"total_duels_won_percentage":{"type":"number","nullable":true,"description":"Null when no percentage was recorded."},"successful_dribbles":{"type":"integer"},"successful_dribbles_percentage":{"type":"number","nullable":true,"description":"Null when no percentage was recorded."}}},"discipline":{"type":"object","properties":{"yellow_cards":{"type":"integer"},"red_cards":{"type":"integer"},"direct_red_cards":{"type":"integer"},"penalty_won":{"type":"integer"},"penalty_conceded":{"type":"integer"}}}}},"ExchangePriceLevel":{"type":"object","description":"One exchange depth level — decimal price and the monetary liquidity available to match at it.","properties":{"price":{"type":"number","example":1.96},"size":{"type":"number","example":5000.25}}},"OddsValue":{"type":"object","example":{"opening":"1.380","last_seen":"1.380"},"properties":{"opening":{"type":"string","nullable":true,"description":"Decimal odds when the bookmaker first listed the selection\n(first pre-match sighting). `null` when no opening price was\nrecorded for this line.\n","example":"1.750"},"last_seen":{"type":"string","example":"1.810"},"available_to_back":{"type":"array","description":"Betfair Exchange entries only: market depth, best price\nfirst, up to 3 levels per side. `last_seen` mirrors the best\nback price. An empty array means no unmatched offers on that\nside; the field is absent for fixed-odds bookmakers.\n","items":{"$ref":"#/components/schemas/ExchangePriceLevel"}},"available_to_lay":{"type":"array","description":"Betfair Exchange entries only — see `available_to_back`.","items":{"$ref":"#/components/schemas/ExchangePriceLevel"}}}},"OverUnderOdds":{"type":"object","properties":{"over":{"$ref":"#/components/schemas/OddsValue"},"under":{"$ref":"#/components/schemas/OddsValue"}}},"HomeDrawAwayOdds":{"type":"object","properties":{"home":{"$ref":"#/components/schemas/OddsValue"},"draw":{"$ref":"#/components/schemas/OddsValue"},"away":{"$ref":"#/components/schemas/OddsValue"}}},"HomeAwayOdds":{"type":"object","properties":{"home":{"$ref":"#/components/schemas/OddsValue"},"away":{"$ref":"#/components/schemas/OddsValue"}}},"YesNoOdds":{"type":"object","properties":{"yes":{"$ref":"#/components/schemas/OddsValue"},"no":{"$ref":"#/components/schemas/OddsValue"}}},"DoubleChanceOdds":{"type":"object","properties":{"home_draw":{"$ref":"#/components/schemas/OddsValue"},"home_away":{"$ref":"#/components/schemas/OddsValue"},"draw_away":{"$ref":"#/components/schemas/OddsValue"}}},"TeamOverUnderLines":{"type":"object","description":"Per-team over/under prices keyed by line (e.g. \"1.5\").","properties":{"home":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/OverUnderOdds"}},"away":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/OverUnderOdds"}}}},"MatchOddsMarkets":{"type":"object","description":"Every market is optional — it appears only when the bookmaker prices\nit for that match (e.g. shots markets are limited to bigger fixtures,\n`to_qualify` to knockout ties).\n","properties":{"match_odds":{"allOf":[{"$ref":"#/components/schemas/HomeDrawAwayOdds"}],"example":{"home":{"opening":"1.380","last_seen":"1.380"},"draw":{"opening":"4.500","last_seen":"5.000"},"away":{"opening":"7.500","last_seen":"7.500"}}},"btts":{"allOf":[{"$ref":"#/components/schemas/YesNoOdds"}],"example":{"yes":{"opening":"2.000","last_seen":"2.050"},"no":{"opening":"1.750","last_seen":"1.700"}}},"total_goals":{"type":"object","description":"Over/under prices keyed by goals line.","additionalProperties":{"$ref":"#/components/schemas/OverUnderOdds"},"example":{"2.5":{"over":{"opening":"1.725","last_seen":"1.800"},"under":{"opening":"2.075","last_seen":"2.000"}}}},"match_corners":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/OverUnderOdds"}},"total_cards":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/OverUnderOdds"}},"asian_handicap":{"type":"object","properties":{"home":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/OddsValue"},"example":{"-0.5":{"opening":"3.600","last_seen":"3.780"},"+0.0":{"opening":"2.750","last_seen":"2.720"}}},"away":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/OddsValue"},"example":{"+0.5":{"opening":"1.300","last_seen":"1.290"},"+0.0":{"opening":"1.480","last_seen":"1.500"}}}}},"draw_no_bet":{"$ref":"#/components/schemas/HomeAwayOdds"},"double_chance":{"$ref":"#/components/schemas/DoubleChanceOdds"},"first_half_result":{"allOf":[{"$ref":"#/components/schemas/HomeDrawAwayOdds"}],"example":{"home":{"opening":"1.833","last_seen":"1.909"},"draw":{"opening":"2.500","last_seen":"2.400"},"away":{"opening":"7.500","last_seen":"7.500"}}},"first_team_to_score":{"type":"object","description":"Present when the bookmaker prices it for the match.","properties":{"home":{"$ref":"#/components/schemas/OddsValue"},"away":{"$ref":"#/components/schemas/OddsValue"},"none":{"$ref":"#/components/schemas/OddsValue"}}},"second_half_result":{"$ref":"#/components/schemas/HomeDrawAwayOdds"},"half_time_double_chance":{"$ref":"#/components/schemas/DoubleChanceOdds"},"handicap_result":{"type":"object","description":"European (3-way) handicap keyed by the HOME team's handicap line\n(e.g. \"-1\", \"+2\").\n","additionalProperties":{"$ref":"#/components/schemas/HomeDrawAwayOdds"}},"team_total_goals":{"$ref":"#/components/schemas/TeamOverUnderLines"},"btts_first_half":{"$ref":"#/components/schemas/YesNoOdds"},"btts_second_half":{"$ref":"#/components/schemas/YesNoOdds"},"first_half_total_goals":{"type":"object","description":"First-half goals over/under prices keyed by line. Mostly .5 lines;\nsome bookmakers (e.g. Pinnacle) also quote integer and asian\nquarter lines (\"1\", \"1.25\").\n","additionalProperties":{"$ref":"#/components/schemas/OverUnderOdds"}},"total_goals_btts":{"type":"object","description":"Combined total goals + BTTS, keyed by goals line.","additionalProperties":{"type":"object","properties":{"over_yes":{"$ref":"#/components/schemas/OddsValue"},"over_no":{"$ref":"#/components/schemas/OddsValue"},"under_yes":{"$ref":"#/components/schemas/OddsValue"},"under_no":{"$ref":"#/components/schemas/OddsValue"}}}},"match_shots":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/OverUnderOdds"}},"match_shots_on_target":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/OverUnderOdds"}},"team_shots":{"$ref":"#/components/schemas/TeamOverUnderLines"},"team_shots_on_target":{"$ref":"#/components/schemas/TeamOverUnderLines"},"team_corners":{"$ref":"#/components/schemas/TeamOverUnderLines"},"correct_score":{"type":"object","description":"Scoreline (\"home-away\", e.g. \"2-1\") → odds.","additionalProperties":{"$ref":"#/components/schemas/OddsValue"}},"to_qualify":{"$ref":"#/components/schemas/HomeAwayOdds"},"a_penalty_in_match":{"$ref":"#/components/schemas/YesNoOdds"},"to_score_a_penalty":{"$ref":"#/components/schemas/HomeAwayOdds"},"to_miss_a_penalty":{"$ref":"#/components/schemas/HomeAwayOdds"}}},"BookmakerMatchOdds":{"type":"object","properties":{"bookmaker":{"type":"string","description":"One of Bet365, Paddy Power, BetMGM UK, Pinnacle, Betfair Exchange.","example":"Bet365"},"markets":{"$ref":"#/components/schemas/MatchOddsMarkets"}}},"MatchOdds":{"type":"object","properties":{"match_id":{"type":"string","example":"mt_200199332"},"bookmakers":{"type":"array","items":{"$ref":"#/components/schemas/BookmakerMatchOdds"}}}},"LiveOddsValue":{"type":"object","example":{"live":"3.500"},"properties":{"live":{"type":"string","example":"1.810"},"available_to_back":{"type":"array","description":"Betfair Exchange entries only: market depth, best price\nfirst, up to 3 levels per side. `live` mirrors the best back\nprice. An empty array means no unmatched offers on that side;\nthe field is absent for fixed-odds bookmakers.\n","items":{"$ref":"#/components/schemas/ExchangePriceLevel"}},"available_to_lay":{"type":"array","description":"Betfair Exchange entries only — see `available_to_back`.","items":{"$ref":"#/components/schemas/ExchangePriceLevel"}}}},"LiveOverUnderOdds":{"type":"object","properties":{"over":{"$ref":"#/components/schemas/LiveOddsValue"},"under":{"$ref":"#/components/schemas/LiveOddsValue"}}},"LiveHomeDrawAwayOdds":{"type":"object","properties":{"home":{"$ref":"#/components/schemas/LiveOddsValue"},"draw":{"$ref":"#/components/schemas/LiveOddsValue"},"away":{"$ref":"#/components/schemas/LiveOddsValue"}}},"LiveHomeAwayOdds":{"type":"object","properties":{"home":{"$ref":"#/components/schemas/LiveOddsValue"},"away":{"$ref":"#/components/schemas/LiveOddsValue"}}},"LiveYesNoOdds":{"type":"object","properties":{"yes":{"$ref":"#/components/schemas/LiveOddsValue"},"no":{"$ref":"#/components/schemas/LiveOddsValue"}}},"LiveDoubleChanceOdds":{"type":"object","properties":{"home_draw":{"$ref":"#/components/schemas/LiveOddsValue"},"home_away":{"$ref":"#/components/schemas/LiveOddsValue"},"draw_away":{"$ref":"#/components/schemas/LiveOddsValue"}}},"LiveTeamOverUnderLines":{"type":"object","properties":{"home":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/LiveOverUnderOdds"}},"away":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/LiveOverUnderOdds"}}}},"LiveMatchOddsMarkets":{"type":"object","description":"Mirrors `MatchOddsMarkets` with `{live}` price objects: every market\na bookmaker quotes in-play is served. Every key is optional — it\nappears only when the bookmaker quotes that market right now\n(in-play markets shrink and suspend as the match state changes).\n","properties":{"match_odds":{"allOf":[{"$ref":"#/components/schemas/LiveHomeDrawAwayOdds"}],"example":{"home":{"live":"3.500"},"draw":{"live":"2.400"},"away":{"live":"2.750"}}},"btts":{"allOf":[{"$ref":"#/components/schemas/LiveYesNoOdds"}],"example":{"yes":{"live":"3.400"},"no":{"live":"1.300"}}},"total_goals":{"type":"object","description":"Over/under prices keyed by goals line (quarter lines included in-play).","additionalProperties":{"$ref":"#/components/schemas/LiveOverUnderOdds"},"example":{"2.5":{"over":{"live":"4.500"},"under":{"live":"1.182"}}}},"match_corners":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/LiveOverUnderOdds"}},"total_cards":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/LiveOverUnderOdds"}},"asian_handicap":{"type":"object","properties":{"home":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/LiveOddsValue"},"example":{"-0.5":{"live":"2.670"},"+0.0":{"live":"1.750"}}},"away":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/LiveOddsValue"},"example":{"+0.5":{"live":"1.450"},"+0.0":{"live":"2.050"}}}}},"draw_no_bet":{"$ref":"#/components/schemas/LiveHomeAwayOdds"},"double_chance":{"$ref":"#/components/schemas/LiveDoubleChanceOdds"},"first_half_result":{"$ref":"#/components/schemas/LiveHomeDrawAwayOdds"},"first_team_to_score":{"type":"object","properties":{"home":{"$ref":"#/components/schemas/LiveOddsValue"},"away":{"$ref":"#/components/schemas/LiveOddsValue"},"none":{"$ref":"#/components/schemas/LiveOddsValue"}}},"second_half_result":{"$ref":"#/components/schemas/LiveHomeDrawAwayOdds"},"handicap_result":{"type":"object","description":"European (3-way) handicap keyed by the HOME team's handicap line\n(e.g. \"-1\", \"+2\").\n","additionalProperties":{"$ref":"#/components/schemas/LiveHomeDrawAwayOdds"}},"team_total_goals":{"$ref":"#/components/schemas/LiveTeamOverUnderLines"},"btts_first_half":{"$ref":"#/components/schemas/LiveYesNoOdds"},"btts_second_half":{"$ref":"#/components/schemas/LiveYesNoOdds"},"team_corners":{"$ref":"#/components/schemas/LiveTeamOverUnderLines"},"correct_score":{"type":"object","description":"Scoreline (\"home-away\", e.g. \"2-1\") → odds.","additionalProperties":{"$ref":"#/components/schemas/LiveOddsValue"}},"to_qualify":{"$ref":"#/components/schemas/LiveHomeAwayOdds"}}},"LiveBookmakerMatchOdds":{"type":"object","properties":{"bookmaker":{"type":"string","example":"Bet365"},"markets":{"$ref":"#/components/schemas/LiveMatchOddsMarkets"}}},"LiveMatchOdds":{"type":"object","properties":{"match_id":{"type":"string","example":"mt_200199332"},"bookmakers":{"type":"array","items":{"$ref":"#/components/schemas/LiveBookmakerMatchOdds"}}}},"MatchPlayerOddsEntry":{"type":"object","required":["id","name","odd"],"properties":{"id":{"type":"string","nullable":true,"description":"Player ID. Null when the player cannot be uniquely identified.","example":"pl_45126714"},"name":{"type":"string","example":"Bukayo Saka"},"line":{"type":"number","description":"Over/Under half-line for ladder markets (`player_shots`,\n`player_shots_on_target`, `player_shots_on_target_outside_box`,\n`player_headed_shots_on_target`, `player_tackles`,\n`player_fouls_committed`, `player_to_be_fouled`,\n`player_passes`, `goalkeeper_saves`: `0.5`, `1.5`, …).\nInteger \"N or more\"\nthresholds from bookmakers that publish them are normalized\nto `N - 0.5` Over lines. OMITTED for markets with no line\nconcept (goalscorer variants, cards, `player_of_the_match`, …).\n","example":1.5},"market_type":{"type":"string","enum":["Over","Under"],"description":"Direction of the quote. OMITTED for markets with no direction\nconcept (goalscorer variants, cards, …). Books price Over\nalmost exclusively today on player props.\n","example":"Over"},"odd":{"type":"number","description":"Latest decimal odds quoted by the bookmaker, rounded to 4 decimal places.","example":1.1818}}},"MatchPlayerOddsMarket":{"type":"object","required":["name","players"],"properties":{"name":{"type":"string","enum":["anytime_goalscorer","first_goalscorer","last_goalscorer","to_score_2_plus","to_score_3_plus","hattrick","first_half_goalscorer","second_half_goalscorer","to_score_header","to_score_outside_box","player_shots","player_shots_on_target","player_shots_on_target_outside_box","player_headed_shots_on_target","player_assists","score_or_assist","player_booked","player_sent_off","first_card","player_tackles","player_fouls_committed","player_to_be_fouled","player_passes","goalkeeper_saves","player_of_the_match"],"description":"Player-prop market name.","example":"player_shots"},"players":{"type":"array","description":"Entries for this market. Sorted by `(line asc nulls last, market_type asc nulls last, odd asc, name asc)`. Ladder markets emit one entry per `(player × line × market_type)`.","items":{"$ref":"#/components/schemas/MatchPlayerOddsEntry"}}}},"MatchPlayerOddsBookmaker":{"type":"object","required":["bookmaker","markets"],"properties":{"bookmaker":{"type":"string","description":"Bookmaker display name, same convention as match odds.","example":"Bet365"},"markets":{"type":"array","description":"Markets this bookmaker prices. Only markets with at least one price are included.","items":{"$ref":"#/components/schemas/MatchPlayerOddsMarket"}}}},"MatchPlayerOddsTeam":{"type":"object","required":["id","name"],"properties":{"id":{"type":"string","example":"tm_9145"},"name":{"type":"string","example":"Arsenal"}}},"MatchPlayerOdds":{"type":"object","required":["match_id","home_team","away_team","bookmakers"],"properties":{"match_id":{"type":"string","example":"mt_200199332"},"home_team":{"$ref":"#/components/schemas/MatchPlayerOddsTeam"},"away_team":{"allOf":[{"$ref":"#/components/schemas/MatchPlayerOddsTeam"}],"example":{"id":"tm_0256","name":"Leeds United"}},"kickoff_at":{"type":"string","format":"date-time","nullable":true,"description":"ISO-8601 kickoff timestamp; null when not recorded.","example":"2026-10-10T11:30:00.000Z"},"bookmakers":{"type":"array","description":"One entry per bookmaker with priced player props, Bet365 first. Bookmakers with no player props for the match are omitted; empty when no bookmaker prices any prop.","items":{"$ref":"#/components/schemas/MatchPlayerOddsBookmaker"}}}},"MatchPlayerOddsEntryV1":{"type":"object","required":["id","name","line","market_type","odd"],"properties":{"id":{"type":"string","nullable":true,"description":"Player ID. Null when the player cannot be uniquely identified.","example":"pl_45126714"},"name":{"type":"string","example":"Bukayo Saka"},"line":{"type":"number","nullable":true,"description":"Over/Under line, e.g. `0.5` or `1.5`. Null for markets with no\nline, such as `first_goalscorer`.\n","example":1.5},"market_type":{"type":"string","enum":["Over","Under"],"nullable":true,"description":"Direction of the quote. Null for markets with no line, such as\n`first_goalscorer`.\n","example":"Over"},"odd":{"type":"number","format":"float","example":1.182}}},"MatchPlayerOddsMarketV1":{"type":"object","required":["name","players"],"properties":{"name":{"type":"string","enum":["anytime_goalscorer","first_goalscorer","player_shots","player_shots_on_target","player_assists"],"description":"Player-prop market name. v1 has these 5 markets only.","example":"player_shots"},"players":{"type":"array","description":"Entries sorted by line, market_type, odd, then name.","items":{"$ref":"#/components/schemas/MatchPlayerOddsEntryV1"}}}},"MatchPlayerOddsV1":{"type":"object","required":["match_id","home_team","away_team","bookmaker","markets"],"properties":{"match_id":{"type":"string","example":"mt_200199332"},"home_team":{"$ref":"#/components/schemas/MatchPlayerOddsTeam"},"away_team":{"allOf":[{"$ref":"#/components/schemas/MatchPlayerOddsTeam"}],"example":{"id":"tm_0256","name":"Leeds United"}},"kickoff_at":{"type":"string","format":"date-time","nullable":true,"description":"ISO-8601 kickoff timestamp; null when not recorded.","example":"2026-10-10T11:30:00.000Z"},"bookmaker":{"type":"string","description":"Single bookmaker; v1 always returns \"bet365\".","example":"bet365"},"markets":{"type":"array","description":"Markets for the single bookmaker. Empty when the bookmaker has no priced player props for the match.","items":{"$ref":"#/components/schemas/MatchPlayerOddsMarketV1"}}}},"CoverageSeasonStatus":{"type":"string","description":"`complete`: every loaded match has been played. `in_progress`: some loaded matches are still to come. `not_loaded`: no matches are loaded for the season. Based on the matches loaded so far. More values may be added later.","enum":["complete","in_progress","not_loaded"]},"CoverageDataTypeEntry":{"type":"object","required":["available"],"properties":{"available":{"type":"boolean"},"covered_events":{"type":"integer","description":"Finished matches that have this data type. Only on `GET /coverage/leagues/{competition_id}`","example":380},"coverage_pct":{"type":"number","description":"Share of finished matches that have this data type, as a percentage with one decimal. Only on `GET /coverage/leagues/{competition_id}`","example":100}}},"CoverageDataTypes":{"type":"object","description":"Keyed by data type name. New keys may be added, so ignore keys you don't recognise.","properties":{"fixtures":{"$ref":"#/components/schemas/CoverageDataTypeEntry"},"team_stats":{"$ref":"#/components/schemas/CoverageDataTypeEntry"},"xg":{"$ref":"#/components/schemas/CoverageDataTypeEntry"},"odds":{"allOf":[{"$ref":"#/components/schemas/CoverageDataTypeEntry"}],"description":"Alias of closing_odds, kept for existing clients."},"opening_odds":{"allOf":[{"$ref":"#/components/schemas/CoverageDataTypeEntry"}],"description":"Opening prices (first price captured per selection) are stored. These fill the `opening` field on `/football/matches/{match_id}/odds`."},"closing_odds":{"allOf":[{"$ref":"#/components/schemas/CoverageDataTypeEntry"}],"description":"Closing prices (last pre-match price before kickoff) are stored. These fill the `last_seen` field on `/football/matches/{match_id}/odds`."},"lineups":{"$ref":"#/components/schemas/CoverageDataTypeEntry"},"player_stats":{"$ref":"#/components/schemas/CoverageDataTypeEntry"},"standings":{"$ref":"#/components/schemas/CoverageDataTypeEntry"}},"additionalProperties":{"$ref":"#/components/schemas/CoverageDataTypeEntry"}},"CoverageSeasonRef":{"type":"object","nullable":true,"properties":{"id":{"type":"string","example":"sn_8406098"},"year":{"type":"string","nullable":true,"example":"26/27"}}},"CoverageLeague":{"type":"object","properties":{"id":{"type":"string","example":"comp_3039"},"name":{"type":"string","example":"Premier League"},"country":{"type":"string","nullable":true,"example":"England"},"country_code":{"type":"string","nullable":true,"example":"GB"},"seasons":{"type":"object","description":"Seasons with matches loaded. `first` and `last` are not always the oldest and newest season; for the full list use `GET /coverage/leagues/{competition_id}`.","properties":{"count":{"type":"integer","example":13},"first":{"allOf":[{"$ref":"#/components/schemas/CoverageSeasonRef"}],"example":{"id":"sn_612289","year":"14/15"}},"last":{"$ref":"#/components/schemas/CoverageSeasonRef"}}},"data_types":{"allOf":[{"$ref":"#/components/schemas/CoverageDataTypes"}],"example":{"fixtures":{"available":true},"team_stats":{"available":true},"xg":{"available":true},"odds":{"available":true},"opening_odds":{"available":true},"closing_odds":{"available":true},"lineups":{"available":true},"player_stats":{"available":true},"standings":{"available":true}}},"latest_season":{"type":"object","nullable":true,"properties":{"id":{"type":"string","example":"sn_8406098"},"year":{"type":"string","nullable":true,"example":"26/27"},"status":{"$ref":"#/components/schemas/CoverageSeasonStatus"},"finished_events":{"type":"integer","example":50},"total_events":{"type":"integer","example":100}}},"total_finished_events":{"type":"integer","example":4610}}},"CoverageSeason":{"type":"object","properties":{"id":{"type":"string","example":"sn_6125938"},"name":{"type":"string","example":"Premier League 25/26"},"year":{"type":"string","nullable":true,"example":"25/26"},"status":{"$ref":"#/components/schemas/CoverageSeasonStatus"},"events":{"type":"object","properties":{"finished":{"type":"integer","example":380},"total":{"type":"integer","example":380}}},"data_types":{"allOf":[{"$ref":"#/components/schemas/CoverageDataTypes"}],"example":{"fixtures":{"available":true,"covered_events":380},"team_stats":{"available":true,"covered_events":380,"coverage_pct":100},"xg":{"available":true,"covered_events":380,"coverage_pct":100},"odds":{"available":true,"covered_events":380,"coverage_pct":100},"opening_odds":{"available":false,"covered_events":0,"coverage_pct":0},"closing_odds":{"available":true,"covered_events":380,"coverage_pct":100},"lineups":{"available":true,"covered_events":380,"coverage_pct":100},"player_stats":{"available":true,"covered_events":380,"coverage_pct":100},"standings":{"available":true}}}}},"CoverageLeagueDetail":{"type":"object","properties":{"id":{"type":"string","example":"comp_3039"},"name":{"type":"string","example":"Premier League"},"country":{"type":"string","nullable":true,"example":"England"},"country_code":{"type":"string","nullable":true,"example":"GB"},"total_seasons":{"type":"integer","description":"Every season we know of for the competition, including ones with no matches loaded","example":34},"loaded_seasons":{"type":"integer","description":"Seasons with at least one match loaded. Same as `seasons.count` in the list","example":13},"total_finished_events":{"type":"integer","example":4610},"seasons":{"type":"array","items":{"$ref":"#/components/schemas/CoverageSeason"}}}},"CoverageSummary":{"type":"object","properties":{"leagues":{"type":"integer","example":204},"seasons":{"type":"integer","example":1107},"finished_events":{"type":"integer","example":202269},"seasons_by_status":{"type":"object","properties":{"complete":{"type":"integer","example":922},"in_progress":{"type":"integer","example":152},"not_loaded":{"type":"integer","example":33}}},"pct_complete":{"type":"number","description":"Share of seasons that are `complete`, as a percentage with one decimal","example":83.3}}},"PaginationMeta":{"type":"object","properties":{"page":{"type":"integer","example":1},"per_page":{"type":"integer","example":20},"total":{"type":"integer","example":100},"total_pages":{"type":"integer","example":5}}},"Error":{"type":"object","description":"Every error uses this envelope. `code` is a stable UPPERCASE string\nto branch on: `BAD_REQUEST`, `UNKNOWN_PARAMETER`,\n`SEASON_COMPETITION_MISMATCH`, `PAGE_OUT_OF_RANGE` (400);\n`UNAUTHORIZED` (401); `FORBIDDEN`, `KEY_REVOKED`, `ADDON_REQUIRED`\n(403); `NOT_FOUND`, `STATS_NOT_AVAILABLE_AT_SOURCE` (404);\n`METHOD_NOT_ALLOWED` (405, every endpoint is `GET`); `CONFLICT`,\n`MATCH_IS_LIVE` (409); `RATE_LIMITED`, `USAGE_LIMIT_EXCEEDED` (429);\n`INTERNAL_SERVER_ERROR` (500). `message` explains the problem in\nplain English.\n","required":["error"],"properties":{"error":{"type":"object","required":["code","message","status_code"],"properties":{"code":{"type":"string","description":"UPPERCASE error code. See the list above.","example":"BAD_REQUEST"},"message":{"type":"string","description":"What went wrong, in plain English.","example":"season_id is required"},"status_code":{"type":"integer","description":"The HTTP status code, repeated in the body.","example":400}}}}}},"responses":{"TooManyRequests":{"description":"Too many requests: the per-minute rate limit (`RATE_LIMITED`) or the monthly quota (`USAGE_LIMIT_EXCEEDED`) is used up. Wait `Retry-After` seconds before retrying. The quota headers are sent here as on any other authenticated response.\n","headers":{"Retry-After":{"$ref":"#/components/headers/RetryAfter"},"X-RateLimit-Limit":{"$ref":"#/components/headers/XRateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/XRateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/XRateLimitReset"},"X-Monthly-Quota-Limit":{"$ref":"#/components/headers/XMonthlyQuotaLimit"},"X-Monthly-Quota-Remaining":{"$ref":"#/components/headers/XMonthlyQuotaRemaining"},"X-Monthly-Quota-Reset":{"$ref":"#/components/headers/XMonthlyQuotaReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"rate_limited":{"summary":"Per-minute rate limit reached","value":{"error":{"code":"RATE_LIMITED","message":"Rate limit exceeded. Please slow down your requests.","status_code":429}}},"usage_limit_exceeded":{"summary":"Monthly quota used up","value":{"error":{"code":"USAGE_LIMIT_EXCEEDED","message":"Monthly usage limit exceeded. Please upgrade your plan.","status_code":429}}}}}}},"BadRequest":{"description":"The request is invalid. `UNKNOWN_PARAMETER` means you sent a query parameter this endpoint doesn't accept; the message suggests the closest name and lists the accepted ones. Otherwise `code` is usually `BAD_REQUEST` and `message` says which parameter is wrong.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"unknown_parameter":{"summary":"Unknown query parameter (from /football/matches)","value":{"error":{"code":"UNKNOWN_PARAMETER","message":"Unknown query parameter 'date' (did you mean 'date_from' or 'date_to'?). Supported parameters: page, per_page, limit, competition_id, season_id, team_id, date_from, date_to, utc_offset, matchday, status, stage, group, sort.","status_code":400}}},"missing_parameter":{"summary":"Missing required parameter","value":{"error":{"code":"BAD_REQUEST","message":"season_id is required","status_code":400}}},"season_mismatch":{"summary":"Season from a different competition","value":{"error":{"code":"SEASON_COMPETITION_MISMATCH","message":"season_id does not belong to competition_id","status_code":400}}}}}}},"Unauthorized":{"description":"The API key is missing or invalid. Send it as `Authorization: Bearer YOUR_API_KEY`.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"missing_header":{"summary":"No Authorization header","value":{"error":{"code":"UNAUTHORIZED","message":"Missing authorization header","status_code":401}}},"invalid_key":{"summary":"Unknown API key","value":{"error":{"code":"UNAUTHORIZED","message":"Invalid API key","status_code":401}}}}}}},"NotFound":{"description":"The ID or route doesn't exist, or there is no data of this kind for it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"NOT_FOUND","message":"Match not found","status_code":404}}}}},"StatsNotFound":{"description":"No stats for this match. `STATS_NOT_AVAILABLE_AT_SOURCE` means the match was only covered at score and lineup level, so detailed stats don't exist and never will. `NOT_FOUND` means the match doesn't exist or its stats aren't in yet.\n","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["NOT_FOUND","STATS_NOT_AVAILABLE_AT_SOURCE"]},"message":{"type":"string"},"status_code":{"type":"integer","example":404}}}}},"examples":{"not_available":{"summary":"Detailed stats were never collected","value":{"error":{"code":"STATS_NOT_AVAILABLE_AT_SOURCE","message":"Detailed statistics for this match do not exist at the data source","status_code":404}}},"not_found":{"summary":"Stats not found","value":{"error":{"code":"NOT_FOUND","message":"Match statistics not found","status_code":404}}}}}}},"Conflict":{"description":"The match is in the wrong state for this endpoint: a live endpoint was called for a match that isn't live, or a post-match endpoint was called while the match is live. Switch to the matching live or post-match endpoint.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"match_not_live":{"summary":"Live endpoint, match not live","value":{"error":{"code":"CONFLICT","message":"Match is not live","status_code":409}}},"match_is_live":{"summary":"Post-match timeline, match is live","value":{"error":{"code":"CONFLICT","message":"Match is live; use the live timeline endpoint","status_code":409}}},"stats_match_is_live":{"summary":"Post-match stats, match is live","value":{"error":{"code":"MATCH_IS_LIVE","message":"Match is currently live — use /matches/{id}/live-stats for in-play statistics","status_code":409}}}}}}}}}}