GET /rally/matches
Charted matches with shot-by-shot data (ULTRA)
Charted matches, newest first. RALLY CONSTRUCTION IS THE LAYER BELOW THE TAPE: the tape says what the score became after each point, this says how the point was played. It has its OWN id space. The charted corpus and our own match table are different populations — the corpus reaches back decades and concentrates on the biggest events, while our matches begin when our own collection did. Keying this on our match ids would hide most of it. Ask this endpoint for the authoritative coverage list rather than assuming a match is charted: charting is human work, so coverage is deep, not universal.
Parameters
| Name | In | Type | Required | Notes |
|---|---|---|---|---|
player | query | string | no | Substring match on either player name. |
from | query | string | no | YYYY-MM-DD. |
to | query | string | no | YYYY-MM-DD. |
surface | query | string | no | |
gender | query | string (M, W) | no | |
limit | query | integer | no | Default 50. |
offset | query | integer | no | Default 0. |
Responses
| Status | Meaning |
|---|---|
200 | Charted matches, with per-match parse-quality counts |
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. |
Response fields
| Field | Type | Description |
|---|---|---|
data | array of object | |
meta | object |
Example
curl https://api.livetennisapi.com/api/public/v1/rally/matches \
-H "Authorization: Bearer twjp_..."
GET /rally/matches/{rallyMatchId}
Rally construction for one charted match (ULTRA)
One charted match with its points, in play order. Paged with limit/offset; meta.total is the match's full point count.
Parameters
| Name | In | Type | Required | Notes |
|---|---|---|---|---|
rallyMatchId | path | integer | yes | |
limit | query | integer | no | Default 50. |
offset | query | integer | no | Default 0. |
Responses
| Status | Meaning |
|---|---|
200 | The charted match with its rally points |
401 | Missing, unknown, or disabled credentials |
403 | Your tier doesn't unlock this endpoint |
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. |
Response fields
| Field | Type | Description |
|---|---|---|
rally_match_id | integer | The id this product is keyed on. |
source_id | string | |
match_id | integer or null | OUR match id, when the charted match is also one we hold. Null otherwise — most charted matches predate our own collection. |
date | string or null | |
tournament | string or null | |
round | string or null | |
surface | string or null | |
gender | string or null (M, W, null) | |
best_of | integer or null | |
players | array of object | |
points | integer | Charted points in this match. |
points_parsed | integer | How many of them our parser read cleanly — the per-match quality number. |
meta | object | |
rally | array of object |
Example
curl https://api.livetennisapi.com/api/public/v1/rally/matches/{rallyMatchId} \
-H "Authorization: Bearer twjp_..."
GET /history/matches/{matchId}/rally
Rally construction by OUR match id (ULTRA)
Rally construction addressed by OUR match id, resolved through the optional link. Answers 404 {"error":"not_charted"} when we hold the match but nobody charted it — deliberately distinct from "no such match", because most of our matches are not charted and a consumer walking the archive must tell them apart.
Parameters
| Name | In | Type | Required | Notes |
|---|---|---|---|---|
matchId | path | integer | yes | The match id. Every match route shares ONE id space, so the same value works everywhere matchId appears.
Get it from any match list, where it is the id field of each match object:
GET /matches?status=live (in progress), GET /matches?status=upcoming or GET /fixtures (scheduled), GET /matches?status=completed or GET /history/matches (finished).
Ids are stable for the life of a match, so one captured before it starts still resolves after it finishes.
|
limit | query | integer | no | Default 50. |
offset | query | integer | no | Default 0. |
Responses
| Status | Meaning |
|---|---|
200 | The charted match with its rally points |
401 | Missing, unknown, or disabled credentials |
403 | Your tier doesn't unlock this endpoint |
404 | No such resource, or no data yet |
410 | The match id existed and was merged into another match record (since 2026-09-05). The body is MatchMerged, not Error: it carries a forwarding address, which is the whole point of answering 410 rather than 404. |
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. |
Response fields
| Field | Type | Description |
|---|---|---|
rally_match_id | integer | The id this product is keyed on. |
source_id | string | |
match_id | integer or null | OUR match id, when the charted match is also one we hold. Null otherwise — most charted matches predate our own collection. |
date | string or null | |
tournament | string or null | |
round | string or null | |
surface | string or null | |
gender | string or null (M, W, null) | |
best_of | integer or null | |
players | array of object | |
points | integer | Charted points in this match. |
points_parsed | integer | How many of them our parser read cleanly — the per-match quality number. |
meta | object | |
rally | array of object |
Example
curl https://api.livetennisapi.com/api/public/v1/history/matches/18953/rally \
-H "Authorization: Bearer twjp_..."
GET /charting/players
Career shot-level charting aggregate for one player (ULTRA)
The deepest serve/return profile we hold, from the Match Charting Project: serve placement (deuce/ad × wide/body/T), return depth and outcomes, net and serve-and-volley conversion, clutch break/game/set-point serving and returning, winners and unforced errors by wing, and rally-length and shot-direction tendencies — summed over the player's charted matches. name (min 3 chars) is the key; a fragment matching more than one charted person is refused with the candidate list, and gender=men|women disambiguates. Every field is a raw SUM over the player's Total rows and matches_charted states the sample. COVERAGE IS CURATED — 11,646 charted matches across both tours back to the 1960s, concentrated on the majors, NOT full-slate coverage.
Parameters
| Name | In | Type | Required | Notes |
|---|---|---|---|---|
name | query | string | yes | Player name (fragment, min 3 chars). |
gender | query | string (men, women) | no | Disambiguates a fragment that matches one charted person per tour side. |
Responses
| Status | Meaning |
|---|---|
200 | The player's summed charting families with the sample size |
400 | Bad query parameter |
401 | Missing, unknown, or disabled credentials |
403 | Your tier doesn't unlock this endpoint |
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. |
Response fields
| Field | Type | Description |
|---|---|---|
player | object | |
matches_charted | integer | |
coverage | string | |
families | object | Per-family summed numeric columns. |
Example
curl https://api.livetennisapi.com/api/public/v1/charting/players \
-H "Authorization: Bearer twjp_..."
GET /charting/matches/{chartingMatchId}
One charted match, every stat family for both players (ULTRA)
Every Match Charting Project stat family for one charted match, both players, with the per-set split (row/set 1, 2, Total) exactly as charted. chartingMatchId is this product's own id space (1960–2026, mostly matches with no counterpart in the live table).
Parameters
| Name | In | Type | Required | Notes |
|---|---|---|---|---|
chartingMatchId | path | integer | yes |
Responses
| Status | Meaning |
|---|---|
200 | The charted match's stat families, both players, per set |
401 | Missing, unknown, or disabled credentials |
403 | Your tier doesn't unlock this endpoint |
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. |
Response fields
| Field | Type | Description |
|---|---|---|
charting_match_id | integer | |
mcp_id | string | |
gender | string | |
players | object | |
families | object |
Example
curl https://api.livetennisapi.com/api/public/v1/charting/matches/{chartingMatchId} \
-H "Authorization: Bearer twjp_..."