Point-by-point tennis history, 2013 onward
A clean, point-by-point event tape of completed matches — the ordered score states of each match. Two records, one row shape. From January 2023 it is our own collection across ATP, WTA, Challenger, ITF and doubles: every match declares its own coverage, so you always know whether you are holding a tape we watched live from 0-0 — timestamps and model win-probability included — or one reconstructed from a point-by-point feed. Backtest, train, and study how matches actually swing. Before that, a separate 2013-2022 reconstruction: 97,901 more ATP, WTA and Challenger matches, rebuilt from the public record and carrying the score sequence alone.
See plans — from $29/mo Create an account
Same shape, different provenance
One parser reads both. What separates them is not the format — it is how the rows came to exist, and that governs what you may ask of them.
| 2013-2022 archive | January 2023 → now | |
|---|---|---|
| How the rows were obtained | Reconstructed from the public record | Watched live by our own collection, or reconstructed from a point-by-point feed |
| Did anyone watch it? | No — not one row | Yes, on the observed rows |
| Per-point timestamp | null on every row — the table has no timestamp column |
Real UTC timestamp on every observed row; observed rows begin March 2026 — capture itself started 12 October 2025 at low volume (25,747 matches carry at least one as of August 27, 2026) |
| Model win-probability / danger | null on every row — these states were never scored |
On most observed rows; null where the best-effort stamp did not run |
| Size | 97,901 matches · 14,340,663 point rows | More than 171,000 matches · over 26 million point states (counted August 19, 2026) |
| Span | 2013–2022, and nothing earlier | January 2023 → August 2026, growing as matches complete |
| Tours | ATP, WTA and Challenger; ITF not covered | ATP, WTA, Challenger and ITF, singles and doubles |
meta.point_source |
always reconstructed |
observed, mixed or reconstructed |
| What “reconstructed” means here | Rebuilt from the public record years later, and proved back onto the archive match | Rebuilt from a point-by-point feed we did not watch from 0-0 — same word, a different source |
| Read it at | GET /history/archive/matches/{id}/tape | GET /history/matches/{id} |
| Included with | core ULTRA, or any Historical Data plan | Core BASIC and above, or any Historical Data plan |
The two records live in separate id spaces and are never served from one
endpoint. An archive id is an /history/archive/matches row id, not
a match id; the response names it meta.archive_match_id so the two can never be
silently swapped.
The 2023+ tape: what we collect ourselves
- Point-by-point event tape. The ordered score states of a match — sets, games, points, server — not just final scores.
- Coverage you can filter on. Every match carries a
coverageflag:from_start(watched live from 0-0),partial,reconstructed,reconstructed_partial, ornone. Query the index by it, so a backtest never silently mixes what we observed with what we rebuilt. Read it as a statement about how the tape opens: a match we watched whose opening games were rebuilt afterwards opens with rebuilt rows, so it reportsreconstructedeven though the rest of it was observed live. To select on what was actually watched, usetape.rowson the index — it counts observed rows only — ormeta.point_source(observed,mixed,reconstructed) on the tape itself. - Model win-probability where we watched it. Tapes we captured live carry a real
timestamp on every row, and our proprietary win-probability on most of them —
that stamp is best-effort, so where it was not computed the field is
nullrather than guessed. Reconstructed rows carry the score only — no timestamp and no model probability, which is exactly how you tell them apart at row level — and a tape can hold both. Themetablock on every response tells you which you hold. - When the clock starts. This record's score sequence runs from
January 2023 (the 2013-2022 archive below is the earlier one). The rows we watched ourselves — the ones carrying a
real per-point timestamp and, where the model scored the state, a
win-probability — begin in March 2026 (our own live capture started on
12 October 2025 at low volume; rows reach the product from March 2026): as of
August 27, 2026, 25,747 completed matches
carry at least one, across 6.2 million such rows. Earlier
matches are the reconstruction — true about the score sequence, silent about
the clock — and we never estimate one. Select on it with
tape.rows,meta.point_source, or the row's owntimestamp. - Measured completeness. Every tape read carries a
meta.pointsverdict measured for that match, and?points=completeserves the sequence in full — score after each point, with server and point winner — where that verdict is complete. As of August 19, 2026, 75.0% of completed matches since 2023 are measured point-complete, and the running backfills keep raising that share. - Four tours. ATP, WTA, Challenger and ITF — singles and doubles; ITF point depth from 2025 via the governing body's own umpire feed.
- Built for analysis. Structured for backtesting, model training and match research.
Before 2023: the 2013-2022 reconstruction
97,901 ATP, WTA and Challenger matches played between 2013 and 2022, 14,340,663 point rows — the score sequence behind a result that was never streamed. It is rebuilt from the public record, not a recording, and everything below follows from that. There is nothing before 2013: our results archive reaches back to 1968, but no point-by-point record of those years exists to rebuild from, and we do not manufacture one.
- Point-granular, nearly throughout. 99.43% of those matches (97,293) advance point by point; the remaining 556 resolve to one row per game (555 of them in 2013 and one in 2014), and every season from 2015 on is wholly point-granular. A tape averages 146 rows, median 138.
- No clock, and no model. Every row is null on
timestamp,win_probability_p1anddanger— not by convention but by storage: there is no timestamp column to fill, so no later backfill can put a wall clock on a 2016 point. These states were never scored by a model and nothing has been back-computed to look as though they were. Our rule for the 2023+ tape — a real timestamp means we watched that point — holds across both halves of the product. - Qualifying draws, at scale. 30,306 of the 97,901 matches (31.0%) are qualifying play, and 67,595 (69.0%) main draw. Point-by-point qualifying data at this size is genuinely rare — with the caveat in the table below, that slam qualifying is its thinnest bucket (16.0% to 18.1%).
- Bound to the right match, provably. A tape attached to the wrong match is worse than no tape, so each one proves 5 things before it is published — edition binding, player-name identity, round agreement, exact reproduction of the published final score, and a date window — and then passes a 23-invariant interior audit that rejects any physically impossible sequence. A tape that cannot prove its binding is refused outright rather than published against a guess. Across three independent audits, 70 matches were checked against outside sources (Wikipedia draws, a third-party vendor) with 0 mismaps; over the full table, 23 of 23 invariants sit at zero violations with no score mismatches.
- What we know is imperfect. 818 within-game point gaps across 633 matches, and 37 tiebreak rows with the same shape — a point occasionally under-reported by the source. Each endpoint is still a legal score, so a tape is conservative, never inflated. And the archive renders a few players inconsistently — Ekaterina Makarova appears as “Kate Makarova” on 270 matches.
- Two tours, and they account for all of it. ATP 64,785 matches / 9,676,061 rows, WTA 33,116 / 4,664,602 — together the whole 97,901 and 14,340,663. Challenger sits inside the ATP figure, since it is ATP-sanctioned.
Coverage by tier and draw
Share of archive matches actually played in 2013-2022 that carry a tape, as of September 1, 2026: 19.3% across all play, and 44.9% of tour-level play. Where that difference lands is what the table is for — main draws run 91.6% to 98.7%, Challenger main draw 55.3%, slam qualifying 16.0% to 18.1%:
| Tier and draw | With a tape | Played | Share |
|---|---|---|---|
| ATP Masters, main draw | 4,775 | 4,837 | 98.7% |
| ATP Grand Slam, main draw | 4,702 | 4,797 | 98.0% |
| WTA Premier Mandatory, main draw | 1,841 | 1,880 | 97.9% |
| WTA Grand Slam, main draw | 4,674 | 4,797 | 97.4% |
| ATP 250/500, main draw | 12,219 | 12,802 | 95.4% |
| WTA Premier, main draw | 5,782 | 6,142 | 94.1% |
| WTA International, main draw | 7,296 | 7,965 | 91.6% |
| ATP Challenger, main draw | 23,802 | 43,038 | 55.3% |
| ATP Challenger, qualifying | 7,212 | 21,455 | 33.6% |
| WTA Grand Slam, qualifying | 675 | 3,734 | 18.1% |
| ATP Grand Slam, qualifying | 706 | 4,412 | 16.0% |
| ITF and futures, all draws | 141 | 145,117 | under 0.1% |
ITF and futures are not covered: 25 of 116,575 ATP futures, 68 of 19,162 ITF M15 and 48 of 9,380 ITF M25 matches carry a tape — if your work is ITF, this corpus is not for you and we would rather you knew that here than after buying. Coverage also varies by year (Coverage roughly doubles from 2017 on and holds there; 2020 is the short covid season): 2013 14.0%, 2014 13.2%, 2015 13.6%, 2016 12.5%, 2017 21.3%, 2018 22.9%, 2019 22.7%, 2020 25.9%, 2021 25.5%, 2022 24.2%.
Coverage labels on the tape itself:
reconstructed on 94,307 matches
(96.33%) means it reproduces the published final score
exactly, and that score is a match that finished. reconstructed_partial on
3,594 (3.67%) has two causes and
the label does not say which: 3,038 of them are matches
that stopped early — a retirement or a default, with the tape running exactly as far as
play did — and 556 are per-game tapes, which are labelled
this way whatever their completeness (542 of those
are complete matches). To tell them apart, read granularity and the archive
row's own score and outcome, not the label.
How to reach it. One match:
GET /history/archive/matches/{id}/tape — core ULTRA, or any Historical Data plan. A whole year
in bulk: GET /history/packages/{YYYY}?kind=archive_tape — core ULTRA, or Historical Data Pro, Business or an active one-off package;
all ten years 2013-2022 are published as of
September 1, 2026. A key without the entitlement gets
403 upgrade_required carrying capability: archive_tape, which is
the string to branch on. The archive result
(GET /history/archive/matches/{id}) stays where it has always
been — core Basic and above, or any History plan; the tape is the half this adds. Ids here
are archive_matches.id, a different space from live match ids.
Full field-by-field reference:
the 2013-2022
tape in the API docs.
Backtesting against ratings too? The core API's ULTRA plan serves an as-of Elo tape on GET /rankings?system=elo — what a player was rated before a match, ATP from 1877-07-23 and WTA from 1968-01-08, main tour plus challengers plus the futures tier, with no look-ahead by construction (65,622 rated players and 3,417,835 rating snapshots as of the 2026-08-12 build). It is a historical tape, not a live leaderboard: the corpus behind it is fixed, ending 2026-05-25. The plan also serves per-player as-of UTR ratings on system=utr, with history since 2026-07-29.
What a response actually looks like
Three real matches, one from each half of the product. The first we watched live from 0-0; the second is a 2023+ match reconstructed from a point-by-point feed; the third is a 2013-2022 archive tape. The differences are visible in the data, not just in the docs. Field-by-field reference: the 2013-2022 tape in the API docs.
2023+ tape, watched live — coverage: from_start
GET /api/public/v1/history/matches/22896?sequence=clean
Kei Nishikori vs Juncheng Shang, ATP Washington, 27 Jul 2026.
{
"meta": {
"coverage": "from_start",
"point_source": "observed",
"raw_rows": 212,
"rows": 199,
"unique_states": 199,
"sequence": "clean"
},
"tape": [
{ "sets": [0,0], "games": [[0],[0]], "points": ["0","0"], "server": null,
"is_tiebreak": false,
"timestamp": "2026-07-27T21:08:22.773825Z",
"win_probability_p1": 0.39962309494767223, "danger": 0.05732571932609154 },
{ "sets": [0,0], "games": [[0],[0]], "points": ["0","15"], "server": 2,
"is_tiebreak": false,
"timestamp": "2026-07-27T21:08:48.219296Z",
"win_probability_p1": 0.3970008761094225, "danger": 0.10835421670547885 }
]
}
Every row carries a real timestamp, because we were watching when it happened. The
model's win-probability is stamped where it was computed — that step is best-effort in
the live path, so on some observed rows the field is null rather than
filled in after the fact. Both rows above happen to carry it.
2023+ tape, reconstructed from a point-by-point feed — coverage: reconstructed
GET /api/public/v1/history/matches/167732?sequence=clean
Marco Trungelliti vs Maximus Jones, Nonthaburi, 31 Dec 2024.
{
"meta": {
"coverage": "reconstructed",
"point_source": "reconstructed",
"raw_rows": 166,
"rows": 149,
"unique_states": 149,
"sequence": "clean"
},
"tape": [
{ "sets": [0,0], "games": [[0],[0]], "points": ["0","0"], "server": 1,
"is_tiebreak": false,
"timestamp": null, "win_probability_p1": null, "danger": null },
{ "sets": [0,0], "games": [[0],[0]], "points": ["15","0"], "server": 1,
"is_tiebreak": false,
"timestamp": null, "win_probability_p1": null, "danger": null }
]
}
The score sequence is real — it came from a point-by-point feed. What is absent is absent on purpose: timestamp and the model fields are null, because no model ever saw that state. Nothing is synthesised from a final scoreline, and you can exclude these entirely with ?coverage=from_start if your backtest needs observed data only.
point_source can also read mixed, where we joined a match part-way and the opening was filled in afterwards. And note raw_rows vs rows above: ?sequence=clean collapses repeated commits of the same score state — on a busy match that is 5,905 rows down to 89.
Reconstructed archive (2013-2022) — granularity: point
GET /api/public/v1/history/archive/matches/989456/tape
Angelique Kerber def. Serena Williams 6-4 3-6 6-4, Australian Open final, 18 Jan 2016.
177 rows. Note the id: this is an archive_matches.id, not a match id.
{
"meta": {
"archive_match_id": 989456,
"rows": 177,
"coverage": "reconstructed",
"granularity": "point",
"point_source": "reconstructed",
"model_rows": 0,
"observed_rows": 0,
"point_source_detail": "reconstruction from public record",
"sequence": "raw",
"generated_at": "2026-09-01T07:42:54.016856Z"
},
"match": {
"id": 989456, "tour": "wta", "level": "G", "tournament": "Australian Open",
"surface": "hard", "draw_size": 128, "event_date": "2016-01-18", "round": "F",
"winner": { "name": "Angelique Kerber", "rank": 6, "seed": 7, "player_id": 201493 },
"loser": { "name": "Serena Williams", "rank": 1, "seed": 1, "player_id": 200033 },
"score": "6-4 3-6 6-4", "outcome": "completed"
},
"profiles": [],
"tiebreaks": null,
"tape": [
{ "sets": [0,0], "games": [[0],[0]], "points": ["0","0"], "server": 2,
"is_tiebreak": false, "point_winner": null,
"timestamp": null, "age_seconds": null, "stale": false,
"win_probability_p1": null, "danger": null },
{ "sets": [0,0], "games": [[0],[0]], "points": ["0","15"], "server": 2,
"is_tiebreak": false, "point_winner": 2,
"timestamp": null, "age_seconds": null, "stale": false,
"win_probability_p1": null, "danger": null },
… 174 rows …
{ "sets": [2,1], "games": [[6,3,6],[4,6,4]], "points": ["0","0"], "server": 2,
"is_tiebreak": false, "point_winner": 1,
"timestamp": null, "age_seconds": null, "stale": false,
"win_probability_p1": null, "danger": null }
]
}
Three fields are null on every row this endpoint can ever serve — timestamp, win_probability_p1 and danger — and meta.model_rows and meta.observed_rows are always 0. That is not a property of this match; the table has no such columns. meta.granularity says point or game, and meta.coverage is one of the two words above. Rows are winner-first — sets[0] is the winner's and server: 1 means the winner was serving — because an archive result is recorded winner/loser-shaped and the match is over; the 2023+ tape is p1/p2-shaped instead. A match with no tape answers 404 not_found, which is also the answer when a tape exists and failed the identity proof: we do not distinguish them.
In bulk: GET /history/packages/{YYYY}?kind=archive_tape — one JSONL and one
CSV per year. The JSONL record is what the per-match endpoint returns, so a parser written
against one match runs over a whole year unchanged. The CSV deliberately carries no
timestamp, win_probability_p1 or danger column at all:
a column empty in every row of a file invites the belief that it is sometimes populated.
Pricing
Three monthly plans, one ladder: Starter reads the tape per match — both halves, including the 2013-2022 reconstruction — Pro adds the bulk downloads, Business adds year-scale package exports. Every plan includes API access to the history endpoints on your linked key — a free key is enough. Prefer no subscription? Buy a one-off package instead.
The other way in is the core API: the archive is on core ULTRA, $99.99/mo, along with the per-point live feed, WebSocket and webhook push, and the as-of Elo tape. For the 2013-2022 archive on its own, History Starter is the cheaper of the two at $29/mo.
Starter $29/mo
- Best for: A single backtest or study
- Point-by-point tape from January 2023 + model win-probability where we scored it, per match
- Per-match reads of the 2013-2022 reconstructed archive
- Completed-match listings — ATP · WTA · Challenger · ITF
- Works on a free API key — no core subscription needed
Pro $99/mo
- Best for: Ongoing modelling across tours
- Everything in Starter, plus:
- Bulk monthly package downloads (JSONL + CSV)
- Per-year 2013-2022 archive files — one JSONL + CSV for each of the ten years, all published
- 300 requests/min on the history endpoints
Business $299/mo
- Best for: Whole-corpus access for a team or product
- Everything in Pro, plus:
- Year-scale package exports — a full year of published months in one call
- Top history rate limit — 600 requests/min
- Priority support
| Starter | Pro | Business | |
|---|---|---|---|
| Price | $29/mo | $99/mo | $299/mo |
| Best for | A single backtest or study | Ongoing modelling across tours | Whole-corpus access for a team or product |
| Point-by-point tape (per match) | ✓ | ✓ | ✓ |
| 2013-2022 archive — per match | ✓ | ✓ | ✓ |
| Bulk monthly downloads | — | ✓ | ✓ |
| 2013-2022 archive — per-year files | — | ✓ | ✓ |
| Year-scale package exports | — | — | ✓ |
| History rate limit | Your key's plan limit (a free key: 30 req/min) | 300 req/min (or your key's higher limit) | 600 req/min |
| Support | Standard | Standard | Priority |
| Get started | Get Starter | Get Pro | Get Business |
No subscription needed? Grab a one-off package: $49 for a full month of history, or $399 for a full year. While it's active, a package works as Pro-level access for its window — tape reads (both halves, including the 2013-2022 archive) and its per-year archive files, plus the bulk monthly downloads; the 1-year package also includes the year-scale package listing.
Buy 1 month — $49 Buy 1 year — $399
Need a custom volume, date range or tour mix? Email us and we'll size a plan.
Plans, coverage and the tape — FAQ
What's the difference between the plans?
Starter is per-match reads: the point-by-point tape and completed-match listings across ATP, WTA, Challenger and ITF from January 2023, plus per-match reads of the 2013-2022 reconstructed archive. Pro adds the bulk monthly package downloads (JSONL and CSV), the per-year 2013-2022 archive files, and a higher rate limit on the history endpoints (300 requests/min). Business adds year-scale package exports (every published month of a year in one call), the top history rate limit (600 requests/min), and priority support.
How far back does the data go?
The live point-by-point tape runs from January 2023 through August 2026 — more than 171,000 completed matches and over 26 million archived point states (counted August 19, 2026), across ATP, WTA, Challenger, ITF and doubles. It grows as matches complete. That span is the score sequence. The rows we watched ourselves — the ones carrying a real per-point timestamp — begin in March 2026 (our own live capture started on 12 October 2025 at low volume; rows reach the product from March 2026): as of August 27, 2026, 25,747 completed matches carry at least one, across 6.2 million such rows. Earlier matches are reconstructed from a point-by-point feed: true about the score sequence, with no timestamp, and we never estimate one. There is a second, separate corpus behind that: the 2013-2022 reconstructed archive, 97,901 ATP, WTA and Challenger matches and 14,340,663 point rows rebuilt from the public record, with no timestamps and no model fields on any row. It stops at 2013: our results archive reaches back to 1968, but no point-by-point record of those earlier years exists to rebuild from.
What's in the tape?
The ordered score states of a completed match — sets, games, points, server — plus match metadata. Every match declares its own coverage, and you can filter on it: from_start means we watched it live from 0-0, so those rows carry a real timestamp, and our model's win-probability where it was computed — that stamp is best-effort, so it is present on most observed rows and null on the rest. Read the flag as a statement about how the tape opens: a match we watched whose opening games were rebuilt afterwards reports reconstructed even though the rest of it was observed live, so select on tape.rows or meta.point_source rather than the flag alone. At row level the rule is simple: a rebuilt row carries the score only — no timestamp, no model probability — and a tape can hold both kinds. Nothing is ever synthesised from a final scoreline. Completeness is measured per match, not assumed: every tape read carries a meta.points verdict, ?points=complete serves the sequence in full — score after each point, with server and point winner — where that verdict is complete, and as of August 19, 2026, 75.0% of completed matches since 2023 are measured point-complete, a share the running backfills keep raising.
Is there point-by-point data before 2023?
Yes, back to 2013 — and nothing before it. The 2013-2022 archive holds 97,901 ATP, WTA and Challenger matches and 14,340,663 point rows, reconstructed from the public record rather than watched, so no row carries a timestamp or a model number and none ever will. 99.43% of those matches are point-granular; the other 556 resolve to one row per game (555 of them in 2013 and one in 2014). Coverage as of September 1, 2026 is 19.3% of archive matches played in those years and 44.9% of tour-level play — the main draws run 91.6% to 98.7%, Challenger main draw 55.3%, but slam qualifying is 16.0% to 18.1% and ITF and futures are not covered at all. Read a match with GET /history/archive/matches/{id}/tape (core ULTRA, or any Historical Data plan); the per-year bulk files are core ULTRA, or Historical Data Pro, Business or an active one-off package.
Do I need a core API subscription?
No. Every History plan includes API access to the history endpoints on your linked key — a free key is enough. The reverse holds in part: a core PRO or ULTRA subscription already includes the 2023-onward per-match tape and the bulk monthly package downloads on its own key, so there is no History plan to add for those. The 2013-2022 reconstructed archive is the exception — it is on core ULTRA, and on any History plan alongside the key you already have, but it is not part of core PRO.
Choose a plan Create an account
Doing non-commercial academic research or teaching? The January 2023 onward point-by-point data is free under our academic programme, with a citable DOI. The 2013-2022 reconstruction is a commercial product and is not part of that release.
From the makers of the Live Tennis API — real-time scores, odds and model win-probability. Want live signals instead? See Break-point Alerts.