Live Tennis API · docs
diff --git a/docs/historical-results-archive.html b/docs/historical-results-archive.html index 4042e7b..6dc72a5 100644 --- a/docs/historical-results-archive.html +++ b/docs/historical-results-archive.html @@ -199,7 +199,7 @@ Skip to content -Explore the documentation
Explore the documentation
Live Tennis API · docs · version 1.13.78
@@ -312,6 +312,7 @@Example
curl https://api.livetennisapi.com/api/public/v1/h2h
Tennis WebSocket feed and webhooks
Tennis API authentication, quota and status
Live Tennis API tools for the Vercel AI SDK
+Tennis data vocabulary: tiers, rounds, surfaces and results
Generated from openapi.yaml by
livetennisapi/openapi. Every endpoint on
diff --git a/docs/index.html b/docs/index.html
index 6557ea3..c96b90c 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -345,7 +345,7 @@
Skip to content
-
Live Tennis API DocsGet a free key ↗Explore the documentation
+
Live Tennis API DocsGet a free key ↗Explore the documentation
@@ -387,6 +387,7 @@ Tennis data.
Start building.
08 / STREAMINGWebSocket & webhooksConnect to push updates and delivery endpoints.
09 / ESSENTIALSAuth, quota & statusAuthenticate, understand limits and check health.
10 / AI SDKVercel AI SDK toolsGive a model live scores and history with livetennisapi-ai.
+ 11 / CODESTennis data vocabularyWhat every tier, round, surface and result code means.
Official clientspip install livetennisapinpm install livetennisapinpx livetennisapi-mcpnpm install livetennisapi-ai
diff --git a/docs/live-scores.html b/docs/live-scores.html
index 75d2c09..c709797 100644
--- a/docs/live-scores.html
+++ b/docs/live-scores.html
@@ -199,7 +199,7 @@
Skip to content
-
Live Tennis API DocsGet a free key ↗Explore the documentation
+
Live Tennis API DocsGet a free key ↗Explore the documentation
Live Tennis API · docs · version 1.13.78
@@ -438,6 +438,7 @@ Example
curl https://api.livetennisapi.com/api/public/v1/fixt
Tennis WebSocket feed and webhooks
Tennis API authentication, quota and status
Live Tennis API tools for the Vercel AI SDK
+Tennis data vocabulary: tiers, rounds, surfaces and results
Generated from openapi.yaml by
livetennisapi/openapi. Every endpoint on
diff --git a/docs/llms.txt b/docs/llms.txt
index 93942c3..bbb1792 100644
--- a/docs/llms.txt
+++ b/docs/llms.txt
@@ -29,6 +29,7 @@ endpoints, generated from the same spec as the reference:
## Guides
- How do you give a Vercel AI SDK model live tennis data? https://docs.livetennisapi.com/ai-sdk.html
+- What does each tennis data code mean? https://docs.livetennisapi.com/tennis-data-vocabulary.html
## Quickstart (no code required)
Open this in a browser — no install, no headers: https://api.livetennisapi.com/api/public/v1/matches?status=live&token=YOUR_KEY
diff --git a/docs/players-and-tournaments.html b/docs/players-and-tournaments.html
index 26e799e..ce42159 100644
--- a/docs/players-and-tournaments.html
+++ b/docs/players-and-tournaments.html
@@ -199,7 +199,7 @@
Skip to content
-
Live Tennis API DocsGet a free key ↗Explore the documentation
+
Live Tennis API DocsGet a free key ↗Explore the documentation
Live Tennis API · docs · version 1.13.78
@@ -329,6 +329,7 @@ Example
curl https://api.livetennisapi.com/api/public/v1/rank
Tennis WebSocket feed and webhooks
Tennis API authentication, quota and status
Live Tennis API tools for the Vercel AI SDK
+Tennis data vocabulary: tiers, rounds, surfaces and results
Generated from openapi.yaml by
livetennisapi/openapi. Every endpoint on
diff --git a/docs/point-by-point-history.html b/docs/point-by-point-history.html
index c18eff9..340eec1 100644
--- a/docs/point-by-point-history.html
+++ b/docs/point-by-point-history.html
@@ -199,7 +199,7 @@
Skip to content
-
Live Tennis API DocsGet a free key ↗Explore the documentation
+
Live Tennis API DocsGet a free key ↗Explore the documentation
Live Tennis API · docs · version 1.13.78
@@ -333,6 +333,7 @@ Example
curl https://api.livetennisapi.com/api/public/v1/hist
Tennis WebSocket feed and webhooks
Tennis API authentication, quota and status
Live Tennis API tools for the Vercel AI SDK
+Tennis data vocabulary: tiers, rounds, surfaces and results
Generated from openapi.yaml by
livetennisapi/openapi. Every endpoint on
diff --git a/docs/push-feed-and-webhooks.html b/docs/push-feed-and-webhooks.html
index e53df41..f635441 100644
--- a/docs/push-feed-and-webhooks.html
+++ b/docs/push-feed-and-webhooks.html
@@ -199,7 +199,7 @@
Skip to content
-
Live Tennis API DocsGet a free key ↗Explore the documentation
+
Live Tennis API DocsGet a free key ↗Explore the documentation
Live Tennis API · docs · version 1.13.78
@@ -292,6 +292,7 @@ Example
curl https://api.livetennisapi.com/api/public/v1/ws-t
Tennis scores for on-air graphics
Tennis API authentication, quota and status
Live Tennis API tools for the Vercel AI SDK
+Tennis data vocabulary: tiers, rounds, surfaces and results
Generated from openapi.yaml by
livetennisapi/openapi. Every endpoint on
diff --git a/docs/reference.html b/docs/reference.html
index e6b25f1..942a19b 100644
--- a/docs/reference.html
+++ b/docs/reference.html
@@ -202,7 +202,7 @@
Skip to content
-
Live Tennis API DocsGet a free key ↗Explore the documentation
+
Live Tennis API DocsGet a free key ↗Explore the documentation
@@ -227,6 +227,7 @@ Live Tennis API — Full Reference
Tennis WebSocket feed and webhooks
Tennis API authentication, quota and status
Live Tennis API tools for the Vercel AI SDK
+Tennis data vocabulary: tiers, rounds, surfaces and results
diff --git a/docs/shot-level-rally-data.html b/docs/shot-level-rally-data.html
index 05185af..dce0bad 100644
--- a/docs/shot-level-rally-data.html
+++ b/docs/shot-level-rally-data.html
@@ -199,7 +199,7 @@
Skip to content
-
Live Tennis API DocsGet a free key ↗Explore the documentation
+
Live Tennis API DocsGet a free key ↗Explore the documentation
Live Tennis API · docs · version 1.13.78
@@ -294,6 +294,7 @@ Example
curl https://api.livetennisapi.com/api/public/v1/char
Tennis WebSocket feed and webhooks
Tennis API authentication, quota and status
Live Tennis API tools for the Vercel AI SDK
+Tennis data vocabulary: tiers, rounds, surfaces and results
Generated from openapi.yaml by
livetennisapi/openapi. Every endpoint on
diff --git a/docs/sitemap.xml b/docs/sitemap.xml
index 9937a0b..01347b3 100644
--- a/docs/sitemap.xml
+++ b/docs/sitemap.xml
@@ -12,5 +12,6 @@
https://docs.livetennisapi.com/push-feed-and-webhooks.html weekly 0.8
https://docs.livetennisapi.com/auth-quota-and-health.html weekly 0.8
https://docs.livetennisapi.com/ai-sdk.html 2026-10-22 weekly 0.8
+ https://docs.livetennisapi.com/tennis-data-vocabulary.html 2026-11-06 weekly 0.8
https://docs.livetennisapi.com/changelog.html 2026-10-29 weekly 0.6
diff --git a/docs/tennis-data-vocabulary.html b/docs/tennis-data-vocabulary.html
new file mode 100644
index 0000000..34ece27
--- /dev/null
+++ b/docs/tennis-data-vocabulary.html
@@ -0,0 +1,344 @@
+
+
+
+
+
+Tennis data codes: tiers, rounds, surfaces | Live Tennis API
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Skip to content
+
Live Tennis API DocsGet a free key ↗Explore the documentation
+
+
+Live Tennis API · docs · guide
+Tennis data vocabulary: tiers, rounds, surfaces and results
+What does each tennis data code mean? This page lists every closed code the API returns for tournaments, rounds, surfaces, results, rankings and charted shots, with its meaning. It is built from the OpenAPI spec, so it lists exactly the codes the API can send.
+Meanings checked against the spec on . Machine-readable copy: tennis-data-vocabulary.json, MIT licensed, like the spec.
+
+
+
+How to use these codes
+Each code on this page is one the API can return. Where a field can be null, null means the value is not known. The match outcome list can grow within v1, so treat a value you do not recognise as unknown. Each code has its own link, so a schema or a column note can point at one meaning. The same table is published as tennis-data-vocabulary.json for code that validates or labels the values.
+
+Tournament tier
+The level a tournament was played at in the match's own season, as the tour publishes it. A tournament keeps one id while its level changes between seasons, so the tier is read per season. On a tournament the field carries the current season's level. Null means the level is unknown.
+Appears in Match.tier, Tournament.tier, GET /tournaments?tier=.
+Tournament tier codes Code Meaning grand_slamGrand Slam atp_finalsATP Finals atp_1000ATP Masters 1000 atp_500ATP 500 atp_250ATP 250 next_gen_finalsNext Gen ATP Finals united_cupUnited Cup, every group and stage davis_cupDavis Cup, every group and stage olympicsOlympic tennis event laver_cupLaver Cup wta_finalsWTA Finals wta_elite_trophyWTA Elite Trophy, last held in 2023 wta_1000WTA 1000 wta_500WTA 500 wta_250WTA 250 wta_125WTA 125 bjk_cupBillie Jean King Cup, every group and stage challenger_175ATP Challenger 175 challenger_125ATP Challenger 125 challenger_100ATP Challenger 100 challenger_75ATP Challenger 75 challenger_50ATP Challenger 50 itf_m15ITF World Tennis Tour, men's M15 itf_m25ITF World Tennis Tour, men's M25 itf_w15ITF World Tennis Tour, women's W15 itf_w25ITF women's W25, a 2023 category kept as printed that season itf_w35ITF World Tennis Tour, women's W35 itf_w40ITF women's W40, a 2023 category kept as printed that season itf_w50ITF World Tennis Tour, women's W50 itf_w60ITF women's W60, a 2023 category kept as printed that season itf_w75ITF World Tennis Tour, women's W75 itf_w80ITF women's W80, a 2023 category kept as printed that season itf_w100ITF World Tennis Tour, women's W100 juniorsJunior events utrUTR events exhibitionExhibition with no ranking points
+Tournament category
+The coarse class of a tournament. It is set only where the catalogues agree on an exact name, and is null otherwise. Read the tier for the level.
+Appears in Tournament.category.
+Tournament category codes Code Meaning grand_slamGrand Slam masters_1000Masters 1000 tour_finalsSeason-ending tour finals atp_500ATP 500 atp_250ATP 250 wta_1000WTA 1000 wta_500WTA 500 wta_250WTA 250 wta_125WTA 125 challengerATP Challenger, any level itfITF World Tennis Tour, any category juniorsJunior events
+Tour
+The tour a match belongs to. The field and the filter come from one registry, so a match selected with ?tour=X carries X. Exhibitions, team events and mixed events have no public tour name, so their tour is null.
+Appears in Match.tour, GET /broadcast/live?tour=, GET /players/{playerId}/stats?level=.
+Tour codes Code Meaning atpATP Tour wtaWTA Tour challengerATP Challenger Tour itfITF World Tennis Tour juniorsJunior events
+Round code
+The round in one controlled vocabulary, shared by live matches and the results archive. It is relative to the draw: a qualifying semi-final also reads SF, so read it together with is_qualifying. Null when the round label is not recognised. Q appears on matches only: the archive's round filter does not accept it.
+Appears in Match.round_code, GET /history/archive/matches?round=.
+Round code codes Code Meaning FFinal SFSemi-final QFQuarter-final R16Round of 16 R32Round of 32 R64Round of 64 R128Round of 128 RRRound robin BRThird-place or bronze-medal match QQualifying round that the label does not number Q1First qualifying round Q2Second qualifying round Q3Third qualifying round Q4Fourth qualifying round ERUnnumbered round
+Archive level code
+Level codes used by the results archive. ITF events carry their category number instead of a letter, for example 15 or 25.
+Appears in GET /history/archive/matches?level=.
+Archive level code codes Code Meaning GGrand Slam MMasters AOther tour-level events FSeason-ending finals DDavis Cup CChallenger OOlympics
+Court surface
+The court surface as stated for the event. It is never inferred from the surface a tournament used in other seasons. A Davis Cup, Billie Jean King Cup or Laver Cup match carries the surface of its own tie. The player stats filter also takes indoor, which keeps matches stated as played indoors on any surface.
+Appears in Match.surface, Tournament.surface, GET /players/{playerId}/stats?surface=.
+Court surface codes Code Meaning hardHard court clayClay court grassGrass court
+Draw
+Singles or doubles. Null means nothing states which, and is not singles.
+Appears in Match.draw.
+Draw codes Code Meaning singlesSingles doublesDoubles
+Match format
+The number of sets the match is played over.
+Appears in Match.format.
+Match format codes Code Meaning BO3Best of three sets BO5Best of five sets
+Match status
+Where the match is in its lifecycle.
+Appears in Match.status.
+Match status codes Code Meaning upcomingScheduled and not yet started liveIn play completedPlayed to a result cancelledNot played as scheduled
+Match outcome
+How a finished match was settled. Null while a match is upcoming or live.
+Appears in Match.outcome.
+Match outcome codes Code Meaning completedRan its full course retiredA player retired during play walkoverConceded before a ball was struck defaultA player was defaulted abandonedCalled off unfinished, with no result awarded unresolvedClosed unfinished with no result asserted, until a result is confirmed
+Event status
+The stated match state, published as written. Null means nothing was stated, not that the match finished normally.
+Appears in Match.event_status.
+Event status codes Code Meaning FinishedStated as finished RetiredEnded by a retirement CancelledStated as cancelled Walk OverWalkover PostponedStated as postponed InterruptedSuspended in play, for example rain, darkness or a medical stop UnresolvedClosed before a result could be confirmed
+Match event type
+The timeline of a match. Medical timeouts, trainer calls and toilet breaks are stated by the scorer or chair umpire. The player field names the player where the feed carries one and is null otherwise.
+Appears in Event.type, GET /matches/{matchId}/events, GET /events.
+Match event type codes Code Meaning breakA break of serve set_wonA set won game_wonA game won momentum_runA momentum run stoppage_startPlay stopped stoppage_endPlay resumed after a stoppage pause_startA pause began pause_endA pause ended medical_timeout_startA medical timeout began medical_timeout_endA medical timeout ended trainer_calledThe trainer was called trainer_called_endThe trainer visit ended toilet_break_startA toilet break began toilet_break_endA toilet break ended
+Ranking system
+The ranking a row belongs to. Elo keeps four independent ladders, named by the surface field: all surfaces together, hard, clay and grass. ATP and WTA Elo are separate and never combined.
+Appears in RankingRecord.system, GET /rankings?system=.
+Ranking system codes Code Meaning atpATP singles ranking wtaWTA singles ranking itf_jtITF junior ranking itf_mtITF men's World Tennis Tour ranking itf_wtITF women's World Tennis Tour ranking utrUTR rating eloElo rating, by surface ladder atp_doublesATP doubles ranking wta_doublesWTA doubles ranking
+Point outcome
+How a point ended, as stated for that point in the point-by-point history.
+Appears in HistoryTapeRow.outcome.
+Point outcome codes Code Meaning aceAce double_faultDouble fault winnerWinner forced_errorForced error unforced_errorUnforced error
+Charted point outcome
+How a charted point ended in the shot-level data.
+Appears in RallyPoint.outcome.
+Charted point outcome codes Code Meaning winnerWinner forced_errorForced error unforced_errorUnforced error errorA miss charted without saying whether it was forced otherAny other ending
+Unreturned serve
+How a serve went unreturned. Null when the return was in play.
+Appears in RallyPoint.unreturned_kind.
+Unreturned serve codes Code Meaning aceAce serve_winnerServe winner charted without a direction, counted with aces serve_forced_errorThe serve forced a return error return_forced_errorForced return error return_unforced_errorUnforced return error return_errorMissed return, not charted as forced or unforced
+Stroke
+The kind of shot in charted rally data.
+Appears in RallyShot.stroke.
+Stroke codes Code Meaning serveServe groundstrokeGroundstroke sliceSlice volleyVolley half_volleyHalf volley swinging_volleySwinging volley overheadOverhead drop_shotDrop shot lobLob trickTrick shot unknownShot type not recorded
+Wing
+The side a shot was struck from.
+Appears in RallyShot.wing.
+Wing codes Code Meaning forehandForehand backhandBackhand
+Shot direction
+Where the ball was sent.
+Appears in RallyShot.direction.
+Shot direction codes Code Meaning forehand_sideTo the forehand side middleThrough the middle backhand_sideTo the backhand side
+Shot depth
+How deep the ball landed.
+Appears in RallyShot.depth.
+Shot depth codes Code Meaning shallowShallow midMid-court deepDeep
+Serve direction
+Where the serve was aimed.
+Appears in RallyPoint.serve_direction.
+Serve direction codes Code Meaning wideOut wide bodyAt the body down_the_tDown the T
+Error location
+Where a missed shot went.
+Appears in RallyPoint.error_location.
+Error location codes Code Meaning netInto the net wideWide deepLong wide_and_deepWide and long
+Links
+
+
+
+
+
+
+
+
diff --git a/docs/tennis-data-vocabulary.json b/docs/tennis-data-vocabulary.json
new file mode 100644
index 0000000..f7b9f30
--- /dev/null
+++ b/docs/tennis-data-vocabulary.json
@@ -0,0 +1,1061 @@
+{
+ "name": "Live Tennis API tennis data vocabulary",
+ "description": "Every closed code the Live Tennis API returns for tournaments, rounds, surfaces, results and charted shots, with its meaning.",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.json",
+ "page": "https://docs.livetennisapi.com/tennis-data-vocabulary.html",
+ "spec_version": "1.13.78",
+ "license": "MIT, https://github.com/livetennisapi/openapi/blob/main/LICENSE",
+ "attribution": "Live Tennis API, https://livetennisapi.com",
+ "vocabularies": [
+ {
+ "id": "tier",
+ "title": "Tournament tier",
+ "fields": [
+ "Match.tier",
+ "Tournament.tier",
+ "GET /tournaments?tier="
+ ],
+ "about": "The level a tournament was played at in the match's own season, as the tour publishes it. A tournament keeps one id while its level changes between seasons, so the tier is read per season. On a tournament the field carries the current season's level. Null means the level is unknown.",
+ "values": [
+ {
+ "code": "grand_slam",
+ "meaning": "Grand Slam",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#tier-grand_slam"
+ },
+ {
+ "code": "atp_finals",
+ "meaning": "ATP Finals",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#tier-atp_finals"
+ },
+ {
+ "code": "atp_1000",
+ "meaning": "ATP Masters 1000",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#tier-atp_1000"
+ },
+ {
+ "code": "atp_500",
+ "meaning": "ATP 500",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#tier-atp_500"
+ },
+ {
+ "code": "atp_250",
+ "meaning": "ATP 250",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#tier-atp_250"
+ },
+ {
+ "code": "next_gen_finals",
+ "meaning": "Next Gen ATP Finals",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#tier-next_gen_finals"
+ },
+ {
+ "code": "united_cup",
+ "meaning": "United Cup, every group and stage",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#tier-united_cup"
+ },
+ {
+ "code": "davis_cup",
+ "meaning": "Davis Cup, every group and stage",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#tier-davis_cup"
+ },
+ {
+ "code": "olympics",
+ "meaning": "Olympic tennis event",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#tier-olympics"
+ },
+ {
+ "code": "laver_cup",
+ "meaning": "Laver Cup",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#tier-laver_cup"
+ },
+ {
+ "code": "wta_finals",
+ "meaning": "WTA Finals",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#tier-wta_finals"
+ },
+ {
+ "code": "wta_elite_trophy",
+ "meaning": "WTA Elite Trophy, last held in 2023",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#tier-wta_elite_trophy"
+ },
+ {
+ "code": "wta_1000",
+ "meaning": "WTA 1000",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#tier-wta_1000"
+ },
+ {
+ "code": "wta_500",
+ "meaning": "WTA 500",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#tier-wta_500"
+ },
+ {
+ "code": "wta_250",
+ "meaning": "WTA 250",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#tier-wta_250"
+ },
+ {
+ "code": "wta_125",
+ "meaning": "WTA 125",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#tier-wta_125"
+ },
+ {
+ "code": "bjk_cup",
+ "meaning": "Billie Jean King Cup, every group and stage",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#tier-bjk_cup"
+ },
+ {
+ "code": "challenger_175",
+ "meaning": "ATP Challenger 175",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#tier-challenger_175"
+ },
+ {
+ "code": "challenger_125",
+ "meaning": "ATP Challenger 125",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#tier-challenger_125"
+ },
+ {
+ "code": "challenger_100",
+ "meaning": "ATP Challenger 100",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#tier-challenger_100"
+ },
+ {
+ "code": "challenger_75",
+ "meaning": "ATP Challenger 75",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#tier-challenger_75"
+ },
+ {
+ "code": "challenger_50",
+ "meaning": "ATP Challenger 50",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#tier-challenger_50"
+ },
+ {
+ "code": "itf_m15",
+ "meaning": "ITF World Tennis Tour, men's M15",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#tier-itf_m15"
+ },
+ {
+ "code": "itf_m25",
+ "meaning": "ITF World Tennis Tour, men's M25",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#tier-itf_m25"
+ },
+ {
+ "code": "itf_w15",
+ "meaning": "ITF World Tennis Tour, women's W15",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#tier-itf_w15"
+ },
+ {
+ "code": "itf_w25",
+ "meaning": "ITF women's W25, a 2023 category kept as printed that season",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#tier-itf_w25"
+ },
+ {
+ "code": "itf_w35",
+ "meaning": "ITF World Tennis Tour, women's W35",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#tier-itf_w35"
+ },
+ {
+ "code": "itf_w40",
+ "meaning": "ITF women's W40, a 2023 category kept as printed that season",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#tier-itf_w40"
+ },
+ {
+ "code": "itf_w50",
+ "meaning": "ITF World Tennis Tour, women's W50",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#tier-itf_w50"
+ },
+ {
+ "code": "itf_w60",
+ "meaning": "ITF women's W60, a 2023 category kept as printed that season",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#tier-itf_w60"
+ },
+ {
+ "code": "itf_w75",
+ "meaning": "ITF World Tennis Tour, women's W75",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#tier-itf_w75"
+ },
+ {
+ "code": "itf_w80",
+ "meaning": "ITF women's W80, a 2023 category kept as printed that season",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#tier-itf_w80"
+ },
+ {
+ "code": "itf_w100",
+ "meaning": "ITF World Tennis Tour, women's W100",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#tier-itf_w100"
+ },
+ {
+ "code": "juniors",
+ "meaning": "Junior events",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#tier-juniors"
+ },
+ {
+ "code": "utr",
+ "meaning": "UTR events",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#tier-utr"
+ },
+ {
+ "code": "exhibition",
+ "meaning": "Exhibition with no ranking points",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#tier-exhibition"
+ }
+ ]
+ },
+ {
+ "id": "category",
+ "title": "Tournament category",
+ "fields": [
+ "Tournament.category"
+ ],
+ "about": "The coarse class of a tournament. It is set only where the catalogues agree on an exact name, and is null otherwise. Read the tier for the level.",
+ "values": [
+ {
+ "code": "grand_slam",
+ "meaning": "Grand Slam",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#category-grand_slam"
+ },
+ {
+ "code": "masters_1000",
+ "meaning": "Masters 1000",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#category-masters_1000"
+ },
+ {
+ "code": "tour_finals",
+ "meaning": "Season-ending tour finals",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#category-tour_finals"
+ },
+ {
+ "code": "atp_500",
+ "meaning": "ATP 500",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#category-atp_500"
+ },
+ {
+ "code": "atp_250",
+ "meaning": "ATP 250",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#category-atp_250"
+ },
+ {
+ "code": "wta_1000",
+ "meaning": "WTA 1000",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#category-wta_1000"
+ },
+ {
+ "code": "wta_500",
+ "meaning": "WTA 500",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#category-wta_500"
+ },
+ {
+ "code": "wta_250",
+ "meaning": "WTA 250",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#category-wta_250"
+ },
+ {
+ "code": "wta_125",
+ "meaning": "WTA 125",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#category-wta_125"
+ },
+ {
+ "code": "challenger",
+ "meaning": "ATP Challenger, any level",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#category-challenger"
+ },
+ {
+ "code": "itf",
+ "meaning": "ITF World Tennis Tour, any category",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#category-itf"
+ },
+ {
+ "code": "juniors",
+ "meaning": "Junior events",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#category-juniors"
+ }
+ ]
+ },
+ {
+ "id": "tour",
+ "title": "Tour",
+ "fields": [
+ "Match.tour",
+ "GET /broadcast/live?tour=",
+ "GET /players/{playerId}/stats?level="
+ ],
+ "about": "The tour a match belongs to. The field and the filter come from one registry, so a match selected with ?tour=X carries X. Exhibitions, team events and mixed events have no public tour name, so their tour is null.",
+ "values": [
+ {
+ "code": "atp",
+ "meaning": "ATP Tour",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#tour-atp"
+ },
+ {
+ "code": "wta",
+ "meaning": "WTA Tour",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#tour-wta"
+ },
+ {
+ "code": "challenger",
+ "meaning": "ATP Challenger Tour",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#tour-challenger"
+ },
+ {
+ "code": "itf",
+ "meaning": "ITF World Tennis Tour",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#tour-itf"
+ },
+ {
+ "code": "juniors",
+ "meaning": "Junior events",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#tour-juniors"
+ }
+ ]
+ },
+ {
+ "id": "round",
+ "title": "Round code",
+ "fields": [
+ "Match.round_code",
+ "GET /history/archive/matches?round="
+ ],
+ "about": "The round in one controlled vocabulary, shared by live matches and the results archive. It is relative to the draw: a qualifying semi-final also reads SF, so read it together with is_qualifying. Null when the round label is not recognised. Q appears on matches only: the archive's round filter does not accept it.",
+ "values": [
+ {
+ "code": "F",
+ "meaning": "Final",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#round-f"
+ },
+ {
+ "code": "SF",
+ "meaning": "Semi-final",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#round-sf"
+ },
+ {
+ "code": "QF",
+ "meaning": "Quarter-final",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#round-qf"
+ },
+ {
+ "code": "R16",
+ "meaning": "Round of 16",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#round-r16"
+ },
+ {
+ "code": "R32",
+ "meaning": "Round of 32",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#round-r32"
+ },
+ {
+ "code": "R64",
+ "meaning": "Round of 64",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#round-r64"
+ },
+ {
+ "code": "R128",
+ "meaning": "Round of 128",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#round-r128"
+ },
+ {
+ "code": "RR",
+ "meaning": "Round robin",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#round-rr"
+ },
+ {
+ "code": "BR",
+ "meaning": "Third-place or bronze-medal match",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#round-br"
+ },
+ {
+ "code": "Q",
+ "meaning": "Qualifying round that the label does not number",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#round-q"
+ },
+ {
+ "code": "Q1",
+ "meaning": "First qualifying round",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#round-q1"
+ },
+ {
+ "code": "Q2",
+ "meaning": "Second qualifying round",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#round-q2"
+ },
+ {
+ "code": "Q3",
+ "meaning": "Third qualifying round",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#round-q3"
+ },
+ {
+ "code": "Q4",
+ "meaning": "Fourth qualifying round",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#round-q4"
+ },
+ {
+ "code": "ER",
+ "meaning": "Unnumbered round",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#round-er"
+ }
+ ]
+ },
+ {
+ "id": "archive-level",
+ "title": "Archive level code",
+ "fields": [
+ "GET /history/archive/matches?level="
+ ],
+ "about": "Level codes used by the results archive. ITF events carry their category number instead of a letter, for example 15 or 25.",
+ "values": [
+ {
+ "code": "G",
+ "meaning": "Grand Slam",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#archive-level-g"
+ },
+ {
+ "code": "M",
+ "meaning": "Masters",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#archive-level-m"
+ },
+ {
+ "code": "A",
+ "meaning": "Other tour-level events",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#archive-level-a"
+ },
+ {
+ "code": "F",
+ "meaning": "Season-ending finals",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#archive-level-f"
+ },
+ {
+ "code": "D",
+ "meaning": "Davis Cup",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#archive-level-d"
+ },
+ {
+ "code": "C",
+ "meaning": "Challenger",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#archive-level-c"
+ },
+ {
+ "code": "O",
+ "meaning": "Olympics",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#archive-level-o"
+ }
+ ]
+ },
+ {
+ "id": "surface",
+ "title": "Court surface",
+ "fields": [
+ "Match.surface",
+ "Tournament.surface",
+ "GET /players/{playerId}/stats?surface="
+ ],
+ "about": "The court surface as stated for the event. It is never inferred from the surface a tournament used in other seasons. A Davis Cup, Billie Jean King Cup or Laver Cup match carries the surface of its own tie. The player stats filter also takes indoor, which keeps matches stated as played indoors on any surface.",
+ "values": [
+ {
+ "code": "hard",
+ "meaning": "Hard court",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#surface-hard"
+ },
+ {
+ "code": "clay",
+ "meaning": "Clay court",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#surface-clay"
+ },
+ {
+ "code": "grass",
+ "meaning": "Grass court",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#surface-grass"
+ }
+ ]
+ },
+ {
+ "id": "draw",
+ "title": "Draw",
+ "fields": [
+ "Match.draw"
+ ],
+ "about": "Singles or doubles. Null means nothing states which, and is not singles.",
+ "values": [
+ {
+ "code": "singles",
+ "meaning": "Singles",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#draw-singles"
+ },
+ {
+ "code": "doubles",
+ "meaning": "Doubles",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#draw-doubles"
+ }
+ ]
+ },
+ {
+ "id": "format",
+ "title": "Match format",
+ "fields": [
+ "Match.format"
+ ],
+ "about": "The number of sets the match is played over.",
+ "values": [
+ {
+ "code": "BO3",
+ "meaning": "Best of three sets",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#format-bo3"
+ },
+ {
+ "code": "BO5",
+ "meaning": "Best of five sets",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#format-bo5"
+ }
+ ]
+ },
+ {
+ "id": "status",
+ "title": "Match status",
+ "fields": [
+ "Match.status"
+ ],
+ "about": "Where the match is in its lifecycle.",
+ "values": [
+ {
+ "code": "upcoming",
+ "meaning": "Scheduled and not yet started",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#status-upcoming"
+ },
+ {
+ "code": "live",
+ "meaning": "In play",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#status-live"
+ },
+ {
+ "code": "completed",
+ "meaning": "Played to a result",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#status-completed"
+ },
+ {
+ "code": "cancelled",
+ "meaning": "Not played as scheduled",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#status-cancelled"
+ }
+ ]
+ },
+ {
+ "id": "outcome",
+ "title": "Match outcome",
+ "fields": [
+ "Match.outcome"
+ ],
+ "about": "How a finished match was settled. Null while a match is upcoming or live.",
+ "values": [
+ {
+ "code": "completed",
+ "meaning": "Ran its full course",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#outcome-completed"
+ },
+ {
+ "code": "retired",
+ "meaning": "A player retired during play",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#outcome-retired"
+ },
+ {
+ "code": "walkover",
+ "meaning": "Conceded before a ball was struck",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#outcome-walkover"
+ },
+ {
+ "code": "default",
+ "meaning": "A player was defaulted",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#outcome-default"
+ },
+ {
+ "code": "abandoned",
+ "meaning": "Called off unfinished, with no result awarded",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#outcome-abandoned"
+ },
+ {
+ "code": "unresolved",
+ "meaning": "Closed unfinished with no result asserted, until a result is confirmed",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#outcome-unresolved"
+ }
+ ]
+ },
+ {
+ "id": "event-status",
+ "title": "Event status",
+ "fields": [
+ "Match.event_status"
+ ],
+ "about": "The stated match state, published as written. Null means nothing was stated, not that the match finished normally.",
+ "values": [
+ {
+ "code": "Finished",
+ "meaning": "Stated as finished",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#event-status-finished"
+ },
+ {
+ "code": "Retired",
+ "meaning": "Ended by a retirement",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#event-status-retired"
+ },
+ {
+ "code": "Cancelled",
+ "meaning": "Stated as cancelled",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#event-status-cancelled"
+ },
+ {
+ "code": "Walk Over",
+ "meaning": "Walkover",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#event-status-walk-over"
+ },
+ {
+ "code": "Postponed",
+ "meaning": "Stated as postponed",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#event-status-postponed"
+ },
+ {
+ "code": "Interrupted",
+ "meaning": "Suspended in play, for example rain, darkness or a medical stop",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#event-status-interrupted"
+ },
+ {
+ "code": "Unresolved",
+ "meaning": "Closed before a result could be confirmed",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#event-status-unresolved"
+ }
+ ]
+ },
+ {
+ "id": "match-event",
+ "title": "Match event type",
+ "fields": [
+ "Event.type",
+ "GET /matches/{matchId}/events",
+ "GET /events"
+ ],
+ "about": "The timeline of a match. Medical timeouts, trainer calls and toilet breaks are stated by the scorer or chair umpire. The player field names the player where the feed carries one and is null otherwise.",
+ "values": [
+ {
+ "code": "break",
+ "meaning": "A break of serve",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#match-event-break"
+ },
+ {
+ "code": "set_won",
+ "meaning": "A set won",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#match-event-set_won"
+ },
+ {
+ "code": "game_won",
+ "meaning": "A game won",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#match-event-game_won"
+ },
+ {
+ "code": "momentum_run",
+ "meaning": "A momentum run",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#match-event-momentum_run"
+ },
+ {
+ "code": "stoppage_start",
+ "meaning": "Play stopped",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#match-event-stoppage_start"
+ },
+ {
+ "code": "stoppage_end",
+ "meaning": "Play resumed after a stoppage",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#match-event-stoppage_end"
+ },
+ {
+ "code": "pause_start",
+ "meaning": "A pause began",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#match-event-pause_start"
+ },
+ {
+ "code": "pause_end",
+ "meaning": "A pause ended",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#match-event-pause_end"
+ },
+ {
+ "code": "medical_timeout_start",
+ "meaning": "A medical timeout began",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#match-event-medical_timeout_start"
+ },
+ {
+ "code": "medical_timeout_end",
+ "meaning": "A medical timeout ended",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#match-event-medical_timeout_end"
+ },
+ {
+ "code": "trainer_called",
+ "meaning": "The trainer was called",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#match-event-trainer_called"
+ },
+ {
+ "code": "trainer_called_end",
+ "meaning": "The trainer visit ended",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#match-event-trainer_called_end"
+ },
+ {
+ "code": "toilet_break_start",
+ "meaning": "A toilet break began",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#match-event-toilet_break_start"
+ },
+ {
+ "code": "toilet_break_end",
+ "meaning": "A toilet break ended",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#match-event-toilet_break_end"
+ }
+ ]
+ },
+ {
+ "id": "ranking-system",
+ "title": "Ranking system",
+ "fields": [
+ "RankingRecord.system",
+ "GET /rankings?system="
+ ],
+ "about": "The ranking a row belongs to. Elo keeps four independent ladders, named by the surface field: all surfaces together, hard, clay and grass. ATP and WTA Elo are separate and never combined.",
+ "values": [
+ {
+ "code": "atp",
+ "meaning": "ATP singles ranking",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#ranking-system-atp"
+ },
+ {
+ "code": "wta",
+ "meaning": "WTA singles ranking",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#ranking-system-wta"
+ },
+ {
+ "code": "itf_jt",
+ "meaning": "ITF junior ranking",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#ranking-system-itf_jt"
+ },
+ {
+ "code": "itf_mt",
+ "meaning": "ITF men's World Tennis Tour ranking",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#ranking-system-itf_mt"
+ },
+ {
+ "code": "itf_wt",
+ "meaning": "ITF women's World Tennis Tour ranking",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#ranking-system-itf_wt"
+ },
+ {
+ "code": "utr",
+ "meaning": "UTR rating",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#ranking-system-utr"
+ },
+ {
+ "code": "elo",
+ "meaning": "Elo rating, by surface ladder",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#ranking-system-elo"
+ },
+ {
+ "code": "atp_doubles",
+ "meaning": "ATP doubles ranking",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#ranking-system-atp_doubles"
+ },
+ {
+ "code": "wta_doubles",
+ "meaning": "WTA doubles ranking",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#ranking-system-wta_doubles"
+ }
+ ]
+ },
+ {
+ "id": "point-outcome",
+ "title": "Point outcome",
+ "fields": [
+ "HistoryTapeRow.outcome"
+ ],
+ "about": "How a point ended, as stated for that point in the point-by-point history.",
+ "values": [
+ {
+ "code": "ace",
+ "meaning": "Ace",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#point-outcome-ace"
+ },
+ {
+ "code": "double_fault",
+ "meaning": "Double fault",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#point-outcome-double_fault"
+ },
+ {
+ "code": "winner",
+ "meaning": "Winner",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#point-outcome-winner"
+ },
+ {
+ "code": "forced_error",
+ "meaning": "Forced error",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#point-outcome-forced_error"
+ },
+ {
+ "code": "unforced_error",
+ "meaning": "Unforced error",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#point-outcome-unforced_error"
+ }
+ ]
+ },
+ {
+ "id": "rally-outcome",
+ "title": "Charted point outcome",
+ "fields": [
+ "RallyPoint.outcome"
+ ],
+ "about": "How a charted point ended in the shot-level data.",
+ "values": [
+ {
+ "code": "winner",
+ "meaning": "Winner",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#rally-outcome-winner"
+ },
+ {
+ "code": "forced_error",
+ "meaning": "Forced error",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#rally-outcome-forced_error"
+ },
+ {
+ "code": "unforced_error",
+ "meaning": "Unforced error",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#rally-outcome-unforced_error"
+ },
+ {
+ "code": "error",
+ "meaning": "A miss charted without saying whether it was forced",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#rally-outcome-error"
+ },
+ {
+ "code": "other",
+ "meaning": "Any other ending",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#rally-outcome-other"
+ }
+ ]
+ },
+ {
+ "id": "unreturned",
+ "title": "Unreturned serve",
+ "fields": [
+ "RallyPoint.unreturned_kind"
+ ],
+ "about": "How a serve went unreturned. Null when the return was in play.",
+ "values": [
+ {
+ "code": "ace",
+ "meaning": "Ace",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#unreturned-ace"
+ },
+ {
+ "code": "serve_winner",
+ "meaning": "Serve winner charted without a direction, counted with aces",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#unreturned-serve_winner"
+ },
+ {
+ "code": "serve_forced_error",
+ "meaning": "The serve forced a return error",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#unreturned-serve_forced_error"
+ },
+ {
+ "code": "return_forced_error",
+ "meaning": "Forced return error",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#unreturned-return_forced_error"
+ },
+ {
+ "code": "return_unforced_error",
+ "meaning": "Unforced return error",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#unreturned-return_unforced_error"
+ },
+ {
+ "code": "return_error",
+ "meaning": "Missed return, not charted as forced or unforced",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#unreturned-return_error"
+ }
+ ]
+ },
+ {
+ "id": "stroke",
+ "title": "Stroke",
+ "fields": [
+ "RallyShot.stroke"
+ ],
+ "about": "The kind of shot in charted rally data.",
+ "values": [
+ {
+ "code": "serve",
+ "meaning": "Serve",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#stroke-serve"
+ },
+ {
+ "code": "groundstroke",
+ "meaning": "Groundstroke",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#stroke-groundstroke"
+ },
+ {
+ "code": "slice",
+ "meaning": "Slice",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#stroke-slice"
+ },
+ {
+ "code": "volley",
+ "meaning": "Volley",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#stroke-volley"
+ },
+ {
+ "code": "half_volley",
+ "meaning": "Half volley",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#stroke-half_volley"
+ },
+ {
+ "code": "swinging_volley",
+ "meaning": "Swinging volley",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#stroke-swinging_volley"
+ },
+ {
+ "code": "overhead",
+ "meaning": "Overhead",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#stroke-overhead"
+ },
+ {
+ "code": "drop_shot",
+ "meaning": "Drop shot",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#stroke-drop_shot"
+ },
+ {
+ "code": "lob",
+ "meaning": "Lob",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#stroke-lob"
+ },
+ {
+ "code": "trick",
+ "meaning": "Trick shot",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#stroke-trick"
+ },
+ {
+ "code": "unknown",
+ "meaning": "Shot type not recorded",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#stroke-unknown"
+ }
+ ]
+ },
+ {
+ "id": "wing",
+ "title": "Wing",
+ "fields": [
+ "RallyShot.wing"
+ ],
+ "about": "The side a shot was struck from.",
+ "values": [
+ {
+ "code": "forehand",
+ "meaning": "Forehand",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#wing-forehand"
+ },
+ {
+ "code": "backhand",
+ "meaning": "Backhand",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#wing-backhand"
+ }
+ ]
+ },
+ {
+ "id": "direction",
+ "title": "Shot direction",
+ "fields": [
+ "RallyShot.direction"
+ ],
+ "about": "Where the ball was sent.",
+ "values": [
+ {
+ "code": "forehand_side",
+ "meaning": "To the forehand side",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#direction-forehand_side"
+ },
+ {
+ "code": "middle",
+ "meaning": "Through the middle",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#direction-middle"
+ },
+ {
+ "code": "backhand_side",
+ "meaning": "To the backhand side",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#direction-backhand_side"
+ }
+ ]
+ },
+ {
+ "id": "depth",
+ "title": "Shot depth",
+ "fields": [
+ "RallyShot.depth"
+ ],
+ "about": "How deep the ball landed.",
+ "values": [
+ {
+ "code": "shallow",
+ "meaning": "Shallow",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#depth-shallow"
+ },
+ {
+ "code": "mid",
+ "meaning": "Mid-court",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#depth-mid"
+ },
+ {
+ "code": "deep",
+ "meaning": "Deep",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#depth-deep"
+ }
+ ]
+ },
+ {
+ "id": "serve-direction",
+ "title": "Serve direction",
+ "fields": [
+ "RallyPoint.serve_direction"
+ ],
+ "about": "Where the serve was aimed.",
+ "values": [
+ {
+ "code": "wide",
+ "meaning": "Out wide",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#serve-direction-wide"
+ },
+ {
+ "code": "body",
+ "meaning": "At the body",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#serve-direction-body"
+ },
+ {
+ "code": "down_the_t",
+ "meaning": "Down the T",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#serve-direction-down_the_t"
+ }
+ ]
+ },
+ {
+ "id": "error-location",
+ "title": "Error location",
+ "fields": [
+ "RallyPoint.error_location"
+ ],
+ "about": "Where a missed shot went.",
+ "values": [
+ {
+ "code": "net",
+ "meaning": "Into the net",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#error-location-net"
+ },
+ {
+ "code": "wide",
+ "meaning": "Wide",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#error-location-wide"
+ },
+ {
+ "code": "deep",
+ "meaning": "Long",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#error-location-deep"
+ },
+ {
+ "code": "wide_and_deep",
+ "meaning": "Wide and long",
+ "url": "https://docs.livetennisapi.com/tennis-data-vocabulary.html#error-location-wide_and_deep"
+ }
+ ]
+ }
+ ]
+}
diff --git a/docs/tennis-odds.html b/docs/tennis-odds.html
index 6f95458..6641abf 100644
--- a/docs/tennis-odds.html
+++ b/docs/tennis-odds.html
@@ -199,7 +199,7 @@
Skip to content
-
Live Tennis API DocsGet a free key ↗Explore the documentation
+
Live Tennis API DocsGet a free key ↗Explore the documentation
Live Tennis API · docs · version 1.13.78
@@ -286,6 +286,7 @@ Example
curl https://api.livetennisapi.com/api/public/v1/matc
Tennis WebSocket feed and webhooks
Tennis API authentication, quota and status
Live Tennis API tools for the Vercel AI SDK
+Tennis data vocabulary: tiers, rounds, surfaces and results
Generated from openapi.yaml by
livetennisapi/openapi. Every endpoint on
diff --git a/scripts/build_reference.py b/scripts/build_reference.py
index 4ccd883..5d6911a 100644
--- a/scripts/build_reference.py
+++ b/scripts/build_reference.py
@@ -1970,6 +1970,472 @@ def _ai_sdk_body() -> str:
"""
+# ------------------------------------------------------------------ vocabulary -----------------
+# One page and one JSON file that list every closed code the API returns for tournaments, rounds,
+# surfaces, results and shots, with what each code means. The codes are read from openapi.yaml at
+# build time and every code must have a label below, so a new code in the spec fails the build
+# until it is described here. The page and the JSON are written from the same table.
+VOCAB_SLUG = "tennis-data-vocabulary"
+VOCAB_JSON = "tennis-data-vocabulary.json"
+# The day the meanings below were last checked against the spec's own field descriptions.
+VOCAB_CHECKED = "2026-11-06"
+
+
+def _schema_enum(spec: dict[str, Any], schema: str, *path: str) -> list[str]:
+ node: Any = spec["components"]["schemas"][schema]
+ for key in path:
+ node = node["properties"][key] if key != "[]" else node["items"]
+ return [str(v) for v in node["enum"] if v is not None]
+
+
+def _archive_level_codes(spec: dict[str, Any]) -> list[str]:
+ """The letter codes the archive's ?level= parameter describes. The parameter has no enum, so
+ the codes are read from its description: a new code there fails the build until it has a
+ meaning below, the same as every enum."""
+ for prm in spec["paths"]["/history/archive/matches"]["get"].get("parameters", []):
+ if isinstance(prm, dict) and prm.get("name") == "level":
+ codes = re.findall(r"\b([A-Z])=", prm.get("description", ""))
+ if not codes:
+ raise SystemExit("vocabulary: no level codes found in /history/archive/matches?level=")
+ return codes
+ raise SystemExit("vocabulary: /history/archive/matches has no level parameter")
+
+
+def _param_enum(spec: dict[str, Any], route: str, name: str) -> list[str]:
+ for prm in spec["paths"][route]["get"].get("parameters", []):
+ if isinstance(prm, dict) and prm.get("name") == name:
+ return [str(v) for v in prm["schema"]["enum"]]
+ raise SystemExit(f"vocabulary: {route} has no parameter {name}")
+
+
+# (id, title, where it appears, how to read it, reader for the codes, {code: meaning}).
+# Meanings restate the spec's own definitions; nothing here adds a fact the spec does not hold.
+VOCABULARIES: list[dict[str, Any]] = [
+ {
+ "id": "tier",
+ "title": "Tournament tier",
+ "fields": ["Match.tier", "Tournament.tier", "GET /tournaments?tier="],
+ "about": ("The level a tournament was played at in the match's own season, as the tour "
+ "publishes it. A tournament keeps one id while its level changes between seasons, "
+ "so the tier is read per season. On a tournament the field carries the current "
+ "season's level. Null means the level is unknown."),
+ "codes": lambda s: _schema_enum(s, "Match", "tier"),
+ "labels": {
+ "grand_slam": "Grand Slam",
+ "atp_finals": "ATP Finals",
+ "atp_1000": "ATP Masters 1000",
+ "atp_500": "ATP 500",
+ "atp_250": "ATP 250",
+ "next_gen_finals": "Next Gen ATP Finals",
+ "united_cup": "United Cup, every group and stage",
+ "davis_cup": "Davis Cup, every group and stage",
+ "olympics": "Olympic tennis event",
+ "laver_cup": "Laver Cup",
+ "wta_finals": "WTA Finals",
+ "wta_elite_trophy": "WTA Elite Trophy, last held in 2023",
+ "wta_1000": "WTA 1000",
+ "wta_500": "WTA 500",
+ "wta_250": "WTA 250",
+ "wta_125": "WTA 125",
+ "bjk_cup": "Billie Jean King Cup, every group and stage",
+ "challenger_175": "ATP Challenger 175",
+ "challenger_125": "ATP Challenger 125",
+ "challenger_100": "ATP Challenger 100",
+ "challenger_75": "ATP Challenger 75",
+ "challenger_50": "ATP Challenger 50",
+ "itf_m15": "ITF World Tennis Tour, men's M15",
+ "itf_m25": "ITF World Tennis Tour, men's M25",
+ "itf_w15": "ITF World Tennis Tour, women's W15",
+ "itf_w25": "ITF women's W25, a 2023 category kept as printed that season",
+ "itf_w35": "ITF World Tennis Tour, women's W35",
+ "itf_w40": "ITF women's W40, a 2023 category kept as printed that season",
+ "itf_w50": "ITF World Tennis Tour, women's W50",
+ "itf_w60": "ITF women's W60, a 2023 category kept as printed that season",
+ "itf_w75": "ITF World Tennis Tour, women's W75",
+ "itf_w80": "ITF women's W80, a 2023 category kept as printed that season",
+ "itf_w100": "ITF World Tennis Tour, women's W100",
+ "juniors": "Junior events",
+ "utr": "UTR events",
+ "exhibition": "Exhibition with no ranking points",
+ },
+ },
+ {
+ "id": "category",
+ "title": "Tournament category",
+ "fields": ["Tournament.category"],
+ "about": ("The coarse class of a tournament. It is set only where the catalogues agree "
+ "on an exact name, and is null otherwise. Read the tier for the level."),
+ "codes": lambda s: _schema_enum(s, "Tournament", "category"),
+ "labels": {
+ "grand_slam": "Grand Slam",
+ "masters_1000": "Masters 1000",
+ "tour_finals": "Season-ending tour finals",
+ "atp_500": "ATP 500",
+ "atp_250": "ATP 250",
+ "wta_1000": "WTA 1000",
+ "wta_500": "WTA 500",
+ "wta_250": "WTA 250",
+ "wta_125": "WTA 125",
+ "challenger": "ATP Challenger, any level",
+ "itf": "ITF World Tennis Tour, any category",
+ "juniors": "Junior events",
+ },
+ },
+ {
+ "id": "tour",
+ "title": "Tour",
+ "fields": ["Match.tour", "GET /broadcast/live?tour=", "GET /players/{playerId}/stats?level="],
+ "about": ("The tour a match belongs to. The field and the filter come from one registry, "
+ "so a match selected with ?tour=X carries X. Exhibitions, team events and mixed "
+ "events have no public tour name, so their tour is null."),
+ "codes": lambda s: _schema_enum(s, "Match", "tour"),
+ "labels": {
+ "atp": "ATP Tour",
+ "wta": "WTA Tour",
+ "challenger": "ATP Challenger Tour",
+ "itf": "ITF World Tennis Tour",
+ "juniors": "Junior events",
+ },
+ },
+ {
+ "id": "round",
+ "title": "Round code",
+ "fields": ["Match.round_code", "GET /history/archive/matches?round="],
+ "about": ("The round in one controlled vocabulary, shared by live matches and the results "
+ "archive. It is relative to the draw: a qualifying semi-final also reads SF, so "
+ "read it together with is_qualifying. Null when the round label is not recognised. "
+ "Q appears on matches only: the archive's round filter does not accept it."),
+ "codes": lambda s: _schema_enum(s, "Match", "round_code"),
+ "labels": {
+ "F": "Final",
+ "SF": "Semi-final",
+ "QF": "Quarter-final",
+ "R16": "Round of 16",
+ "R32": "Round of 32",
+ "R64": "Round of 64",
+ "R128": "Round of 128",
+ "RR": "Round robin",
+ "BR": "Third-place or bronze-medal match",
+ "Q": "Qualifying round that the label does not number",
+ "Q1": "First qualifying round",
+ "Q2": "Second qualifying round",
+ "Q3": "Third qualifying round",
+ "Q4": "Fourth qualifying round",
+ "ER": "Unnumbered round",
+ },
+ },
+ {
+ "id": "archive-level",
+ "title": "Archive level code",
+ "fields": ["GET /history/archive/matches?level="],
+ "about": ("Level codes used by the results archive. ITF events carry their category "
+ "number instead of a letter, for example 15 or 25."),
+ "codes": lambda s: _archive_level_codes(s),
+ "labels": {
+ "G": "Grand Slam",
+ "M": "Masters",
+ "A": "Other tour-level events",
+ "F": "Season-ending finals",
+ "D": "Davis Cup",
+ "C": "Challenger",
+ "O": "Olympics",
+ },
+ },
+ {
+ "id": "surface",
+ "title": "Court surface",
+ "fields": ["Match.surface", "Tournament.surface", "GET /players/{playerId}/stats?surface="],
+ "about": ("The court surface as stated for the event. It is never inferred from the "
+ "surface a tournament used in other seasons. A Davis Cup, Billie Jean King Cup or "
+ "Laver Cup match carries the surface of its own tie. The player stats filter also "
+ "takes indoor, which keeps matches stated as played indoors on any surface."),
+ "codes": lambda s: _schema_enum(s, "Match", "surface"),
+ "labels": {"hard": "Hard court", "clay": "Clay court", "grass": "Grass court"},
+ },
+ {
+ "id": "draw",
+ "title": "Draw",
+ "fields": ["Match.draw"],
+ "about": "Singles or doubles. Null means nothing states which, and is not singles.",
+ "codes": lambda s: _schema_enum(s, "Match", "draw"),
+ "labels": {"singles": "Singles", "doubles": "Doubles"},
+ },
+ {
+ "id": "format",
+ "title": "Match format",
+ "fields": ["Match.format"],
+ "about": "The number of sets the match is played over.",
+ "codes": lambda s: _schema_enum(s, "Match", "format"),
+ "labels": {"BO3": "Best of three sets", "BO5": "Best of five sets"},
+ },
+ {
+ "id": "status",
+ "title": "Match status",
+ "fields": ["Match.status"],
+ "about": "Where the match is in its lifecycle.",
+ "codes": lambda s: _schema_enum(s, "Match", "status"),
+ "labels": {
+ "upcoming": "Scheduled and not yet started",
+ "live": "In play",
+ "completed": "Played to a result",
+ "cancelled": "Not played as scheduled",
+ },
+ },
+ {
+ "id": "outcome",
+ "title": "Match outcome",
+ "fields": ["Match.outcome"],
+ "about": "How a finished match was settled. Null while a match is upcoming or live.",
+ "codes": lambda s: _schema_enum(s, "Match", "outcome"),
+ "labels": {
+ "completed": "Ran its full course",
+ "retired": "A player retired during play",
+ "walkover": "Conceded before a ball was struck",
+ "default": "A player was defaulted",
+ "abandoned": "Called off unfinished, with no result awarded",
+ "unresolved": "Closed unfinished with no result asserted, until a result is confirmed",
+ },
+ },
+ {
+ "id": "event-status",
+ "title": "Event status",
+ "fields": ["Match.event_status"],
+ "about": ("The stated match state, published as written. Null means nothing was stated, "
+ "not that the match finished normally."),
+ "codes": lambda s: _schema_enum(s, "Match", "event_status"),
+ "labels": {
+ "Finished": "Stated as finished",
+ "Retired": "Ended by a retirement",
+ "Cancelled": "Stated as cancelled",
+ "Walk Over": "Walkover",
+ "Postponed": "Stated as postponed",
+ "Interrupted": "Suspended in play, for example rain, darkness or a medical stop",
+ "Unresolved": "Closed before a result could be confirmed",
+ },
+ },
+ {
+ "id": "match-event",
+ "title": "Match event type",
+ "fields": ["Event.type", "GET /matches/{matchId}/events", "GET /events"],
+ "about": ("The timeline of a match. Medical timeouts, trainer calls and toilet breaks are "
+ "stated by the scorer or chair umpire. The player field names the player where "
+ "the feed carries one and is null otherwise."),
+ "codes": lambda s: _schema_enum(s, "Event", "type"),
+ "labels": {
+ "break": "A break of serve",
+ "set_won": "A set won",
+ "game_won": "A game won",
+ "momentum_run": "A momentum run",
+ "stoppage_start": "Play stopped",
+ "stoppage_end": "Play resumed after a stoppage",
+ "pause_start": "A pause began",
+ "pause_end": "A pause ended",
+ "medical_timeout_start": "A medical timeout began",
+ "medical_timeout_end": "A medical timeout ended",
+ "trainer_called": "The trainer was called",
+ "trainer_called_end": "The trainer visit ended",
+ "toilet_break_start": "A toilet break began",
+ "toilet_break_end": "A toilet break ended",
+ },
+ },
+ {
+ "id": "ranking-system",
+ "title": "Ranking system",
+ "fields": ["RankingRecord.system", "GET /rankings?system="],
+ "about": ("The ranking a row belongs to. Elo keeps four independent ladders, named by "
+ "the surface field: all surfaces together, hard, clay and grass. ATP and WTA Elo "
+ "are separate and never combined."),
+ "codes": lambda s: _schema_enum(s, "RankingRecord", "system"),
+ "labels": {
+ "atp": "ATP singles ranking",
+ "wta": "WTA singles ranking",
+ "atp_doubles": "ATP doubles ranking",
+ "wta_doubles": "WTA doubles ranking",
+ "itf_mt": "ITF men's World Tennis Tour ranking",
+ "itf_wt": "ITF women's World Tennis Tour ranking",
+ "itf_jt": "ITF junior ranking",
+ "utr": "UTR rating",
+ "elo": "Elo rating, by surface ladder",
+ },
+ },
+ {
+ "id": "point-outcome",
+ "title": "Point outcome",
+ "fields": ["HistoryTapeRow.outcome"],
+ "about": "How a point ended, as stated for that point in the point-by-point history.",
+ "codes": lambda s: _schema_enum(s, "HistoryTapeRow", "outcome"),
+ "labels": {
+ "ace": "Ace",
+ "double_fault": "Double fault",
+ "winner": "Winner",
+ "forced_error": "Forced error",
+ "unforced_error": "Unforced error",
+ },
+ },
+ {
+ "id": "rally-outcome",
+ "title": "Charted point outcome",
+ "fields": ["RallyPoint.outcome"],
+ "about": "How a charted point ended in the shot-level data.",
+ "codes": lambda s: _schema_enum(s, "RallyPoint", "outcome"),
+ "labels": {
+ "winner": "Winner",
+ "forced_error": "Forced error",
+ "unforced_error": "Unforced error",
+ "error": "A miss charted without saying whether it was forced",
+ "other": "Any other ending",
+ },
+ },
+ {
+ "id": "unreturned",
+ "title": "Unreturned serve",
+ "fields": ["RallyPoint.unreturned_kind"],
+ "about": "How a serve went unreturned. Null when the return was in play.",
+ "codes": lambda s: _schema_enum(s, "RallyPoint", "unreturned_kind"),
+ "labels": {
+ "ace": "Ace",
+ "serve_winner": "Serve winner charted without a direction, counted with aces",
+ "serve_forced_error": "The serve forced a return error",
+ "return_forced_error": "Forced return error",
+ "return_unforced_error": "Unforced return error",
+ "return_error": "Missed return, not charted as forced or unforced",
+ },
+ },
+ {
+ "id": "stroke",
+ "title": "Stroke",
+ "fields": ["RallyShot.stroke"],
+ "about": "The kind of shot in charted rally data.",
+ "codes": lambda s: _schema_enum(s, "RallyShot", "stroke"),
+ "labels": {
+ "serve": "Serve", "groundstroke": "Groundstroke", "slice": "Slice", "volley": "Volley",
+ "half_volley": "Half volley", "swinging_volley": "Swinging volley",
+ "overhead": "Overhead", "drop_shot": "Drop shot", "lob": "Lob", "trick": "Trick shot",
+ "unknown": "Shot type not recorded",
+ },
+ },
+ {
+ "id": "wing",
+ "title": "Wing",
+ "fields": ["RallyShot.wing"],
+ "about": "The side a shot was struck from.",
+ "codes": lambda s: _schema_enum(s, "RallyShot", "wing"),
+ "labels": {"forehand": "Forehand", "backhand": "Backhand"},
+ },
+ {
+ "id": "direction",
+ "title": "Shot direction",
+ "fields": ["RallyShot.direction"],
+ "about": "Where the ball was sent.",
+ "codes": lambda s: _schema_enum(s, "RallyShot", "direction"),
+ "labels": {"forehand_side": "To the forehand side", "middle": "Through the middle",
+ "backhand_side": "To the backhand side"},
+ },
+ {
+ "id": "depth",
+ "title": "Shot depth",
+ "fields": ["RallyShot.depth"],
+ "about": "How deep the ball landed.",
+ "codes": lambda s: _schema_enum(s, "RallyShot", "depth"),
+ "labels": {"shallow": "Shallow", "mid": "Mid-court", "deep": "Deep"},
+ },
+ {
+ "id": "serve-direction",
+ "title": "Serve direction",
+ "fields": ["RallyPoint.serve_direction"],
+ "about": "Where the serve was aimed.",
+ "codes": lambda s: _schema_enum(s, "RallyPoint", "serve_direction"),
+ "labels": {"wide": "Out wide", "body": "At the body", "down_the_t": "Down the T"},
+ },
+ {
+ "id": "error-location",
+ "title": "Error location",
+ "fields": ["RallyPoint.error_location"],
+ "about": "Where a missed shot went.",
+ "codes": lambda s: _schema_enum(s, "RallyPoint", "error_location"),
+ "labels": {"net": "Into the net", "wide": "Wide", "deep": "Long",
+ "wide_and_deep": "Wide and long"},
+ },
+]
+
+
+def vocabulary_table(spec: dict[str, Any]) -> list[dict[str, Any]]:
+ """The vocabularies with their codes read from the spec. Fails on a code with no label."""
+ out = []
+ for v in VOCABULARIES:
+ codes = v["codes"](spec)
+ missing = [c for c in codes if c not in v["labels"]]
+ extra = [c for c in v["labels"] if c not in codes]
+ if missing or extra:
+ raise SystemExit(
+ f"vocabulary {v['id']}: codes without a label {missing}, labels without a code {extra}."
+ "\n Describe new codes in VOCABULARIES (scripts/build_reference.py)."
+ )
+ out.append({
+ "id": v["id"], "title": v["title"], "fields": v["fields"], "about": v["about"],
+ "values": [{"code": c, "meaning": v["labels"][c],
+ "url": f"{DOCS_URL}/{VOCAB_SLUG}.html#{v['id']}-{_anchor(c)}"} for c in codes],
+ })
+ return out
+
+
+def _anchor(code: str) -> str:
+ return re.sub(r"[^a-z0-9_]+", "-", code.lower()).strip("-")
+
+
+def build_vocabulary_json(spec: dict[str, Any]) -> str:
+ doc = {
+ "name": "Live Tennis API tennis data vocabulary",
+ "description": ("Every closed code the Live Tennis API returns for tournaments, rounds, "
+ "surfaces, results and charted shots, with its meaning."),
+ "url": f"{DOCS_URL}/{VOCAB_JSON}",
+ "page": f"{DOCS_URL}/{VOCAB_SLUG}.html",
+ "spec_version": spec["info"]["version"],
+ "license": "MIT, https://github.com/livetennisapi/openapi/blob/main/LICENSE",
+ "attribution": "Live Tennis API, https://livetennisapi.com",
+ "vocabularies": vocabulary_table(spec),
+ }
+ return json.dumps(doc, ensure_ascii=False, indent=1) + "\n"
+
+
+def _vocabulary_body(spec: dict[str, Any]) -> str:
+ sections = []
+ toc = []
+ for v in vocabulary_table(spec):
+ toc.append(f'
{E(v["title"])} ')
+ rows = "".join(
+ f'{E(x["code"])} '
+ f'{E(x["meaning"])} '
+ for x in v["values"]
+ )
+ fields = ", ".join(f"{E(f)}" for f in v["fields"])
+ table = scrollx(
+ f"{vh_caption(v['title'] + ' codes')}"
+ 'Code Meaning '
+ f"{rows}
",
+ v["title"] + " codes",
+ )
+ sections.append(
+ f'{E(v["title"])}
\n{E(v["about"])}
\n'
+ f'Appears in {fields}.
\n{table}'
+ )
+ return (
+ "How to use these codes
\n"
+ "Each code on this page is one the API can return. Where a field can be null, null "
+ "means the value is not known. The match outcome list can grow within v1, so treat a "
+ "value you do not recognise as unknown. Each code has its own link, so a schema or a "
+ "column note can point at one meaning. The same table is published as "
+ f'{VOCAB_JSON} for code that validates or labels the values.
\n'
+ f'\n'
+ + "\n".join(sections)
+ + "\nLinks
\n\n"
+ f'- The vocabulary as JSON
\n'
+ '- Full API reference
\n'
+ '- OpenAPI spec
\n'
+ f'- Free API key
\n
\n'
+ )
+
+
GUIDES: list[dict[str, Any]] = [
{
"slug": "ai-sdk",
@@ -2004,6 +2470,33 @@ def _ai_sdk_body() -> str:
'livetennisapi/openapi.'
),
},
+ {
+ "slug": VOCAB_SLUG,
+ "title": "Tennis data vocabulary: tiers, rounds, surfaces and results",
+ "head_title": "Tennis data codes: tiers, rounds, surfaces | Live Tennis API",
+ "question": "What does each tennis data code mean?",
+ "description": (
+ "What does each tennis data code mean? Tournament tiers, round codes, surfaces, match "
+ "outcomes and shot types, with one link per code and a JSON copy."
+ ),
+ "lede": (
+ "This page lists every closed code the API returns for tournaments, rounds, surfaces, "
+ "results, rankings and charted shots, with its meaning. It is built from the OpenAPI "
+ "spec, so it lists exactly the codes the API can send."
+ ),
+ "meta": (
+ f'Meanings checked against the spec on . Machine-readable copy: {VOCAB_JSON}, '
+ "MIT licensed, like the spec."
+ ),
+ "body": lambda: _vocabulary_body(load_spec()),
+ "date": VOCAB_CHECKED,
+ "about": {"@type": "DefinedTermSet", "name": "Live Tennis API tennis data vocabulary",
+ "url": f"{DOCS_URL}/{VOCAB_JSON}"},
+ "footer": (
+ 'Page source in livetennisapi/openapi, '
+ "generated by scripts/build_reference.py."
+ ),
+ },
]
@@ -2174,6 +2667,8 @@ def main() -> int:
# The same spec as JSON, for the agents and tools that only read JSON (GPT Actions,
# Postman import, most MCP scaffolds). Both URLs are stable and both are advertised.
DOCS / "openapi.json": json.dumps(spec, ensure_ascii=False, indent=1) + "\n",
+ # The vocabulary page's table as data, for validators and labelling code.
+ DOCS / VOCAB_JSON: build_vocabulary_json(spec),
}
# Heading levels must not skip. h1 -> h3 is a level a screen reader reports as missing and