---
title: "List matches with filters"
method: GET
path: "/v1/matches"
tags: ["matches"]
---

# List matches with filters

`GET /v1/matches`

## Request

- Base URL: `https://api.bigballsdata.com`
- URL: `https://api.bigballsdata.com/v1/matches`
- Auth: one of:
  - API key in header `x-api-key`
  - HTTP bearer

Filter the scheduled and recent match list. Both ?sport= and ?league= are optional: a bare /v1/matches returns upcoming matches across all sports. `data` is ALWAYS an array of match objects, for every sport and every league. `?league=nba` is the one NBA history surface and reaches all 80 archive seasons plus canonical match-only rows. Its shape is an additive superset: archive identity/statistics and `time_precision` are added. Where only a date is genuinely known, `kickoff_utc` is that date at midnight UTC and `time_precision=date`; the time is not a tip-off. For the live per-adapter scores feed (integer scores keyed by match_id) use /v1/scores with the same parameters. ?date accepts an ISO date (YYYY-MM-DD) or the literal 'today'.

## Query parameters

- `sport` 'football' | 'basketball' | 'baseball' | 'ice_hockey' | 'cricket' | 'mma' | 'boxing' | 'formula1' | 'american_football'
- `league` string: A short league code, not an id. `GET /v1/leagues` returns the authoritative list in its `id` field; that is the value to pass to every `?league=` parameter. Some codes accept an alias (`serie_a` and `seriea` both resolve).
- `date` string
- `tz` string
- `status` 'scheduled' | 'live' | 'in_progress' | 'finished' | 'postponed' | 'cancelled' | 'suspended'
- `season` integer
- `team` string
- `type` 'REG' | 'POST' | 'PRE' | 'ALLSTAR' | 'Regular Season' | 'Playoffs' | 'Pre Season' | 'All Star'
- `limit` integer
- `offset` integer

## Response `200`

ok

- object: The standard response wrapper. Only `data` is guaranteed. `error` is absent (not null) on 16 of 40 live 200 responses measured 2026-09-18, so test for the KEY before reading it; this block declared it required until then. On a non-2xx, `error` is present and `data` is absent.
  - `data` union[], required
    - union
      - Match: The match object. This one shape is returned by `/v1/matches`, `/v1/stored/matches` and `/v1/cricket/matches` alike, for every sport (verified field-for-field across all three on 2026-09-18).
        - `id` string, uuid, required: A stored BigBalls id. This is the identifier every match, team, player and league endpoint returns, and the one every `{id}` path segment accepts. Take it from the `id` field of the corresponding list endpoint and use it as-is.
        - `sport` 'football' | 'basketball' | 'baseball' | 'ice_hockey' | 'cricket' | 'mma' | 'boxing' | 'formula1' | 'american_football', required
        - `league` string, required: The league's DISPLAY name as served here (e.g. "MLB", "NHL"). This is not the `?league=` code; see LeagueCode for that.
        - `kickoff_utc` string, date-time, required
        - `status` 'scheduled' | 'live' | 'in_progress' | 'finished' | 'postponed' | 'cancelled' | 'suspended', required
        - `home` MatchSide, required: One side of a match. The field names are `id`/`name`; there is no `team_id`/`team_name` here. Until 2026-09-18 this schema said `team_id` + `team_name`, which no endpoint has ever returned.
          - `id` string, uuid, required: A stored BigBalls id. This is the identifier every match, team, player and league endpoint returns, and the one every `{id}` path segment accepts. Take it from the `id` field of the corresponding list endpoint and use it as-is.
          - `name` string, required
          - `short_name` string, nullable
          - `logo_url` string, nullable: Served from our own asset endpoint; treat it as opaque and do not construct it.
        - `away` MatchSide, required: One side of a match. The field names are `id`/`name`; there is no `team_id`/`team_name` here. Until 2026-09-18 this schema said `team_id` + `team_name`, which no endpoint has ever returned.
          - `id` string, uuid, required: A stored BigBalls id. This is the identifier every match, team, player and league endpoint returns, and the one every `{id}` path segment accepts. Take it from the `id` field of the corresponding list endpoint and use it as-is.
          - `name` string, required
          - `short_name` string, nullable
          - `logo_url` string, nullable: Served from our own asset endpoint; treat it as opaque and do not construct it.
        - `score` MatchScore: Final or current goal/run/point totals. Null on a match with no score yet.
          - `home` integer, nullable, required
          - `away` integer, nullable, required
        - `linescore` Linescore: Per-period totals. The array length and meaning follow the sport, and are not uniform even within one sport, so read it positionally only after checking `sport`.
          - `home` integer[]
          - `away` integer[]
        - `attendance` integer, nullable
        - `broadcast` string, nullable
        - `has_odds` boolean: Whether stored odds exist for this match; see /v1/matches/{id}/odds.
        - `round` string, nullable: Competition segment as the source names it, e.g. "Regular Season - 2" or "Group Stage". This is the axis to group gameweek analytics by: a postponed fixture keeps its round while its kickoff_utc moves by weeks, so bucketing by date splits one gameweek and merges another. Free text rather than an integer, because the phase carries meaning an integer would discard. Null where the source supplies none, currently populated for soccer only.
        - `season` string: NOT EMITTED by the list endpoints as measured on 2026-09-18. Declared because callers may hold it from older payloads; branch on its absence rather than expecting it.
        - `venue` string: NOT EMITTED by the list endpoints as measured on 2026-09-18.
        - `updated_at` string, date-time: NOT EMITTED by the list endpoints as measured on 2026-09-18.
      - UnifiedNbaMatch: NBA's additive v1 match shape. It preserves the ordinary Match fields, adds archive identity/statistics, and preserves a date-only historical game as midnight UTC plus time_precision=date. The marker explicitly says that the time component is not a known tip-off.
        - `id` string, uuid, required
        - `sport` 'basketball', required
        - `league` 'NBA', required
        - `archive_game_id` string, nullable
        - `match_id` string, uuid, nullable
        - `season` integer, required
        - `season_type` string
        - `game_date` string, date, required
        - `kickoff_utc` string, date-time, required
        - `time_precision` 'date' | 'timestamp', required
        - `status` 'scheduled' | 'live' | 'in_progress' | 'finished' | 'postponed' | 'cancelled' | 'suspended', required
        - `home` UnifiedNbaTeamGame, required
          - `id` string, uuid, required: Canonical team UUID.
          - `archive_team_id` string, nullable, required: Original archive participant ID; null on match-only rows.
          - `name` string, required
          - `short_name` string, nullable, required
          - `logo_url` string, nullable, required
        - `away` UnifiedNbaTeamGame, required
          - `id` string, uuid, required: Canonical team UUID.
          - `archive_team_id` string, nullable, required: Original archive participant ID; null on match-only rows.
          - `name` string, required
          - `short_name` string, nullable, required
          - `logo_url` string, nullable, required
        - `score` MatchScore: Final or current goal/run/point totals. Null on a match with no score yet.
          - `home` integer, nullable, required
          - `away` integer, nullable, required
        - `linescore` Linescore: Per-period totals. The array length and meaning follow the sport, and are not uniform even within one sport, so read it positionally only after checking `sport`.
          - `home` integer[]
          - `away` integer[]
        - `attendance` integer, nullable
        - `broadcast` string, nullable
        - `round` string, nullable
        - `has_odds` boolean
        - `archive_stats` object, nullable
  - `meta` ResponseMeta: Response metadata. NOTHING here is guaranteed: measured over 38 live envelopes on 2026-09-18, `cached` and `request_id` appeared in 29, `source` in 28, `cache_age_ms` in 9 and `confidence` in 6. This block previously declared all five REQUIRED, which no response has ever satisfied. Treat every field as optional and branch on its absence. Routes add their own keys here (`count`, `limit`, `offset`, `note`, `total`, and per-endpoint ones), so do not assume a closed set.
    - `date_window` object: Present when ?date= was applied. The exact window the filter used, so an omission at a day boundary is visible. start_utc is inclusive, end_utc exclusive. timezone is UTC, or the zone named by ?tz=, in which case the window is that local day. Boxing card dates are published event dates, so timezone and bounds are null there.
      - `date` string
      - `timezone` string, nullable
      - `start_utc` string, date-time, nullable
      - `end_utc` string, date-time, nullable
      - `note` string
    - `source` 'official-league' | 'aggregator-paid' | 'aggregator-free' | 'community-scraper' | 'forecast' | 'mixed' | 'cache' | 'none' | 'unknown-source' | 'stored' | 'catalogue' | 'gateway' | 'coverage-snapshot': Opaque source-tier label. Customers never see the vendor identity. `forecast` marks a weather forecast (GET /v1/matches/{id}/weather).
    - `confidence` number, float
    - `cached` boolean
    - `cache_age_ms` integer
    - `request_id` string
    - `fields_missing` string[]
    - `note` string: Short factual disclosure about something this response deliberately withheld, present only when there is something to disclose. On live scores it appears when `match_id` could not be resolved to a stored match and was therefore omitted: it gives the counts and points at `/v1/stored/matches`, whose ids you can call back into. Absent when every served id resolves. Distinct from `message`, which explains `coverage: false` or a stale response. This one is attached to a response that DID serve data and names the part of it that is deliberately missing.
  - `error` ApiError
    - `code` string, required
    - `message` string, required
    - `details` unknown

## Other responses

- `400`: Invalid parameters: an invalid ?date (not 'today' or YYYY-MM-DD), ?limit > 200, or an unknown sport/league value (e.g. ?sport=soccer, it's 'football'). A bare multi-league ?sport= is NOT a 400: it returns 200 matches across that sport's leagues. The error body includes validation_errors and a working example_url.

---

[API](https://skmtc.dev/bigballsdata/apis/big-ball-sports-api.md) · [All operations](https://skmtc.dev/bigballsdata/apis/big-ball-sports-api/llms.txt) · [OpenAPI document](https://skmtc.dev/bigballsdata/apis/big-ball-sports-api/revisions/31fb54ac414e?raw)
