---
title: "One tournament by its stable id (FREE)"
method: GET
path: "/tournaments/{tournamentId}"
---

# One tournament by its stable id (FREE)

`GET /tournaments/{tournamentId}`

## Request

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

## Path parameters

- `tournamentId` string, required

## Response `200`

The tournament

- Tournament: One tournament of the catalogue — the stable id space `Match.tournament_id` joins, one row per tournament × event type, stable across seasons.
  - `id` string: The stable id `Match.tournament_id` joins.
  - `name` string, nullable
  - `tour` 'atp' | 'wta' | 'challenger' | 'itf' | 'juniors' | 'null', nullable
  - `surface` 'hard' | 'clay' | 'grass' | 'null', nullable: Court surface, null where the catalogue holds none — 61 of 10,287 tournaments on 2026-09-20. 35 of those are team competitions (Davis Cup, Billie Jean King Cup, Laver Cup), which have no tournament-level surface to hold: each tie is played on the host nation's choice, and that surface is published on each MATCH of the tie instead. The upstream field for those rows carries the DRAW TIER rather than a surface ("- Preliminary", "- Play Offs", "- Promotion"), and a value that is not one of hard/clay/grass is rejected rather than published. 3 are the exhibition catch-all and 23 are ordinary events the upstream catalogue simply never stated a surface for.
  - `indoor` boolean
  - `city` string, nullable: Host city, from a curated table — null where not curated.
  - `country` string, nullable: Host country, ISO-3166 alpha-2 — null where not curated. (NOTE this differs from `player.country` and the `?country=` filter, which use IOC-style lowercase 3-letter codes.)
  - `category` 'grand_slam' | 'masters_1000' | 'tour_finals' | 'atp_500' | 'atp_250' | 'wta_1000' | 'wta_500' | 'wta_250' | 'wta_125' | 'challenger' | 'itf' | 'juniors' | 'null', nullable: Tournament category where our catalogues agree unambiguously on an exact-name join — null otherwise, never derived from the name. Unchanged by `tier` (2026-09-22) and remains the coarse class; read `tier` for the level.
  - `tier` 'grand_slam' | 'atp_finals' | 'atp_1000' | 'atp_500' | 'atp_250' | 'next_gen_finals' | 'united_cup' | 'davis_cup' | 'olympics' | 'laver_cup' | 'wta_finals' | 'wta_elite_trophy' | 'wta_1000' | 'wta_500' | 'wta_250' | 'wta_125' | 'bjk_cup' | 'challenger_175' | 'challenger_125' | 'challenger_100' | 'challenger_75' | 'challenger_50' | 'itf_m15' | 'itf_m25' | 'itf_w15' | 'itf_w25' | 'itf_w35' | 'itf_w40' | 'itf_w50' | 'itf_w60' | 'itf_w75' | 'itf_w80' | 'itf_w100' | 'juniors' | 'utr' | 'exhibition' | 'null', nullable: The level this tournament is played at in the CURRENT season (added 2026-09-22) — the same closed vocabulary as `tier` on a match (`grand_slam`, `atp_1000` … `atp_250`, `wta_1000` … `wta_125`, `challenger_175` … `challenger_50`, `itf_m15` / `itf_m25`, `itf_w15` … `itf_w100`, the team events, `juniors`, `utr`, `exhibition`). A catalogue row has no season of its own; a match does: the tier is PER SEASON and it moves under one id — Dallas was `atp_250` in 2023 and 2024 and `atp_500` from 2025; Estoril `atp_250`, then `challenger_175` in 2025, then `atp_250` again in 2026; Eastbourne `wta_500` through 2024 and `wta_250` from 2025 — so read the tier off each MATCH for its season, never copy this value onto a match. Null when this season's calendar does not list the event — never guessed.
  - `tier_source` 'calendar' | 'name' | 'wikipedia' | 'resolver' | 'null', nullable: How `tier` was established (added 2026-09-22): `calendar` = the official per-season tour calendar named the level (ATP tour and Challenger calendars, the WTA tournament list); `name` = an unambiguous name rule (the ITF category is in the event's official name — `M25 …`, `W35 …` — team and UTR events are named as such, and the Slam junior draws and exhibitions are their own event type); `wikipedia` = the season's schedule page, used only where the official calendar could not state the level for that season (Eastbourne 2023–2025); `resolver` = resolved after the seed dataset, by the same rules, from the event's name, tour and dates in that season. Null exactly when `tier` is null.

## Other responses

- `401`: Missing, unknown, or disabled credentials
- `404`: No such resource, or no data yet
- `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)
