---
title: "Results archive (1968–2022) — deep historical results (BASIC)"
method: GET
path: "/history/archive/matches"
---

# Results archive (1968–2022) — deep historical results (BASIC)

`GET /history/archive/matches`

## Request

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

Completed-match RESULTS from a licensed historical corpus — ATP and WTA main draws, qualifying/challengers and futures tiers, 1968 through 2022. Winner/loser-shaped records with final score, round, seeds, the players' ranks AT THE TIME, and per-match serve statistics where the era recorded them. Requires BASIC on the live API, or any Historical Data API plan (Starter and up).

A SEPARATE id space from `/matches` — archive people are identified by the corpus person id and by name, never by roster player ids — and the archive ends where our own point-by-point coverage begins (2023-01), so no match is ever served from two datasets. `event_date` is the TOURNAMENT START date, the only date records of this era carry.

## Query parameters

- `tour` 'atp' | 'wta'
- `name` string
- `from` string, date
- `to` string, date
- `round` 'F' | 'SF' | 'QF' | 'R16' | 'R32' | 'R64' | 'R128' | 'RR' | 'BR' | 'Q1' | 'Q2' | 'Q3' | 'Q4' | 'ER'
- `level` string
- `limit` integer
- `offset` integer

## Response `200`

Archive results, newest tournament first

- object
  - `data` ArchiveMatch[]
    - `id` integer
    - `source_id` string
    - `tour` 'atp' | 'wta'
    - `level` string, nullable: Source tier code (G/M/A/F/D/C/O, or a futures category code as published).
    - `tournament` string, nullable
    - `surface` string, nullable
    - `draw_size` integer, nullable
    - `event_date` string, date, nullable: Tournament START date — per-match dates do not exist in this era's records, and we do not invent them.
    - `round` string, nullable
    - `best_of` integer, nullable
    - `minutes` integer, nullable
    - `winner` ArchivePlayer: One participant of an archive result, as the corpus records them.
      - `name` string, nullable
      - `hand` string, nullable
      - `country` string, nullable: 3-letter code, same vocabulary as `player.country`.
      - `rank` integer, nullable: The player's rank AT THE TIME of the match, as published.
      - `seed` integer, nullable
      - `player_id` integer, nullable: The corpus person id — joins `/history/archive/players` within the same tour. NOT a roster player id.
      - `height_cm` integer, nullable
      - `age` number, nullable: Age at the time of the match, as the corpus records it.
      - `entry` string, nullable: Draw entry where recorded (WC, Q, LL, PR, SE, ...) — null for direct acceptances.
    - `loser` ArchivePlayer: One participant of an archive result, as the corpus records them.
      - `name` string, nullable
      - `hand` string, nullable
      - `country` string, nullable: 3-letter code, same vocabulary as `player.country`.
      - `rank` integer, nullable: The player's rank AT THE TIME of the match, as published.
      - `seed` integer, nullable
      - `player_id` integer, nullable: The corpus person id — joins `/history/archive/players` within the same tour. NOT a roster player id.
      - `height_cm` integer, nullable
      - `age` number, nullable: Age at the time of the match, as the corpus records it.
      - `entry` string, nullable: Draw entry where recorded (WC, Q, LL, PR, SE, ...) — null for direct acceptances.
    - `score` string, nullable: The final score as published, e.g. "6-4 7-6(5)", "6-3 RET", "W/O".
    - `outcome` 'completed' | 'retired' | 'walkover' | 'default' | 'abandoned' | 'null', nullable: Parsed from the score's own vocabulary; null when unparseable — never guessed.
    - `stats` object, nullable: Detail endpoint only. {"winner":{...}, "loser":{...}} with aces, double_faults, serve_points, first_in, first_won, second_won, serve_games, bp_saved, bp_faced where the source recorded them; null otherwise (most rows before 1991) — never synthesised.
  - `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

- `400`: Bad query parameter
- `401`: Missing, unknown, or disabled credentials
- `403`: Your tier doesn't unlock this endpoint
- `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)
