---
title: "Upcoming scheduled fixtures, earliest first (FREE)"
method: GET
path: "/fixtures"
---

# Upcoming scheduled fixtures, earliest first (FREE)

`GET /fixtures`

## Request

- Base URL: `https://api.livetennisapi.com/api/public/v1`
- URL: `https://api.livetennisapi.com/api/public/v1/fixtures`
- Auth: one of:
  - HTTP bearer
  - API key in header `X-API-Key`

## Query parameters

- `tour` 'atp' | 'wta' | 'challenger' | 'itf' | 'juniors'
- `draw` 'singles' | 'doubles'
- `is_qualifying` boolean
- `gender` 'men' | 'women'
- `limit` integer
- `offset` integer

## Response `200`

Fixtures with start time and player ids where resolved — the nulls are real states, not gaps (names are always present)

- object
  - `data` Fixture[]
    - `id` integer
    - `event_date` string, date, nullable
    - `start_time` string, date-time, nullable: Scheduled start (UTC). Null until the order of play assigns a time — a date-only fixture is a real state, and rows written before 2026-08-03 stay null.
    - `player1_id` integer, nullable: Our player id, when the participant is in our roster (exact-key resolution, never a name match). Null otherwise — names are always present regardless.
    - `player2_id` integer, nullable: As `player1_id`, for the second participant.
    - `tour` string, nullable: The record's OWN tour, which is NOT the `tour` filter vocabulary. It is granular (`juniors_boys`, `juniors_girls`, `challenger_men`) where the filter is grouped (`juniors`, `challenger`), and a doubles team reports it UPPERCASE (`ATP`) where an individual reports lowercase (`atp`). Treat it as an opaque string; do not parse it into the filter enum.
    - `tournament` string, nullable
    - `round` string, nullable
    - `round_code` 'F' | 'SF' | 'QF' | 'R16' | 'R32' | 'R64' | 'R128' | 'RR' | 'BR' | 'Q' | 'Q1' | 'Q2' | 'Q3' | 'Q4' | 'ER' | 'null', nullable: Normalized round — same vocabulary as `Match.round_code`. Null when the label is unrecognised, never guessed.
    - `surface` string, nullable
    - `player1_name` string, nullable
    - `player2_name` string, nullable
    - `status` string, nullable
  - `meta` ListMeta
    - `limit` integer
    - `offset` integer
    - `count` integer
    - `total` integer, nullable: Size of the whole filtered set. Null when it cannot be counted cheaply.
    - `has_more` boolean: More results exist beyond this page. Read this rather than comparing count to limit. For a complete enumeration of a filtered set, page `offset` forward by `limit` until this is false; on `completed`/`cancelled` listings `total` is null and this is the only end-of-data signal.
    - `next_cursor` string, nullable: Change-feed only (`?updated_since=` / `?cursor=`). Opaque token to pass back as `?cursor=`. Null means you are caught up — stop paging and switch to polling from `watermark`.
    - `watermark` string, date-time, nullable: Change-feed only. The floor to use on your NEXT poll. Deliberately trails the server clock by 5s so a row committed during this request cannot fall through the gap.

## Other responses

- `401`: Missing, unknown, or disabled credentials
- `429`: Rate limit exceeded (Retry-After header present). Three body shapes, told apart by `error` and `scope`: the per-MINUTE limit (`rate_limited`, with `upgrade_url`, `tier` and `price` naming the next tier up); the per-DAY quota (`rate_limited` with `scope: "day"`, `limit_per_day`, and `resets_at` — the absolute ISO instant the daily window resets); and `abuse_throttled` with `retry_at_epoch` — a 24-hour block applied to clients that keep hammering far past their cap, which a well-behaved retry loop never sees. Fix the loop rather than retrying through it.

---

[API](https://skmtc.dev/livetennisapi/apis/live-tennis-api.md) · [All operations](https://skmtc.dev/livetennisapi/apis/live-tennis-api/llms.txt) · [OpenAPI document](https://skmtc.dev/livetennisapi/apis/live-tennis-api/revisions/7881947e8e43?raw)
