---
title: "Conditions at kickoff"
method: GET
path: "/v1/matches/{id}/weather"
tags: ["matches"]
---

# Conditions at kickoff

`GET /v1/matches/{id}/weather`

## Request

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

Conditions at a match's kickoff. For a kickoff in the past these are OBSERVED values, for one in the future they are a forecast; `forecast.observed` says which, so a caller never has to infer it by comparing the kickoff against the clock. The shape is identical either way.

`forecast` is null, with a machine-readable `unavailable_reason` and a prose `note`, whenever conditions cannot honestly be attributed to the kickoff. Branch on `unavailable_reason`, never on `note`.

For an entitled caller, a valid match id returns 200. `404 not_found` means only that no match exists with that id. Missing venue data or coordinates are coverage gaps represented by a 200 response, not a claim that the caller supplied the wrong id.

Outdoor weather at an enclosed stadium describes the car park, not the game, so it is withheld rather than served. `venue.roof` is returned so a consumer can render "Indoor" instead of a blank.

A `retractable` roof is withheld on the same grounds: the roof's STATE at a given kickoff is a property of the match, we have no source for it, and a guess is indistinguishable from a fact in the response.

## Path parameters

- `id` string, uuid, required

## 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` object, required
    - `match_id` string, uuid
    - `kickoff_utc` string, date-time
    - `venue` object, nullable: The resolved venue, or null when none is known. Coordinates are null when the venue is known but has not been geocoded.
      - `name` string, nullable
      - `latitude` number, nullable
      - `longitude` number, nullable
      - `roof` 'open' | 'fixed' | 'retractable' | 'unknown': Whether the venue is open to the sky.
    - `forecast` object, nullable
      - `temperature_c` number, nullable
      - `apparent_temperature_c` number, nullable: Feels-like temperature: wind chill in the cold, heat index in the heat.
      - `relative_humidity_pct` number, nullable
      - `precipitation_mm` number, nullable
      - `wind_speed_kmh` number, nullable
      - `wind_gusts_kmh` number, nullable
      - `weather_code` integer, nullable: WMO code.
      - `condition` string, nullable: Human label for weather_code.
      - `observed` boolean: true = measured after the fact, false = predicted beforehand.
    - `note` string
    - `unavailable_reason` 'venue_unknown' | 'venue_not_geocoded' | 'roof_fixed' | 'roof_retractable_state_unknown' | 'roof_unknown' | 'provider_licence_pending' | 'beyond_forecast_horizon' | 'provider_no_data' | 'no_reading_for_kickoff_hour': Stable reason that forecast is null.
  - `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 match id (must be UUID).
- `404`: No match with that id.

---

[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)
