# Stats API > A versioned, cache-first REST API for football fixtures, results, competitions, seasons, and teams. Canonical site: https://stats-api.com Canonical API base URL: https://stats-api.com/api/v1 OpenAPI 3.1 specification: https://stats-api.com/openapi.json Human documentation: https://stats-api.com/docs Direct HTTP examples (Python, JavaScript, PHP, Go, Java, and C#; not SDK packages): https://stats-api.com/docs#client-examples Complete agent bundle: https://stats-api.com/llms-full.txt Coverage: https://stats-api.com/coverage Data-source and rights ledger: https://stats-api.com/data-sources Engineering blog: https://stats-api.com/blog Frequently asked questions: https://stats-api.com/faq Changelog and service history: https://stats-api.com/changelog Authentication guide: https://stats-api.com/docs/authentication Rate-limit guide: https://stats-api.com/docs/rate-limits Error guide: https://stats-api.com/docs/errors Launch status: API access is open. Account signup is open. ## Authentication Send the API key as a Bearer token: Authorization: Bearer $STATS_API_KEY Never put an API key in a query string or client-side public code. Keys are shown once when created and stored as SHA-256 hashes by Stats API. ## Available endpoints - GET /api/v1/health — unauthenticated service health - GET /api/v1/coverage/summary - GET /api/v1/coverage/leagues - GET /api/v1/coverage/leagues/{competition_id} - GET /api/v1/football/competitions - GET /api/v1/football/competitions/{competition_id} - GET /api/v1/football/competitions/{competition_id}/seasons - GET /api/v1/football/matches - GET /api/v1/football/matches/{match_id} - GET /api/v1/football/teams - GET /api/v1/football/teams/{team_id} Collection endpoints accept page and limit. Limit is capped at 100. Match filters are competition_id, season_id, team_id, status, date_from, and date_to. Team and competition collections accept q where documented. ## Contract and freshness Public IDs are stable Stats API IDs and do not expose an upstream provider's identifiers. Customer requests read Redis and normalized MySQL data; they never call an upstream provider directly. Responses include X-Request-ID, X-StatsAPI-Cache, rate-limit, and monthly-quota headers. Every request admitted through the per-minute limit counts once toward the billing-period quota, including a Redis cache hit and an application-level error produced after authentication. Authentication failures and requests rejected by the per-minute limiter do not consume the billing-period quota. Two sources are enabled. OpenFootball football.json supplies community-maintained competitions, seasons, teams, fixtures, and full-time results under CC0. API-Football supplies competitions, seasons, teams, fixtures, results, match events, lineups, team and player match statistics including expected goals, standings, players, and injuries under a commercial redistribution agreement. Coverage is not uniform. Fixtures and results reach far more competitions than statistics do, and every statistics response reports its own coverage flag rather than failing. Ingestion is scheduled rather than streamed, so this is not a guaranteed live feed and no maximum data age is promised. Odds, shotmaps, and heatmaps are not available. Do not infer guaranteed freshness from an ingestion timestamp: it records only when our importer ran. Only fields and endpoints present in the OpenAPI specification are the executable contract. Planned operations in the coverage map are not available endpoints. ## Pagination, limits, and errors Collection endpoints default to page=1 and limit=25; limit must be between 1 and 100. Match date filters are inclusive UTC dates in YYYY-MM-DD format. Inspect X-RateLimit-Limit, X-RateLimit-Remaining, X-Quota-Limit, X-Quota-Remaining, and X-Quota-Reset. Do not retry 400, 401, 403, 404, 405, or 422 without changing the request. A 405 response requires changing the request to GET. Retry 429 only after the applicable reset with jitter. Retry 503 with bounded exponential backoff. Preserve X-Request-ID when reporting a failure. ## Response shape Successful item: {"data": {...}} Successful collection: {"data": [...], "meta": {"page": 1, "limit": 25, "request_id": "..."}} Error: {"error": {"code": "machine_readable_code", "message": "Human-readable message", "details": {}}} ## Safe integration prompt Treat the OpenAPI document embedded in this bundle, or https://stats-api.com/openapi.json, as authoritative. Generate a server-side client that reads the API key from the STATS_API_KEY environment variable, sends the Authorization Bearer header, handles 401, 403, 405, 429, and 503 responses, honors rate-limit and monthly-quota headers, and uses only operations present in the specification. Do not invent fields, sources, freshness, or planned endpoints. Never place the key in a model prompt, browser bundle, URL, log, or generated example. ## Complete human developer guides The following sections mirror the detailed public guides under `/docs/`. ### API authentication Public URL: https://stats-api.com/docs/authentication Stats API uses bearer API keys. Keep them on a trusted server, send them in the Authorization header, and rotate each integration independently. #### Send a key Create a named key in the dashboard. The full value is shown once; Stats API stores only its SHA-256 hash. Send the key in the HTTP Authorization header. Query-string keys are not supported because URLs commonly appear in logs, analytics, and browser history. ```text curl "https://stats-api.com/api/v1/football/competitions?limit=10" \ -H "Authorization: Bearer $STATS_API_KEY" ``` #### Key lifecycle Use one key per deployed application or environment so a compromised integration can be revoked without interrupting the others. - Store keys in server-side environment variables or a secret manager. - Never commit a key, put it in client-side JavaScript, or paste it into a public issue. - Create a replacement before revoking an active key when rotating without downtime. - A user may keep up to ten active keys. Revoked keys remain visible for audit history and do not count toward that active-key limit. #### Authentication errors | HTTP | Code | Meaning | | --- | --- | --- | | 401 | invalid_api_key | The bearer credential is missing, malformed, unknown, or revoked. | | 403 | subscription_inactive | The key is valid, but its account has no current API entitlement. Keep the key and restore or choose a plan. | | 429 | rate_limit | The per-minute allowance has been exhausted. | | 429 | monthly_quota | The subscription request allowance has been exhausted. | ### Rate limits and monthly quota Public URL: https://stats-api.com/docs/rate-limits Each plan has a short-window request rate and a billing-period quota. Both are enforced atomically before a football-data response is returned. #### What counts Every request admitted through the per-minute limit counts once toward the billing-period quota, including a Redis cache hit and an application-level error produced after authentication. Authentication failures and requests rejected by the per-minute limiter do not consume the billing-period quota. Provider refresh traffic is internal ingestion work and is never billed to a customer. #### Response headers | Header | Meaning | | --- | --- | | X-RateLimit-Limit | Maximum requests in the current minute. | | X-RateLimit-Remaining | Requests left in that minute. | | X-RateLimit-Reset | Unix timestamp for the next minute window. | | X-Quota-Limit | Maximum requests in the current billing period. | | X-Quota-Remaining | Requests left in the billing period. | | X-Quota-Reset | Unix timestamp when the billing-period counter resets. | | Retry-After | Seconds until the applicable reset on an HTTP 429 response. | | X-StatsAPI-Cache | HIT or MISS for the versioned response cache. | #### Handle HTTP 429 Stop sending requests when a 429 response arrives. Retry after the next minute boundary for a rate-limit error; a depleted monthly quota requires the billing period to reset or the account plan to change. Use Retry-After for delay duration and the matching X-RateLimit-Reset or X-Quota-Reset timestamp for scheduling. Add jitter for unexpected bursts and cache stable responses in your own application when appropriate. ### API errors Public URL: https://stats-api.com/docs/errors Every API error has an HTTP status plus a stable code, human-readable message, and optional details object. #### Error shape ```text { "error": { "code": "invalid_parameter", "message": "limit must be between 1 and 100.", "details": { "parameter": "limit" } } } ``` #### Status codes | HTTP | Use | | --- | --- | | 401 · invalid_api_key | Bearer key is missing, malformed, unknown, or revoked. | | 403 · subscription_inactive | Bearer key is valid, but its account has no current API entitlement. | | 404 · not_found | Endpoint or stable resource ID was not found. | | 405 · method_not_allowed | HTTP method is not supported for this route. | | 422 · invalid_parameter | A documented query parameter has an invalid value. | | 422 · unknown_parameter | A query parameter name is outside the documented contract. | | 429 · rate_limit | Per-minute request allowance is exhausted. | | 429 · monthly_quota | Billing-period request allowance is exhausted. | | 503 · service_unavailable | A required service such as the normalized store is unavailable. | #### Retry policy - Do not retry 401, 403, 404, 405, or 422 without changing credentials, billing state, or the request. - Retry 429 only after the applicable reset, with jitter. - Retry 503 with bounded exponential backoff. - Log the response request ID when present so support can trace a failure. ### Competitions endpoints Public URL: https://stats-api.com/docs/endpoints/competitions Competition resources are the entry point for league, cup, and tournament discovery. Stats API IDs are independent of the source format; a provider migration still requires reviewed mappings. #### Season standings Standings are published per season rather than computed from results. That is deliberate: a table derived purely from match scores will disagree with the official one whenever a points deduction, an awarded result, or an expunged record applies — and it will disagree silently. Tiebreakers also vary by competition. The Premier League orders on goal difference; La Liga resolves ties on head-to-head record first. Reading the published standing avoids reimplementing each competition's rulebook. | Method | Path | Purpose | | --- | --- | --- | | GET | /api/v1/football/competitions/{competition_id}/seasons/{season_id}/standings | Ranked table for one season. | | GET | /api/v1/football/teams/{team_id}/standings | Current-season positions for one team. | #### Worked example: finding a season and its table Competition and season identifiers are Stats API values, not upstream ones. Resolve a name to an ID once, store the ID, and use it in later requests — that is what keeps an integration stable across a provider change. ```text GET /api/v1/football/competitions?q=premier%20league&limit=5 Authorization: Bearer sapi_live_... { "data": [ { "id": "comp_4f2a91c7de03", "name": "Premier League", "country_name": "England", "type": "league", "season_count": 17 } ], "meta": { "page": 1, "limit": 5, "total": 1 } } GET /api/v1/football/competitions/comp_4f2a91c7de03/seasons { "data": [ { "id": "season_31a7c0d94b26", "name": "2026", "is_current": true, "team_count": 20, "match_count": 380 } ] } ``` #### Standing field semantics | Field | Type | Meaning | | --- | --- | --- | | position | integer | Rank within the group, applying the competition's own tiebreakers. | | group | string or null | Group label for competitions with a group stage; null for a single table. | | played / won / drawn / lost | integer | Matches counted toward this standing. | | goals_for / goals_against | integer | Goals in counted matches. | | goal_difference | integer | Convenience field: goals_for minus goals_against. | | points | integer | Includes any deduction the competition has applied. | | form | string or null | Recent results, most recent last, where the source supplies it. | | description | string or null | Qualification or relegation note, e.g. Champions League. | #### Things that surprise integrators | Situation | What to expect | | --- | --- | | A season shows is_current false for every season | Correct for archived datasets. Do not assume exactly one current season exists. | | season_count is high but few have data | The catalog lists seasons the source knows about; coverage varies per season. | | Standings are empty for a covered competition | Group-stage or cup formats may have no table. Check the coverage flag rather than inferring failure. | | Points do not match played results | A deduction or awarded result has been applied. The published standing is authoritative. | #### Available operations | Method | Path | Purpose | | --- | --- | --- | | GET | /api/v1/football/competitions | Paginated competition catalog. | | GET | /api/v1/football/competitions/{competition_id} | One competition by Stats API public ID. | | GET | /api/v1/football/competitions/{competition_id}/seasons | Seasons belonging to a competition. | #### Collection filters The collection accepts q for a case-insensitive name search plus page and limit for pagination. Limit defaults to 25 and is capped at 100. ```text GET /api/v1/football/competitions?q=premier&page=1&limit=25 ``` #### Public IDs Persist the public competition_id in your own records. Identical imports keep it unchanged. Do not infer an ID from a name or slug; ambiguous corrections or a provider migration without an approved mapping may require a new resource ID. ### Matches endpoints Public URL: https://stats-api.com/docs/endpoints/matches The match collection covers scheduled fixtures and completed full-time results from the currently enabled datasets. Per-match team statistics — corners, shots, possession, cards, passes and expected goals — are available separately at /football/matches/{match_id}/stats for covered competitions. #### Available operations | Method | Path | Purpose | | --- | --- | --- | | GET | /api/v1/football/matches | Filter and paginate fixtures or results. | | GET | /api/v1/football/matches/{match_id} | Retrieve one match by Stats API public ID. | #### Match detail operations A match resource is deliberately small. Richer detail lives on dedicated sub-resources so a client polling scores every thirty seconds is not also transferring lineups and per-player statistics it already has. Each detail route reports its own coverage. A competition without statistics coverage returns the match with an explicit coverage flag and an empty collection rather than a 404 — an empty result and an unsupported competition are different answers, and the response says which one you received. | Method | Path | Purpose | | --- | --- | --- | | GET | /api/v1/football/matches/{match_id}/stats | Team statistics: corners, shots, possession, cards, passes, expected goals. | | GET | /api/v1/football/matches/{match_id}/live-stats | Same shape, cached briefly for matches in progress. | | GET | /api/v1/football/matches/{match_id}/lineups | Starting eleven, bench, formation, coach. | | GET | /api/v1/football/matches/{match_id}/timeline | Ordered goals, cards, substitutions, VAR decisions. | | GET | /api/v1/football/matches/{match_id}/live-timeline | Same events, short cache, for in-play matches. | | GET | /api/v1/football/matches/{match_id}/player-stats | Per-player minutes, rating and canonical statistics. | | GET | /api/v1/football/matches/{match_id}/live-player-stats | Same shape for matches in progress. | #### Worked example: reading corners Request team statistics for a single match. Every documented field is always present; a statistic the source did not supply is null rather than absent, so a client never has to distinguish a missing key from a missing value. ```text GET /api/v1/football/matches/match_a14c38236c0e/stats Authorization: Bearer sapi_live_... { "data": { "match_id": "match_a14c38236c0e", "status": "finished", "coverage": { "statistics_available": true }, "teams": [ { "team": { "id": "team_f2c85c3c8cb2", "name": "Colorado Rapids" }, "statistics": { "corners": 7, "shots_total": 14, "shots_on_target": 4, "possession_percent": 64, "passes_accuracy_percent": 86, "expected_goals": 1.22, "red_cards": null } } ] } } ``` #### Statistic field semantics Field names are canonical to Stats API, not inherited from any upstream provider. That is deliberate: a provider change must not rename a field your code reads. | Field | Type | Meaning | | --- | --- | --- | | corners | integer | Corner kicks awarded to the team. | | shots_total | integer | All goal attempts, including blocked and off target. | | shots_on_target | integer | Attempts that would enter the goal without intervention. | | shots_inside_box | integer | Attempts taken inside the penalty area. | | possession_percent | number | Share of possession, 0-100. Both teams sum to roughly 100. | | passes_accuracy_percent | number | Completed passes as a percentage of attempted. | | expected_goals | number | Cumulative xG. Available where the source supplies it. | | yellow_cards / red_cards | integer | Cards shown. Null means not reported, not zero. | #### Error cases and how to tell them apart Three outcomes look similar from the outside and mean different things. Handling them identically is the most common integration mistake. | Situation | Response | Correct handling | | --- | --- | --- | | Match ID does not exist | 404 not_found | The identifier is wrong. Do not retry. | | Competition has no statistics coverage | 200 with coverage.statistics_available false and empty teams | Show "not covered". Do not treat as an error or retry. | | Match has not been played | 200 with empty statistics | Poll again after full time. | | Key lacks an active entitlement | 403 subscription_inactive | Renew the plan; retrying will not help. | | Per-minute limit exceeded | 429 rate_limit with Retry-After | Back off for the stated interval. | #### Filters ```text GET /api/v1/football/matches?team_id=tm_example&date_from=2026-08-01&date_to=2026-08-31 ``` | Parameter | Format | | --- | --- | | competition_id | Stats API competition ID. | | season_id | Stats API season ID. | | team_id | Matches where the team is home or away. | | status | scheduled, live, finished, postponed, canceled, or unknown. | | date_from | Inclusive YYYY-MM-DD UTC date. | | date_to | Inclusive YYYY-MM-DD UTC date. | | page / limit | Positive page; limit 1–100. | #### Freshness boundary OpenFootball is a community-maintained historical fixtures and results source, not a guaranteed live-score feed. A match updated_at value records when that normalized row last changed during Stats API ingestion; it is not a timestamp published by the source, and an identical re-import does not advance it. The beta serves each dataset as Stats API observed it at the latest successful ingestion, with no promised maximum age or refresh interval. GET /api/v1/coverage/summary reports dataset-level last_success_at, artifact_retrieved_at, and records_seen. For OpenFootball, source_updated_at is null and freshness_guaranteed is false. Each ingestion run separately records the retrieved artifact SHA-256, byte count, and retrieval time for audit. Retrieval and success times prove only what Stats API imported and when, not when the football facts became current. Live incidents remain outside the beta contract. ### Teams endpoints Public URL: https://stats-api.com/docs/endpoints/teams Team resources use Stats API identifiers so customer integrations do not inherit the naming and ID conventions of an upstream source. #### Squad, standings and absences A team resource carries identity only. Squad membership, table position and reported absences are separate sub-resources, because each changes on a different cadence: identity is effectively static, a table moves weekly, and an injury list moves daily. | Method | Path | Purpose | | --- | --- | --- | | GET | /api/v1/football/teams/{team_id}/players | Current-season squad with shirt number and position. | | GET | /api/v1/football/teams/{team_id}/standings | Current-season table positions. | | GET | /api/v1/football/teams/{team_id}/injuries-suspensions | Reported absences, where covered. | #### Worked example: resolving a team once Search by name once, then keep the Stats API identifier. Team names are not stable keys — clubs rename, and the same club is spelled differently by different sources. The identifier is the thing that does not move. ```text GET /api/v1/football/teams?q=arsenal&limit=3 Authorization: Bearer sapi_live_... { "data": [ { "id": "team_9f771cb2e781", "name": "Arsenal", "country_name": "England", "logo_url": "https://..." } ], "meta": { "page": 1, "limit": 3, "total": 1 } } GET /api/v1/football/teams/team_9f771cb2e781/players { "data": [ { "id": "player_9c1b2f4ea77d", "name": "Example Player", "shirt_number": 8, "position": "Midfielder", "season": { "id": "season_31a7c0d94b26", "name": "2026" } } ], "meta": { "count": 1 } } ``` #### Coverage, and why a list can be empty Squad and absence data are not available for every competition. Injury reporting in particular is sparse: most competitions supply none at all. An empty list is therefore an ordinary answer, and the response distinguishes the two reasons it can occur. Treating both as an error produces false alarms on competitions that simply are not covered. | Response | Meaning | Correct handling | | --- | --- | --- | | coverage.injuries_available true, empty data | Covered, and nothing is currently reported. | Show "no absences reported". | | coverage.injuries_available false, empty data | The source does not supply this competition. | Show "not covered". Do not retry. | | 404 not_found | The team identifier does not exist. | Fix the identifier. | | Squad empty mid-season | Roster sync has not yet run for that team. | Retry later; it fills on the weekly cycle. | #### Available operations | Method | Path | Purpose | | --- | --- | --- | | GET | /api/v1/football/teams | Search and paginate the team catalog. | | GET | /api/v1/football/teams/{team_id} | Retrieve one team by Stats API public ID. | #### Search Use q for a case-insensitive name search. Results can contain clubs with similar names, so retain the returned ID and country metadata rather than matching by display name alone. ```text GET /api/v1/football/teams?q=united&page=1&limit=25 ``` #### Current scope The beta team record covers identity and country metadata. Squads, players, injuries, lineups, and team statistics are planned operations and are not silently synthesized. ## Complete direct HTTP examples # Direct HTTP client examples Stats API's current public contract is JSON over HTTP. The files below are direct HTTP examples, not official SDK packages. Every example: - calls the implemented `GET https://stats-api.com/api/v1/football/competitions?limit=10` operation; - reads `STATS_API_KEY` from the server-side process environment; - sends the key only in the `Authorization: Bearer ...` header; - sets a finite timeout and checks transport and non-2xx HTTP failures; and - reports `X-Request-ID` when the server supplies it, without printing the key. The endpoint returns a `data` array and pagination `meta`. See [`docs/openapi.json`](docs/openapi.json) for the authoritative request and response contract. Examples with a standard JSON parser also validate the JSON envelope; the dependency-free Java example returns the successful JSON body to your application's parser. Do not put a Stats API key in browser code, a mobile binary, a URL, source control, logs, or an AI prompt. Browser and mobile applications should call a server-side route that owns the key. ## Configure the key Provide `STATS_API_KEY` through your deployment platform's secret manager. For a local shell session, use a method that does not save the value in shell history. Do not replace the environment-variable lookup with a literal key. ## Python [`examples/direct-http/python.py`](examples/direct-http/python.py) uses the generic `requests` package: ```bash python3 -m pip install requests python3 examples/direct-http/python.py ``` It uses separate five-second connection and fifteen-second response timeouts, validates the JSON envelope, and prints the stable competition ID and name. ## JavaScript [`examples/direct-http/javascript.mjs`](examples/direct-http/javascript.mjs) uses the built-in `fetch` implementation in Node.js 18 or newer: ```bash node examples/direct-http/javascript.mjs ``` This is server-side JavaScript. Do not copy the bearer header into JavaScript served to a browser. ## PHP [`examples/direct-http/php.php`](examples/direct-http/php.php) uses PHP's cURL extension: ```bash php examples/direct-http/php.php ``` It distinguishes cURL transport failures, non-2xx API responses, malformed JSON, and an unexpected success envelope. ## Go [`examples/direct-http/main.go`](examples/direct-http/main.go) uses only Go's standard library: ```bash go run examples/direct-http/main.go ``` The example limits the response read to one MiB, decodes the documented JSON envelope, and returns an error for every non-2xx status. ## Java [`examples/direct-http/StatsApiExample.java`](examples/direct-http/StatsApiExample.java) uses the standard HTTP client included in Java 11 and newer: ```bash javac examples/direct-http/StatsApiExample.java java -cp examples/direct-http StatsApiExample ``` It checks connection, interruption, timeout, HTTP-status, and empty-body failures without requiring a JSON library. Add the JSON parser already used by your application if you need typed response objects. ## C# [`examples/direct-http/Program.cs`](examples/direct-http/Program.cs) uses the standard `HttpClient` and `System.Text.Json` APIs in .NET 6 and newer. Copy the file over the generated `Program.cs` in a console project: ```bash dotnet new console --name StatsApiExample cp examples/direct-http/Program.cs StatsApiExample/Program.cs dotnet run --project StatsApiExample ``` It validates the documented error and success envelopes and prints each stable competition ID and name. ## Handle failures deliberately Do not automatically retry `400`, `401`, `403`, `404`, or `422`; change the request or credential first. For `429`, inspect `Retry-After` and the rate-limit or quota reset headers before a bounded retry with jitter. A `503` may be retried with bounded exponential backoff. Preserve `X-Request-ID` in your application's sanitized error report so support can trace the request. Every request admitted through the per-minute limit counts once toward the billing-period quota, including a Redis cache hit and an application-level error produced after authentication. Authentication failures and requests rejected by the per-minute limiter do not consume the billing-period quota. Cache stable responses in your own server where appropriate, and use `meta.has_more` and `meta.next_page` instead of guessing whether another page exists. ## Complete OpenAPI 3.1 contract The JSON below is the authoritative operation and schema contract. ```json { "openapi": "3.1.0", "info": { "title": "Stats API", "version": "1.0.0-beta", "summary": "A cache-first football data API with stable public identifiers.", "description": "Retrieve normalized football competitions, seasons, teams, fixtures, results, and explicit coverage. Customer requests are served from Stats API storage and never call an upstream provider synchronously. Every request admitted through the per-minute limit counts once toward the billing-period quota, including a Redis cache hit and an application-level error produced after authentication. Authentication failures and requests rejected by the per-minute limiter do not consume the billing-period quota.", "contact": { "url": "https://stats-api.com/docs" } }, "servers": [ { "url": "https://stats-api.com/api/v1", "description": "Production" }, { "url": "http://localhost:8080/api/v1", "description": "Local development" } ], "tags": [ { "name": "Platform", "description": "Service health." }, { "name": "Coverage", "description": "Data availability by competition." }, { "name": "Competitions", "description": "Football competitions and seasons." }, { "name": "Matches", "description": "Scheduled and completed matches." }, { "name": "Teams", "description": "Football team lookup." } ], "security": [ { "bearerAuth": [] } ], "paths": { "/coverage/leagues": { "get": { "tags": [ "Coverage" ], "operationId": "listCoveredCompetitions", "summary": "List covered competitions", "description": "Lists competitions with explicit capability flags. A false flag means the data is not currently sold.", "parameters": [ { "$ref": "#/components/parameters/Page" }, { "$ref": "#/components/parameters/Limit" } ], "responses": { "200": { "description": "Paginated coverage list.", "headers": { "X-Request-ID": { "$ref": "#/components/headers/RequestId" }, "X-StatsAPI-Cache": { "$ref": "#/components/headers/CacheStatus" }, "X-RateLimit-Limit": { "$ref": "#/components/headers/RateLimitLimit" }, "X-RateLimit-Remaining": { "$ref": "#/components/headers/RateLimitRemaining" }, "X-RateLimit-Reset": { "$ref": "#/components/headers/RateLimitReset" }, "X-Quota-Limit": { "$ref": "#/components/headers/QuotaLimit" }, "X-Quota-Remaining": { "$ref": "#/components/headers/QuotaRemaining" }, "X-Quota-Reset": { "$ref": "#/components/headers/QuotaReset" } }, "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/CoverageLeague" } }, "meta": { "$ref": "#/components/schemas/PaginationMeta" } }, "required": [ "data", "meta" ] } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/SubscriptionInactive" }, "405": { "$ref": "#/components/responses/MethodNotAllowed" }, "422": { "$ref": "#/components/responses/UnprocessableEntity" }, "429": { "$ref": "#/components/responses/RateLimited" } } } }, "/coverage/leagues/{competition_id}": { "get": { "tags": [ "Coverage" ], "operationId": "getCoveredCompetition", "summary": "Get competition coverage", "description": "Returns available seasons and capability flags for one competition.", "parameters": [ { "$ref": "#/components/parameters/CompetitionId" } ], "responses": { "200": { "description": "Competition coverage.", "headers": { "X-Request-ID": { "$ref": "#/components/headers/RequestId" }, "X-StatsAPI-Cache": { "$ref": "#/components/headers/CacheStatus" }, "X-RateLimit-Limit": { "$ref": "#/components/headers/RateLimitLimit" }, "X-RateLimit-Remaining": { "$ref": "#/components/headers/RateLimitRemaining" }, "X-RateLimit-Reset": { "$ref": "#/components/headers/RateLimitReset" }, "X-Quota-Limit": { "$ref": "#/components/headers/QuotaLimit" }, "X-Quota-Remaining": { "$ref": "#/components/headers/QuotaRemaining" }, "X-Quota-Reset": { "$ref": "#/components/headers/QuotaReset" } }, "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "type": "object", "properties": { "competition": { "$ref": "#/components/schemas/Competition" }, "seasons": { "type": "array", "items": { "$ref": "#/components/schemas/Season" } }, "available": { "$ref": "#/components/schemas/Capabilities" } }, "required": [ "competition", "seasons", "available" ] } }, "required": [ "data" ] } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/SubscriptionInactive" }, "405": { "$ref": "#/components/responses/MethodNotAllowed" }, "422": { "$ref": "#/components/responses/UnprocessableEntity" }, "404": { "$ref": "#/components/responses/NotFound" }, "429": { "$ref": "#/components/responses/RateLimited" } } } }, "/coverage/summary": { "get": { "tags": [ "Coverage" ], "operationId": "getCoverageSummary", "summary": "Get coverage totals", "description": "Returns counts of currently active normalized rows plus dataset-level ingestion observations. Dataset timestamps say when Stats API retrieved and successfully ingested an artifact; they are not upstream publication times or a freshness SLA.", "responses": { "200": { "description": "Coverage totals.", "headers": { "X-Request-ID": { "$ref": "#/components/headers/RequestId" }, "X-StatsAPI-Cache": { "$ref": "#/components/headers/CacheStatus" }, "X-RateLimit-Limit": { "$ref": "#/components/headers/RateLimitLimit" }, "X-RateLimit-Remaining": { "$ref": "#/components/headers/RateLimitRemaining" }, "X-RateLimit-Reset": { "$ref": "#/components/headers/RateLimitReset" }, "X-Quota-Limit": { "$ref": "#/components/headers/QuotaLimit" }, "X-Quota-Remaining": { "$ref": "#/components/headers/QuotaRemaining" }, "X-Quota-Reset": { "$ref": "#/components/headers/QuotaReset" } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CoverageSummaryResponse" } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/SubscriptionInactive" }, "405": { "$ref": "#/components/responses/MethodNotAllowed" }, "422": { "$ref": "#/components/responses/UnprocessableEntity" }, "429": { "$ref": "#/components/responses/RateLimited" } } } }, "/football/competitions": { "get": { "tags": [ "Competitions" ], "operationId": "listCompetitions", "summary": "List competitions", "description": "Returns active football competitions ordered by country, name, and stable competition ID.", "parameters": [ { "name": "country_code", "in": "query", "description": "ISO 3166-1 alpha-2 country code.", "schema": { "type": "string", "minLength": 2, "maxLength": 2 }, "example": "GB" }, { "name": "q", "in": "query", "description": "Case-insensitive competition-name search.", "schema": { "type": "string", "maxLength": 100 }, "example": "premier" }, { "$ref": "#/components/parameters/Page" }, { "$ref": "#/components/parameters/Limit" } ], "responses": { "200": { "description": "Paginated competition collection.", "headers": { "X-Request-ID": { "$ref": "#/components/headers/RequestId" }, "X-StatsAPI-Cache": { "$ref": "#/components/headers/CacheStatus" }, "X-RateLimit-Limit": { "$ref": "#/components/headers/RateLimitLimit" }, "X-RateLimit-Remaining": { "$ref": "#/components/headers/RateLimitRemaining" }, "X-RateLimit-Reset": { "$ref": "#/components/headers/RateLimitReset" }, "X-Quota-Limit": { "$ref": "#/components/headers/QuotaLimit" }, "X-Quota-Remaining": { "$ref": "#/components/headers/QuotaRemaining" }, "X-Quota-Reset": { "$ref": "#/components/headers/QuotaReset" } }, "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/Competition" } }, "meta": { "$ref": "#/components/schemas/PaginationMeta" } }, "required": [ "data", "meta" ] }, "examples": { "success": { "value": { "data": [ { "id": "comp_4e223f6d8b84", "slug": "premier-league-gb", "name": "Premier League", "country": { "name": "England", "code": "GB" }, "type": "league", "gender": "men", "logo_url": null, "season_count": 5 } ], "meta": { "page": 1, "limit": 25, "total": 1, "has_more": false, "next_page": null, "request_id": "31de9ee84c91ab11" } } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/SubscriptionInactive" }, "405": { "$ref": "#/components/responses/MethodNotAllowed" }, "422": { "$ref": "#/components/responses/UnprocessableEntity" }, "429": { "$ref": "#/components/responses/RateLimited" } } } }, "/football/competitions/{competition_id}": { "get": { "tags": [ "Competitions" ], "operationId": "getCompetition", "summary": "Get competition details", "description": "Retrieves one competition by its stable Stats API identifier.", "parameters": [ { "$ref": "#/components/parameters/CompetitionId" } ], "responses": { "200": { "description": "Competition details.", "headers": { "X-Request-ID": { "$ref": "#/components/headers/RequestId" }, "X-StatsAPI-Cache": { "$ref": "#/components/headers/CacheStatus" }, "X-RateLimit-Limit": { "$ref": "#/components/headers/RateLimitLimit" }, "X-RateLimit-Remaining": { "$ref": "#/components/headers/RateLimitRemaining" }, "X-RateLimit-Reset": { "$ref": "#/components/headers/RateLimitReset" }, "X-Quota-Limit": { "$ref": "#/components/headers/QuotaLimit" }, "X-Quota-Remaining": { "$ref": "#/components/headers/QuotaRemaining" }, "X-Quota-Reset": { "$ref": "#/components/headers/QuotaReset" } }, "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/Competition" } }, "required": [ "data" ] } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/SubscriptionInactive" }, "405": { "$ref": "#/components/responses/MethodNotAllowed" }, "422": { "$ref": "#/components/responses/UnprocessableEntity" }, "404": { "$ref": "#/components/responses/NotFound" }, "429": { "$ref": "#/components/responses/RateLimited" } } } }, "/football/competitions/{competition_id}/seasons": { "get": { "tags": [ "Competitions" ], "operationId": "listCompetitionSeasons", "summary": "List competition seasons", "description": "Returns seasons newest first, including team and match counts.", "parameters": [ { "$ref": "#/components/parameters/CompetitionId" } ], "responses": { "200": { "description": "Competition seasons.", "headers": { "X-Request-ID": { "$ref": "#/components/headers/RequestId" }, "X-StatsAPI-Cache": { "$ref": "#/components/headers/CacheStatus" }, "X-RateLimit-Limit": { "$ref": "#/components/headers/RateLimitLimit" }, "X-RateLimit-Remaining": { "$ref": "#/components/headers/RateLimitRemaining" }, "X-RateLimit-Reset": { "$ref": "#/components/headers/RateLimitReset" }, "X-Quota-Limit": { "$ref": "#/components/headers/QuotaLimit" }, "X-Quota-Remaining": { "$ref": "#/components/headers/QuotaRemaining" }, "X-Quota-Reset": { "$ref": "#/components/headers/QuotaReset" } }, "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/Season" } }, "meta": { "type": "object", "properties": { "count": { "type": "integer" } } }, "required": [ "data", "meta" ] } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/SubscriptionInactive" }, "405": { "$ref": "#/components/responses/MethodNotAllowed" }, "422": { "$ref": "#/components/responses/UnprocessableEntity" }, "429": { "$ref": "#/components/responses/RateLimited" } } } }, "/football/competitions/{competition_id}/seasons/{season_id}/standings": { "get": { "tags": [ "Competitions" ], "summary": "League table for one season", "description": "Ranked table with points, goal difference and form.", "operationId": "getSeasonStandings", "parameters": [ { "name": "competition_id", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^comp_" }, "example": "comp_4f2a91c7de03" }, { "name": "season_id", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^season_" }, "example": "season_31a7c0d94b26" } ], "responses": { "200": { "description": "League table for one season.", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/StandingRow" } }, "meta": { "type": "object" } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/SubscriptionInactive" }, "404": { "$ref": "#/components/responses/NotFound" }, "405": { "$ref": "#/components/responses/MethodNotAllowed" }, "422": { "$ref": "#/components/responses/UnprocessableEntity" }, "429": { "$ref": "#/components/responses/RateLimited" } } } }, "/football/matches": { "get": { "tags": [ "Matches" ], "operationId": "listMatches", "summary": "List matches", "description": "Lists scheduled and completed matches with optional competition, season, team, status, and date filters. Results are ordered by kickoff time and stable match ID.", "parameters": [ { "name": "competition_id", "in": "query", "schema": { "type": "string" }, "example": "comp_4e223f6d8b84" }, { "name": "season_id", "in": "query", "schema": { "type": "string" }, "example": "season_77c2d67f05df" }, { "name": "team_id", "in": "query", "schema": { "type": "string" }, "example": "team_26a5fd461c18" }, { "name": "status", "in": "query", "schema": { "type": "string", "enum": [ "scheduled", "live", "finished", "postponed", "canceled", "unknown" ] } }, { "name": "date_from", "in": "query", "schema": { "type": "string", "format": "date" } }, { "name": "date_to", "in": "query", "schema": { "type": "string", "format": "date" } }, { "$ref": "#/components/parameters/Page" }, { "$ref": "#/components/parameters/Limit" } ], "responses": { "200": { "description": "Paginated match collection.", "headers": { "X-Request-ID": { "$ref": "#/components/headers/RequestId" }, "X-StatsAPI-Cache": { "$ref": "#/components/headers/CacheStatus" }, "X-RateLimit-Limit": { "$ref": "#/components/headers/RateLimitLimit" }, "X-RateLimit-Remaining": { "$ref": "#/components/headers/RateLimitRemaining" }, "X-RateLimit-Reset": { "$ref": "#/components/headers/RateLimitReset" }, "X-Quota-Limit": { "$ref": "#/components/headers/QuotaLimit" }, "X-Quota-Remaining": { "$ref": "#/components/headers/QuotaRemaining" }, "X-Quota-Reset": { "$ref": "#/components/headers/QuotaReset" } }, "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/Match" } }, "meta": { "$ref": "#/components/schemas/PaginationMeta" } }, "required": [ "data", "meta" ] } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/SubscriptionInactive" }, "405": { "$ref": "#/components/responses/MethodNotAllowed" }, "422": { "$ref": "#/components/responses/UnprocessableEntity" }, "429": { "$ref": "#/components/responses/RateLimited" } } } }, "/football/matches/{match_id}": { "get": { "tags": [ "Matches" ], "operationId": "getMatch", "summary": "Get match details", "description": "Retrieves one fixture or result by its stable Stats API identifier.", "parameters": [ { "name": "match_id", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^match_" }, "example": "match_b7f18ac4cc11" } ], "responses": { "200": { "description": "Match details.", "headers": { "X-Request-ID": { "$ref": "#/components/headers/RequestId" }, "X-StatsAPI-Cache": { "$ref": "#/components/headers/CacheStatus" }, "X-RateLimit-Limit": { "$ref": "#/components/headers/RateLimitLimit" }, "X-RateLimit-Remaining": { "$ref": "#/components/headers/RateLimitRemaining" }, "X-RateLimit-Reset": { "$ref": "#/components/headers/RateLimitReset" }, "X-Quota-Limit": { "$ref": "#/components/headers/QuotaLimit" }, "X-Quota-Remaining": { "$ref": "#/components/headers/QuotaRemaining" }, "X-Quota-Reset": { "$ref": "#/components/headers/QuotaReset" } }, "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/Match" } }, "required": [ "data" ] } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/SubscriptionInactive" }, "405": { "$ref": "#/components/responses/MethodNotAllowed" }, "422": { "$ref": "#/components/responses/UnprocessableEntity" }, "404": { "$ref": "#/components/responses/NotFound" }, "429": { "$ref": "#/components/responses/RateLimited" } } } }, "/football/matches/{match_id}/lineups": { "get": { "tags": [ "Matches" ], "summary": "Confirmed lineups for one match", "description": "Starting eleven, bench, formation and coach per team. A competition without coverage returns an explicit coverage flag and an empty result rather than an error.", "operationId": "getMatchLineups", "parameters": [ { "name": "match_id", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^match_" }, "example": "match_b7f18ac4cc11" } ], "responses": { "200": { "description": "Confirmed lineups for one match.", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/MatchLineups" } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/SubscriptionInactive" }, "404": { "$ref": "#/components/responses/NotFound" }, "405": { "$ref": "#/components/responses/MethodNotAllowed" }, "422": { "$ref": "#/components/responses/UnprocessableEntity" }, "429": { "$ref": "#/components/responses/RateLimited" } } } }, "/football/matches/{match_id}/live-player-stats": { "get": { "tags": [ "Matches" ], "summary": "In-play per-player statistics", "description": "Same shape as /player-stats, cached briefly for matches in progress.", "operationId": "getMatchLivePlayerStatistics", "parameters": [ { "name": "match_id", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^match_" }, "example": "match_b7f18ac4cc11" } ], "responses": { "200": { "description": "In-play per-player statistics.", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/MatchPlayerStatistics" } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/SubscriptionInactive" }, "404": { "$ref": "#/components/responses/NotFound" }, "405": { "$ref": "#/components/responses/MethodNotAllowed" }, "422": { "$ref": "#/components/responses/UnprocessableEntity" }, "429": { "$ref": "#/components/responses/RateLimited" } } } }, "/football/matches/{match_id}/live-stats": { "get": { "tags": [ "Matches" ], "summary": "In-play team statistics", "description": "Same shape as /stats, cached briefly for matches in progress.", "operationId": "getMatchLiveStatistics", "parameters": [ { "name": "match_id", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^match_" }, "example": "match_b7f18ac4cc11" } ], "responses": { "200": { "description": "In-play team statistics.", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/MatchStatistics" } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/SubscriptionInactive" }, "404": { "$ref": "#/components/responses/NotFound" }, "405": { "$ref": "#/components/responses/MethodNotAllowed" }, "422": { "$ref": "#/components/responses/UnprocessableEntity" }, "429": { "$ref": "#/components/responses/RateLimited" } } } }, "/football/matches/{match_id}/live-timeline": { "get": { "tags": [ "Matches" ], "summary": "In-play match timeline", "description": "Same events as /timeline, cached briefly for matches in progress.", "operationId": "getMatchLiveTimeline", "parameters": [ { "name": "match_id", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^match_" }, "example": "match_b7f18ac4cc11" } ], "responses": { "200": { "description": "In-play match timeline.", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/MatchTimeline" } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/SubscriptionInactive" }, "404": { "$ref": "#/components/responses/NotFound" }, "405": { "$ref": "#/components/responses/MethodNotAllowed" }, "422": { "$ref": "#/components/responses/UnprocessableEntity" }, "429": { "$ref": "#/components/responses/RateLimited" } } } }, "/football/matches/{match_id}/player-stats": { "get": { "tags": [ "Matches" ], "summary": "Per-player match statistics", "description": "Minutes, rating and the canonical per-player statistic set for each team. A competition without coverage returns an explicit coverage flag and an empty result rather than an error.", "operationId": "getMatchPlayerStatistics", "parameters": [ { "name": "match_id", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^match_" }, "example": "match_b7f18ac4cc11" } ], "responses": { "200": { "description": "Per-player match statistics.", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/MatchPlayerStatistics" } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/SubscriptionInactive" }, "404": { "$ref": "#/components/responses/NotFound" }, "405": { "$ref": "#/components/responses/MethodNotAllowed" }, "422": { "$ref": "#/components/responses/UnprocessableEntity" }, "429": { "$ref": "#/components/responses/RateLimited" } } } }, "/football/matches/{match_id}/stats": { "get": { "tags": [ "Matches" ], "summary": "Team statistics for one match", "description": "Team-level statistics for a single match, including corners, shots, possession, cards, passes and expected goals.\n\nStatistics are not available for every competition. When a match belongs to a competition without statistics coverage, the match is still returned with `coverage.statistics_available: false` and an empty `teams` array — this is not an error and not a 404. Individual statistics a source did not supply are `null` rather than absent, so every documented field is always present.", "operationId": "getMatchStatistics", "parameters": [ { "name": "match_id", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^match_" }, "example": "match_b7f18ac4cc11" } ], "responses": { "200": { "description": "Team statistics for the match.", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/MatchStatistics" } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/SubscriptionInactive" }, "404": { "$ref": "#/components/responses/NotFound" }, "405": { "$ref": "#/components/responses/MethodNotAllowed" }, "422": { "$ref": "#/components/responses/UnprocessableEntity" }, "429": { "$ref": "#/components/responses/RateLimited" } } } }, "/football/matches/{match_id}/timeline": { "get": { "tags": [ "Matches" ], "summary": "Settled match timeline", "description": "Ordered match events: goals, cards, substitutions and VAR decisions. A competition without coverage returns an explicit coverage flag and an empty result rather than an error.", "operationId": "getMatchTimeline", "parameters": [ { "name": "match_id", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^match_" }, "example": "match_b7f18ac4cc11" } ], "responses": { "200": { "description": "Settled match timeline.", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/MatchTimeline" } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/SubscriptionInactive" }, "404": { "$ref": "#/components/responses/NotFound" }, "405": { "$ref": "#/components/responses/MethodNotAllowed" }, "422": { "$ref": "#/components/responses/UnprocessableEntity" }, "429": { "$ref": "#/components/responses/RateLimited" } } } }, "/football/players": { "get": { "tags": [ "Players" ], "summary": "List players", "description": "Paginated player directory with optional name search.", "operationId": "listPlayers", "parameters": [ { "name": "page", "in": "query", "schema": { "type": "integer" }, "example": 1 }, { "name": "limit", "in": "query", "schema": { "type": "integer" }, "example": 25 }, { "name": "q", "in": "query", "schema": { "type": "string" }, "example": "haaland" } ], "responses": { "200": { "description": "List players.", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/Player" } }, "meta": { "type": "object" } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/SubscriptionInactive" }, "404": { "$ref": "#/components/responses/NotFound" }, "405": { "$ref": "#/components/responses/MethodNotAllowed" }, "422": { "$ref": "#/components/responses/UnprocessableEntity" }, "429": { "$ref": "#/components/responses/RateLimited" } } } }, "/football/players/{player_id}": { "get": { "tags": [ "Players" ], "summary": "Get one player", "description": "Player profile with biographical fields.", "operationId": "getPlayer", "parameters": [ { "name": "player_id", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^player_" }, "example": "player_9c1b2f4ea77d" } ], "responses": { "200": { "description": "Get one player.", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/Player" } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/SubscriptionInactive" }, "404": { "$ref": "#/components/responses/NotFound" }, "405": { "$ref": "#/components/responses/MethodNotAllowed" }, "422": { "$ref": "#/components/responses/UnprocessableEntity" }, "429": { "$ref": "#/components/responses/RateLimited" } } } }, "/football/players/{player_id}/injuries-suspensions": { "get": { "tags": [ "Players" ], "summary": "Injuries and suspensions for one player", "description": "Reported absences for the player, with the same coverage caveat as the team route.", "operationId": "getPlayerInjuries", "parameters": [ { "name": "player_id", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^player_" }, "example": "player_9c1b2f4ea77d" } ], "responses": { "200": { "description": "Injuries and suspensions for one player.", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/InjuryRecord" } }, "meta": { "type": "object" } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/SubscriptionInactive" }, "404": { "$ref": "#/components/responses/NotFound" }, "405": { "$ref": "#/components/responses/MethodNotAllowed" }, "422": { "$ref": "#/components/responses/UnprocessableEntity" }, "429": { "$ref": "#/components/responses/RateLimited" } } } }, "/football/teams": { "get": { "tags": [ "Teams" ], "operationId": "listTeams", "summary": "List and search teams", "description": "Lists teams by name and stable team ID. Use q for a case-insensitive name search.", "parameters": [ { "name": "q", "in": "query", "schema": { "type": "string", "maxLength": 100 }, "example": "city" }, { "$ref": "#/components/parameters/Page" }, { "$ref": "#/components/parameters/Limit" } ], "responses": { "200": { "description": "Paginated team collection.", "headers": { "X-Request-ID": { "$ref": "#/components/headers/RequestId" }, "X-StatsAPI-Cache": { "$ref": "#/components/headers/CacheStatus" }, "X-RateLimit-Limit": { "$ref": "#/components/headers/RateLimitLimit" }, "X-RateLimit-Remaining": { "$ref": "#/components/headers/RateLimitRemaining" }, "X-RateLimit-Reset": { "$ref": "#/components/headers/RateLimitReset" }, "X-Quota-Limit": { "$ref": "#/components/headers/QuotaLimit" }, "X-Quota-Remaining": { "$ref": "#/components/headers/QuotaRemaining" }, "X-Quota-Reset": { "$ref": "#/components/headers/QuotaReset" } }, "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/Team" } }, "meta": { "$ref": "#/components/schemas/PaginationMeta" } }, "required": [ "data", "meta" ] } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/SubscriptionInactive" }, "405": { "$ref": "#/components/responses/MethodNotAllowed" }, "422": { "$ref": "#/components/responses/UnprocessableEntity" }, "429": { "$ref": "#/components/responses/RateLimited" } } } }, "/football/teams/{team_id}": { "get": { "tags": [ "Teams" ], "operationId": "getTeam", "summary": "Get team details", "description": "Retrieves one team by its stable Stats API identifier.", "parameters": [ { "name": "team_id", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^team_" }, "example": "team_f2c85c3c8cb2" } ], "responses": { "200": { "description": "Team details.", "headers": { "X-Request-ID": { "$ref": "#/components/headers/RequestId" }, "X-StatsAPI-Cache": { "$ref": "#/components/headers/CacheStatus" }, "X-RateLimit-Limit": { "$ref": "#/components/headers/RateLimitLimit" }, "X-RateLimit-Remaining": { "$ref": "#/components/headers/RateLimitRemaining" }, "X-RateLimit-Reset": { "$ref": "#/components/headers/RateLimitReset" }, "X-Quota-Limit": { "$ref": "#/components/headers/QuotaLimit" }, "X-Quota-Remaining": { "$ref": "#/components/headers/QuotaRemaining" }, "X-Quota-Reset": { "$ref": "#/components/headers/QuotaReset" } }, "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/Team" } }, "required": [ "data" ] } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/SubscriptionInactive" }, "405": { "$ref": "#/components/responses/MethodNotAllowed" }, "422": { "$ref": "#/components/responses/UnprocessableEntity" }, "404": { "$ref": "#/components/responses/NotFound" }, "429": { "$ref": "#/components/responses/RateLimited" } } } }, "/football/teams/{team_id}/injuries-suspensions": { "get": { "tags": [ "Teams" ], "summary": "Injuries and suspensions for one team", "description": "Reported absences. Coverage for this dataset is sparse: most competitions report none, and the response states whether an empty list means none reported or not covered.", "operationId": "getTeamInjuries", "parameters": [ { "name": "team_id", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^team_" }, "example": "team_f2c85c3c8cb2" } ], "responses": { "200": { "description": "Injuries and suspensions for one team.", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/InjuryRecord" } }, "meta": { "type": "object" } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/SubscriptionInactive" }, "404": { "$ref": "#/components/responses/NotFound" }, "405": { "$ref": "#/components/responses/MethodNotAllowed" }, "422": { "$ref": "#/components/responses/UnprocessableEntity" }, "429": { "$ref": "#/components/responses/RateLimited" } } } }, "/football/teams/{team_id}/players": { "get": { "tags": [ "Teams" ], "summary": "Current squad for one team", "description": "Squad members with shirt number and position.", "operationId": "getTeamPlayers", "parameters": [ { "name": "team_id", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^team_" }, "example": "team_f2c85c3c8cb2" } ], "responses": { "200": { "description": "Current squad for one team.", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/SquadMember" } }, "meta": { "type": "object" } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/SubscriptionInactive" }, "404": { "$ref": "#/components/responses/NotFound" }, "405": { "$ref": "#/components/responses/MethodNotAllowed" }, "422": { "$ref": "#/components/responses/UnprocessableEntity" }, "429": { "$ref": "#/components/responses/RateLimited" } } } }, "/football/teams/{team_id}/standings": { "get": { "tags": [ "Teams" ], "summary": "Standings rows for one team", "description": "Every current-season table position for the team.", "operationId": "getTeamStandings", "parameters": [ { "name": "team_id", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^team_" }, "example": "team_f2c85c3c8cb2" } ], "responses": { "200": { "description": "Standings rows for one team.", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/StandingRow" } }, "meta": { "type": "object" } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/SubscriptionInactive" }, "404": { "$ref": "#/components/responses/NotFound" }, "405": { "$ref": "#/components/responses/MethodNotAllowed" }, "422": { "$ref": "#/components/responses/UnprocessableEntity" }, "429": { "$ref": "#/components/responses/RateLimited" } } } }, "/health": { "get": { "tags": [ "Platform" ], "operationId": "getHealth", "summary": "Check service health", "description": "Reports API version and dependency health. Authentication is not required.", "security": [], "responses": { "200": { "description": "All dependencies are healthy.", "headers": { "X-Request-ID": { "$ref": "#/components/headers/RequestId" }, "X-StatsAPI-Cache": { "$ref": "#/components/headers/CacheStatus" } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HealthResponse" } } } }, "405": { "$ref": "#/components/responses/MethodNotAllowed" }, "422": { "$ref": "#/components/responses/UnprocessableEntity" }, "503": { "description": "At least one dependency is degraded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HealthResponse" } } } } } } } }, "components": { "securitySchemes": { "bearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "Stats API key", "description": "A key beginning with sapi_live_ in production or sapi_test_ locally." } }, "parameters": { "Page": { "name": "page", "in": "query", "description": "One-indexed page number.", "schema": { "type": "integer", "minimum": 1, "default": 1 } }, "Limit": { "name": "limit", "in": "query", "description": "Items per page.", "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 25 } }, "CompetitionId": { "name": "competition_id", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^comp_" }, "example": "comp_4e223f6d8b84" } }, "headers": { "RequestId": { "description": "Opaque request identifier for support and tracing.", "schema": { "type": "string" } }, "CacheStatus": { "description": "Whether the normalized response was served from the Stats API cache.", "schema": { "type": "string", "enum": [ "HIT", "MISS", "BYPASS" ] } }, "RateLimitLimit": { "description": "Maximum requests allowed in the current one-minute window.", "schema": { "type": "integer", "minimum": 0 } }, "RateLimitRemaining": { "description": "Requests remaining in the current one-minute window.", "schema": { "type": "integer", "minimum": 0 } }, "RateLimitReset": { "description": "Unix timestamp for the next one-minute window.", "schema": { "type": "integer", "minimum": 0 } }, "QuotaLimit": { "description": "Maximum requests allowed in the current billing period.", "schema": { "type": "integer", "minimum": 0 } }, "QuotaRemaining": { "description": "Requests remaining in the current billing period.", "schema": { "type": "integer", "minimum": 0 } }, "QuotaReset": { "description": "Unix timestamp when the current billing-period quota resets.", "schema": { "type": "integer", "minimum": 0 } } }, "responses": { "Unauthorized": { "description": "Missing, malformed, unknown, or revoked API key.", "headers": { "WWW-Authenticate": { "schema": { "type": "string" } } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "error": { "code": "invalid_api_key", "message": "Provide an active Stats API key in the Authorization Bearer header.", "details": {} } } } } }, "SubscriptionInactive": { "description": "The API key is valid, but its account has no current API entitlement.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "error": { "code": "subscription_inactive", "message": "This API key belongs to an account without current API access.", "details": { "pricing": "/pricing" } } } } } }, "MethodNotAllowed": { "description": "The route only accepts GET requests.", "headers": { "Allow": { "description": "The supported HTTP method.", "schema": { "type": "string", "const": "GET" } } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "error": { "code": "method_not_allowed", "message": "This endpoint only accepts GET requests.", "details": {} } } } } }, "NotFound": { "description": "The requested resource does not exist.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "UnprocessableEntity": { "description": "A query parameter is unsupported or invalid. Unknown parameters are rejected rather than ignored.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "error": { "code": "unknown_parameter", "message": "Unsupported query parameter: competiton_id.", "details": { "parameters": [ "competiton_id" ], "allowed": [ "page", "limit", "competition_id" ] } } } } } }, "RateLimited": { "description": "The per-minute rate limit or billing-period quota was exceeded. A request rejected by the per-minute limiter does not consume billing-period quota; a monthly-quota rejection was admitted through that limiter and counts once.", "headers": { "Retry-After": { "schema": { "type": "integer" } }, "X-RateLimit-Limit": { "schema": { "type": "integer" } }, "X-RateLimit-Remaining": { "schema": { "type": "integer" } }, "X-RateLimit-Reset": { "$ref": "#/components/headers/RateLimitReset" }, "X-Quota-Limit": { "schema": { "type": "integer" } }, "X-Quota-Remaining": { "schema": { "type": "integer" } }, "X-Quota-Reset": { "schema": { "type": "integer" } } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } }, "schemas": { "HealthResponse": { "type": "object", "properties": { "status": { "type": "string", "enum": [ "ok", "degraded" ] }, "version": { "type": "string", "example": "v1" }, "time": { "type": "string", "format": "date-time" }, "checks": { "type": "object", "properties": { "database": { "type": "boolean" }, "schema": { "type": "boolean" }, "cache": { "type": "boolean" } }, "required": [ "database", "schema", "cache" ] } }, "required": [ "status", "version", "time", "checks" ] }, "Country": { "type": "object", "properties": { "name": { "type": [ "string", "null" ] }, "code": { "type": [ "string", "null" ], "minLength": 2, "maxLength": 2 } }, "required": [ "name", "code" ] }, "Competition": { "type": "object", "properties": { "id": { "type": "string", "pattern": "^comp_" }, "slug": { "type": "string" }, "name": { "type": "string" }, "country": { "$ref": "#/components/schemas/Country" }, "type": { "type": "string", "enum": [ "league", "cup", "tournament", "unknown" ] }, "gender": { "type": "string", "enum": [ "men", "women", "mixed", "unknown" ] }, "logo_url": { "type": [ "string", "null" ], "format": "uri" }, "season_count": { "type": "integer", "minimum": 0 } }, "required": [ "id", "slug", "name", "country", "type", "gender", "logo_url", "season_count" ] }, "Season": { "type": "object", "properties": { "id": { "type": "string", "pattern": "^season_" }, "name": { "type": "string" }, "starts_on": { "type": [ "string", "null" ], "format": "date" }, "ends_on": { "type": [ "string", "null" ], "format": "date" }, "is_current": { "type": "boolean" }, "team_count": { "type": "integer", "minimum": 0 }, "match_count": { "type": "integer", "minimum": 0 } }, "required": [ "id", "name", "starts_on", "ends_on", "is_current", "team_count", "match_count" ] }, "Team": { "type": "object", "properties": { "id": { "type": "string", "pattern": "^team_" }, "slug": { "type": "string" }, "name": { "type": "string" }, "short_name": { "type": [ "string", "null" ] }, "country_name": { "type": [ "string", "null" ] }, "country_code": { "type": [ "string", "null" ] }, "logo_url": { "type": [ "string", "null" ], "format": "uri" } }, "required": [ "id", "slug", "name", "short_name", "country_name", "country_code", "logo_url" ] }, "MatchTeam": { "type": "object", "properties": { "id": { "type": "string", "pattern": "^team_" }, "name": { "type": "string" }, "logo_url": { "type": [ "string", "null" ], "format": "uri" }, "score": { "type": [ "integer", "null" ], "minimum": 0 } }, "required": [ "id", "name", "logo_url", "score" ] }, "Match": { "type": "object", "properties": { "id": { "type": "string", "pattern": "^match_" }, "competition": { "type": "object", "properties": { "id": { "type": "string" }, "name": { "type": "string" } }, "required": [ "id", "name" ] }, "season": { "type": "object", "properties": { "id": { "type": "string" }, "name": { "type": "string" } }, "required": [ "id", "name" ] }, "starts_at": { "type": "string", "format": "date-time" }, "round": { "type": [ "string", "null" ] }, "status": { "type": "string", "enum": [ "scheduled", "live", "finished", "postponed", "canceled", "unknown" ] }, "home_team": { "$ref": "#/components/schemas/MatchTeam" }, "away_team": { "$ref": "#/components/schemas/MatchTeam" }, "venue": { "oneOf": [ { "type": "null" }, { "type": "object", "properties": { "name": { "type": "string" } }, "required": [ "name" ] } ] }, "updated_at": { "type": [ "string", "null" ], "format": "date-time", "description": "UTC time when Stats API last observed and stored a change to this normalized match. This is an ingestion observation timestamp, not an upstream source timestamp and not evidence of live-data freshness." } }, "required": [ "id", "competition", "season", "starts_at", "round", "status", "home_team", "away_team", "venue", "updated_at" ] }, "Capabilities": { "type": "object", "properties": { "fixtures": { "type": "boolean" }, "results": { "type": "boolean" }, "teams": { "type": "boolean" }, "standings": { "type": "boolean" }, "players": { "type": "boolean" }, "live": { "type": "boolean" }, "odds": { "type": "boolean" } }, "required": [ "fixtures", "results", "teams", "standings", "players", "live", "odds" ] }, "CoverageLeague": { "type": "object", "properties": { "competition": { "$ref": "#/components/schemas/Competition" }, "available": { "$ref": "#/components/schemas/Capabilities" } }, "required": [ "competition", "available" ] }, "DatasetSource": { "type": "object", "properties": { "slug": { "type": "string" }, "name": { "type": "string" }, "license": { "type": [ "string", "null" ] } }, "required": [ "slug", "name", "license" ] }, "DatasetFreshness": { "type": "object", "description": "Observed ingestion metadata for one configured historical bootstrap dataset. It does not promise live data or a maximum source age.", "properties": { "dataset_key": { "type": "string" }, "source": { "$ref": "#/components/schemas/DatasetSource" }, "data_scope": { "type": "string", "const": "historical_bootstrap", "description": "The enabled OpenFootball source is a historical/bootstrap snapshot, not a contracted live feed." }, "last_success_at": { "type": "string", "format": "date-time", "description": "UTC time when Stats API completed its latest successful ingestion of this dataset." }, "artifact_retrieved_at": { "type": [ "string", "null" ], "format": "date-time", "description": "UTC time when Stats API retrieved the bytes used by that ingestion." }, "source_updated_at": { "type": "null", "description": "Always null for the current source because no trusted upstream publication timestamp is available." }, "freshness_guaranteed": { "type": "boolean", "const": false, "description": "False: the current CC0 source publishes no freshness SLA." }, "records_seen": { "type": "integer", "minimum": 0 } }, "required": [ "dataset_key", "source", "data_scope", "last_success_at", "artifact_retrieved_at", "source_updated_at", "freshness_guaranteed", "records_seen" ] }, "CoverageSummaryResponse": { "type": "object", "properties": { "data": { "type": "object", "properties": { "competitions": { "type": "integer" }, "seasons": { "type": "integer" }, "teams": { "type": "integer" }, "matches": { "type": "integer" }, "completed_matches": { "type": "integer" }, "datasets": { "type": "array", "items": { "$ref": "#/components/schemas/DatasetFreshness" } } }, "required": [ "competitions", "seasons", "teams", "matches", "completed_matches", "datasets" ] } }, "required": [ "data" ] }, "PaginationMeta": { "type": "object", "properties": { "page": { "type": "integer", "minimum": 1 }, "limit": { "type": "integer", "minimum": 1, "maximum": 100 }, "total": { "type": "integer", "minimum": 0 }, "has_more": { "type": "boolean" }, "next_page": { "type": [ "integer", "null" ], "minimum": 1 }, "request_id": { "type": "string" } }, "required": [ "page", "limit", "total", "has_more", "next_page", "request_id" ] }, "ErrorResponse": { "type": "object", "properties": { "error": { "type": "object", "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": { "type": "object" } }, "required": [ "code", "message", "details" ] } }, "required": [ "error" ] }, "MatchStatistics": { "type": "object", "properties": { "match_id": { "type": "string", "example": "match_5f3a1c9d2b" }, "status": { "type": "string", "example": "finished" }, "coverage": { "type": "object", "properties": { "statistics_available": { "type": "boolean", "description": "False when the competition has no statistics coverage." } }, "required": [ "statistics_available" ] }, "teams": { "type": "array", "items": { "type": "object", "properties": { "team": { "type": "object", "properties": { "id": { "type": "string" }, "name": { "type": "string" } }, "required": [ "id", "name" ] }, "statistics": { "type": "object", "properties": { "corners": { "type": [ "integer", "null" ] }, "shots_total": { "type": [ "integer", "null" ] }, "shots_on_target": { "type": [ "integer", "null" ] }, "shots_off_target": { "type": [ "integer", "null" ] }, "shots_blocked": { "type": [ "integer", "null" ] }, "shots_inside_box": { "type": [ "integer", "null" ] }, "shots_outside_box": { "type": [ "integer", "null" ] }, "possession_percent": { "type": [ "number", "null" ] }, "fouls": { "type": [ "integer", "null" ] }, "offsides": { "type": [ "integer", "null" ] }, "yellow_cards": { "type": [ "integer", "null" ] }, "red_cards": { "type": [ "integer", "null" ] }, "saves": { "type": [ "integer", "null" ] }, "passes_total": { "type": [ "integer", "null" ] }, "passes_accurate": { "type": [ "integer", "null" ] }, "passes_accuracy_percent": { "type": [ "number", "null" ] }, "expected_goals": { "type": [ "number", "null" ] } }, "required": [ "corners", "shots_total", "shots_on_target", "shots_off_target", "shots_blocked", "shots_inside_box", "shots_outside_box", "possession_percent", "fouls", "offsides", "yellow_cards", "red_cards", "saves", "passes_total", "passes_accurate", "passes_accuracy_percent", "expected_goals" ] } }, "required": [ "team", "statistics" ] } } }, "required": [ "match_id", "status", "coverage", "teams" ] }, "MatchLineups": { "type": "object", "properties": { "match_id": { "type": "string" }, "status": { "type": "string" }, "coverage": { "type": "object", "properties": { "lineups_available": { "type": "boolean" } }, "required": [ "lineups_available" ] }, "teams": { "type": "array", "items": { "type": "object", "properties": { "team": { "type": "object", "properties": { "id": { "type": "string" }, "name": { "type": "string" } }, "required": [ "id", "name" ] }, "formation": { "type": [ "string", "null" ] }, "coach": { "type": [ "string", "null" ] }, "players": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string" }, "name": { "type": "string" }, "position": { "type": [ "string", "null" ] }, "shirt_number": { "type": [ "integer", "null" ] }, "grid": { "type": [ "string", "null" ] }, "is_starter": { "type": "boolean" } }, "required": [ "id", "name", "position", "shirt_number", "grid", "is_starter" ] } } }, "required": [ "team", "formation", "coach", "players" ] } } }, "required": [ "match_id", "status", "coverage", "teams" ] }, "MatchTimeline": { "type": "object", "properties": { "match_id": { "type": "string" }, "status": { "type": "string" }, "coverage": { "type": "object", "properties": { "timeline_available": { "type": "boolean" } }, "required": [ "timeline_available" ] }, "events": { "type": "array", "items": { "type": "object", "properties": { "minute": { "type": [ "integer", "null" ] }, "minute_extra": { "type": [ "integer", "null" ] }, "type": { "type": "string" }, "detail": { "type": [ "string", "null" ] }, "comments": { "type": [ "string", "null" ] }, "team": { "type": [ "object", "null" ], "properties": { "id": { "type": "string" }, "name": { "type": "string" } } }, "player": { "type": [ "object", "null" ], "properties": { "id": { "type": "string" }, "name": { "type": "string" } } }, "related_player": { "type": [ "object", "null" ], "properties": { "id": { "type": "string" }, "name": { "type": "string" } } } }, "required": [ "minute", "minute_extra", "type", "detail", "comments", "team", "player", "related_player" ] } } }, "required": [ "match_id", "status", "coverage", "events" ] }, "MatchPlayerStatistics": { "type": "object", "properties": { "match_id": { "type": "string" }, "status": { "type": "string" }, "coverage": { "type": "object", "properties": { "player_statistics_available": { "type": "boolean" } }, "required": [ "player_statistics_available" ] }, "teams": { "type": "array", "items": { "type": "object", "properties": { "team": { "type": "object", "properties": { "id": { "type": "string" }, "name": { "type": "string" } }, "required": [ "id", "name" ] }, "players": { "type": "array", "items": { "type": "object", "properties": { "player": { "type": "object", "properties": { "id": { "type": "string" }, "name": { "type": "string" } }, "required": [ "id", "name" ] }, "minutes": { "type": [ "integer", "null" ] }, "rating": { "type": [ "number", "null" ] }, "position": { "type": [ "string", "null" ] }, "is_captain": { "type": "boolean" }, "is_substitute": { "type": "boolean" }, "statistics": { "$ref": "#/components/schemas/CanonicalPlayerStatistics" } }, "required": [ "player", "minutes", "rating", "position", "is_captain", "is_substitute", "statistics" ] } } }, "required": [ "team", "players" ] } } }, "required": [ "match_id", "status", "coverage", "teams" ] }, "StandingRow": { "type": "object", "properties": { "competition": { "type": "object", "properties": { "id": { "type": "string" }, "name": { "type": "string" } }, "required": [ "id", "name" ] }, "season": { "type": "object", "properties": { "id": { "type": "string" }, "name": { "type": "string" } }, "required": [ "id", "name" ] }, "group": { "type": [ "string", "null" ] }, "position": { "type": "integer" }, "team": { "type": "object", "properties": { "id": { "type": "string" }, "name": { "type": "string" } }, "required": [ "id", "name" ] }, "played": { "type": "integer" }, "won": { "type": "integer" }, "drawn": { "type": "integer" }, "lost": { "type": "integer" }, "goals_for": { "type": "integer" }, "goals_against": { "type": "integer" }, "goal_difference": { "type": "integer" }, "points": { "type": "integer" }, "form": { "type": [ "string", "null" ] }, "description": { "type": [ "string", "null" ] } }, "required": [ "competition", "season", "group", "position", "team", "played", "won", "drawn", "lost", "goals_for", "goals_against", "goal_difference", "points", "form", "description" ] }, "SquadMember": { "type": "object", "properties": { "id": { "type": "string" }, "name": { "type": "string" }, "nationality": { "type": [ "string", "null" ] }, "photo_url": { "type": [ "string", "null" ] }, "shirt_number": { "type": [ "integer", "null" ] }, "position": { "type": [ "string", "null" ] }, "season": { "type": "object", "properties": { "id": { "type": "string" }, "name": { "type": "string" } }, "required": [ "id", "name" ] } }, "required": [ "id", "name", "nationality", "photo_url", "shirt_number", "position", "season" ] }, "InjuryRecord": { "type": "object", "properties": { "player": { "type": "object", "properties": { "id": { "type": "string" }, "name": { "type": "string" } }, "required": [ "id", "name" ] }, "team": { "type": "object", "properties": { "id": { "type": "string" }, "name": { "type": "string" } }, "required": [ "id", "name" ] }, "season": { "type": "object", "properties": { "id": { "type": "string" }, "name": { "type": "string" } }, "required": [ "id", "name" ] }, "type": { "type": [ "string", "null" ] }, "reason": { "type": [ "string", "null" ] }, "observed_at": { "type": "string", "format": "date-time" } }, "required": [ "player", "team", "season", "type", "reason", "observed_at" ] }, "Player": { "type": "object", "properties": { "id": { "type": "string" }, "name": { "type": "string" }, "first_name": { "type": [ "string", "null" ] }, "last_name": { "type": [ "string", "null" ] }, "birth_date": { "type": [ "string", "null" ] }, "nationality": { "type": [ "string", "null" ] }, "height_cm": { "type": [ "integer", "null" ] }, "weight_kg": { "type": [ "integer", "null" ] }, "photo_url": { "type": [ "string", "null" ] } }, "required": [ "id", "name", "first_name", "last_name", "birth_date", "nationality", "height_cm", "weight_kg", "photo_url" ] }, "CanonicalPlayerStatistics": { "type": "object", "description": "Canonical Stats API player statistics. Field names are provider-neutral and versioned; a source that does not supply a value reports null rather than omitting the key.", "properties": { "rating": { "type": [ "number", "null" ] }, "position": { "type": [ "string", "null" ] }, "is_captain": { "type": "boolean" }, "is_substitute": { "type": "boolean" }, "minutes": { "type": [ "integer", "null" ] }, "shots_total": { "type": [ "integer", "null" ] }, "shots_on_target": { "type": [ "integer", "null" ] }, "goals": { "type": [ "integer", "null" ] }, "goals_conceded": { "type": [ "integer", "null" ] }, "assists": { "type": [ "integer", "null" ] }, "saves": { "type": [ "integer", "null" ] }, "passes_total": { "type": [ "integer", "null" ] }, "passes_key": { "type": [ "integer", "null" ] }, "passes_accuracy_percent": { "type": [ "integer", "null" ] }, "tackles": { "type": [ "integer", "null" ] }, "blocks": { "type": [ "integer", "null" ] }, "interceptions": { "type": [ "integer", "null" ] }, "duels_total": { "type": [ "integer", "null" ] }, "duels_won": { "type": [ "integer", "null" ] }, "dribbles_attempted": { "type": [ "integer", "null" ] }, "dribbles_successful": { "type": [ "integer", "null" ] }, "fouls_drawn": { "type": [ "integer", "null" ] }, "fouls_committed": { "type": [ "integer", "null" ] }, "yellow_cards": { "type": [ "integer", "null" ] }, "red_cards": { "type": [ "integer", "null" ] }, "penalties_won": { "type": [ "integer", "null" ] }, "penalties_committed": { "type": [ "integer", "null" ] }, "penalties_scored": { "type": [ "integer", "null" ] }, "penalties_missed": { "type": [ "integer", "null" ] }, "penalties_saved": { "type": [ "integer", "null" ] }, "offsides": { "type": [ "integer", "null" ] } }, "required": [ "rating", "position", "is_captain", "is_substitute", "minutes", "shots_total", "shots_on_target", "goals", "goals_conceded", "assists", "saves", "passes_total", "passes_key", "passes_accuracy_percent", "tackles", "blocks", "interceptions", "duels_total", "duels_won", "dribbles_attempted", "dribbles_successful", "fouls_drawn", "fouls_committed", "yellow_cards", "red_cards", "penalties_won", "penalties_committed", "penalties_scored", "penalties_missed", "penalties_saved", "offsides" ] } } } } ```