ATP & WTA Match Dataset - Sackmann CSV Format
Pricing
Pay per event
ATP & WTA Match Dataset - Sackmann CSV Format
Whole seasons of ATP and WTA singles results in the columns of Jeff Sackmann's tennis_atp / tennis_wta CSV files, from 1990 to this week: winner and loser, rank, seed, entry, hand, height, age, score, round, minutes, serve statistics, plus pre-match Elo. Updated every hour. Export as CSV.
Pricing
Pay per event
Rating
0.0
(0)
Developer
CrawlPlant
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
an hour ago
Last modified
Share
Independent tool, not affiliated with, endorsed by or connected to Jeff Sackmann, Tennis Abstract, Flashscore, the ATP, the WTA or the ITF. It reads the public match data that flashscore.com shows to every visitor and the official ATP and WTA ranking lists and player profiles.
Whole seasons of ATP and WTA singles results in the columns of Jeff Sackmann's atp_matches_YYYY.csv and
wta_matches_YYYY.csv, from 1990 to this week, updated every hour. Code and notebooks that read his files by column name
keep working: winner and loser with rank and ranking points of that week, seed, entry (Q, WC, LL, PR), hand, height,
country and age, the score (7-6(5) 6-4, RET, W/O), best of, round, minutes and the serve statistics
(w_ace ... l_bpFaced), plus each player's Elo before the match. Export the dataset as CSV.
Why this one
- Current. This week's matches are in the dataset within the hour: no waiting for a yearly update.
- Same columns, same order.
tourney_id,tourney_name,surface,draw_size,tourney_level,tourney_date,match_num,winner_*,loser_*,score,best_of,round,minutes,w_*/l_*serve columns,winner_rank,winner_rank_points,loser_rank,loser_rank_points. - Seeds and entries from the official draws, hand and height from the official ATP and WTA player profiles, ranks from the official weekly lists.
- Elo included. Four extra columns close each row:
match_id,tour,winner_eloandloser_elo(pre-match Elo, overall). - ATP, WTA, Challenger and ITF.
tours: ["challenger-men"]gives his qual_chall files,["itf-men"]his futures. - Any range. Whole seasons, or the last 7 days to keep your own copy current.
- See it live. The size of the database, the ranking weeks behind the rank columns and the Elo leaders of both tours: crawlplant.com/tennis-match-dataset.
What can you use it for?
- Prediction and betting models trained on the same columns as the best-known open tennis dataset, kept current.
- Research and journalism: season-by-season results with rankings, seeds and serve statistics.
- Replacing a stale copy: refresh your atp_matches / wta_matches tables weekly with one scheduled task.
- AI agents: structured match history as JSON or CSV.
Quick start
- Click Try for free with the default input (the first 1,000 matches of the 2025 ATP season).
- Set Years and Tours, and Max results above the season size (ATP about 4,300 matches, WTA about 3,800).
- Export the dataset as CSV: the columns come in his order.
Copy to your AI assistant
Paste this into ChatGPT, Claude or any agent so it knows how to use the Actor:
crawlplant/tennis-match-dataset on Apify: ATP / WTA / Challenger / ITF singles results in the columns of Jeff Sackmann'satp_matches_YYYY.csv (tourney_id, tourney_name, surface, draw_size, tourney_level, tourney_date, match_num, winner_id,winner_seed, winner_entry, winner_name, winner_hand, winner_ht, winner_ioc, winner_age, loser_*, score, best_of, round,minutes, w_ace ... l_bpFaced, winner_rank, winner_rank_points, loser_rank, loser_rank_points) + match_id, tour,winner_elo, loser_elo. From 1990, updated hourly. Input: years (e.g. [2024, 2025]), tours (atp, wta, challenger-men,challenger-women, itf-men, itf-women), dateFrom / dateTo (when years is empty), maxItems (default 1000).Price: $1.00 per 1,000 matches on the Free plan.
Ready-to-use examples
1. A full ATP season
{ "years": [2025], "tours": ["atp"], "maxItems": 5000 }
2. Three WTA seasons
{ "years": [2023, 2024, 2025], "tours": ["wta"], "maxItems": 15000 }
3. This season so far, both tours
{ "years": [], "tours": ["atp", "wta"], "maxItems": 10000 }
4. The last 7 days, to keep your copy current
{ "years": [], "dateFrom": "-7", "dateTo": "today", "tours": ["atp", "wta"], "maxItems": 5000 }
5. Challenger season (his qual_chall file)
{ "years": [2025], "tours": ["challenger-men"], "maxItems": 20000 }
6. The 1990s, ATP
{ "years": [1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999], "tours": ["atp"], "maxItems": 40000 }
How to…
Replace Jeff Sackmann's tennis_atp and tennis_wta CSV files
Run one season per tour (example 1, then "tours": ["wta"]) and export as CSV. Code that reads his files by column name
works unchanged; keep the rows whose round doesn't start with "Q" for his main-draw-only files.
Keep a match dataset up to date every week
Save example 4 as a task and schedule it weekly: each run returns the last 7 days, and match_id lets you upsert
without duplicates.
Get ATP and WTA results with rankings for a model
Every row carries both players' rank and ranking points from the official list of that week, the seeds and entries of
the draw, and pre-match Elo (winner_elo, loser_elo).
Input options
| Option | Default | Description |
|---|---|---|
years | [2025] | Seasons, from 1990; empty = the From / To days |
tours | atp | atp, wta, challenger-men, challenger-women, itf-men, itf-women; empty = ATP and WTA |
dateFrom / dateTo | this season / today | When years is empty: 2026-01-01, -7, today |
maxItems | 1000 | Maximum matches in total |
Example output
The 2025 Hong Kong final, a real row:
{"tourney_id": "2024-C8wqzbIc","tourney_name": "Hong Kong","surface": "Hard","draw_size": 16,"tourney_level": "A","tourney_date": 20241230,"match_num": 15,"winner_id": "nLLdS9q0","winner_seed": null,"winner_entry": null,"winner_name": "Alexandre Muller","winner_hand": "R","winner_ht": 183,"winner_ioc": "FRA","winner_age": 27.9,"loser_id": "YNkOjUIB","loser_seed": null,"loser_entry": "WC","loser_name": "Kei Nishikori","loser_hand": "R","loser_ht": 178,"loser_ioc": "JPN","loser_age": 35,"score": "2-6 6-1 6-3","best_of": 3,"round": "F","minutes": 104,"w_ace": 2, "w_df": 1, "w_svpt": 77, "w_1stIn": 60, "w_1stWon": 44, "w_2ndWon": 7, "w_SvGms": 12, "w_bpSaved": 4, "w_bpFaced": 6,"l_ace": 4, "l_df": 1, "l_svpt": 75, "l_1stIn": 48, "l_1stWon": 28, "l_2ndWon": 14, "l_SvGms": 12, "l_bpSaved": 4, "l_bpFaced": 8,"winner_rank": 67,"winner_rank_points": 778,"loser_rank": 106,"loser_rank_points": 578,"match_id": "C032DjD6","tour": "atp","winner_elo": 2110,"loser_elo": 2259}
Output fields
His columns, with these conventions:
- Player ids (
winner_id,loser_id) andtourney_iduse Flashscore's ids (strings);match_idis Flashscore's match id. tourney_dateis the Monday of the main-draw week (YYYYMMDD, as in his files);tourney_level: G Grand Slam, M Masters 1000, F tour finals, D Davis Cup / Billie Jean King Cup, A other tour level (WTA 1000 included), C Challenger, S ITF.- Qualifying rounds are in the same rows (
roundQ1-Q3);draw_sizeis counted from the main-draw matches played. - Serve columns: ATP and WTA matches from 2012; earlier seasons have results, ranks, seeds, hand, height and age.
- Extra columns:
match_id,tour,winner_elo,loser_elo, thenscrapedAt,source,store.
Pricing
Pay per result: platform usage is included.
| Event | No discount (Free plan) | Bronze (Starter) | Silver (Scale) | Gold (Business) |
|---|---|---|---|---|
| Match (per 1,000) | $1.00 | $0.90 | $0.80 | $0.70 |
| Actor start (per run) | $0.00005 | $0.00005 | $0.00005 | $0.00005 |
| Example on the Free plan | Matches | Cost |
|---|---|---|
| Default run: 1,000 matches of the 2025 ATP season | 1,000 | ~$1.00 |
| The full 2025 ATP season with qualifying | 4,285 | ~$4.30 |
| The 1990 ATP season | 3,580 | ~$3.58 |
| Last 7 days, ATP and WTA (week to 2026-10-02) | 193 | ~$0.19 |
Set a maximum cost per run in the run options to stop a large run at your budget.
Reliability
- Rows come from our tennis database (more than 900,000 matches, updated every hour, ranks and player profiles from the official ATP and WTA sources): a season comes back in seconds, nothing is scraped during your run.
- Each run writes a summary to the key-value store (
OUTPUT) with the rows saved and any warnings.
Run it through the API
JavaScript:
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: 'YOUR_TOKEN' });const run = await client.actor('crawlplant/tennis-match-dataset').call({ years: [2025], tours: ['atp'], maxItems: 5000 });// the dataset as CSV, columns in Sackmann's orderconst csv = await client.dataset(run.defaultDatasetId).downloadItems('csv');
Python:
import pandas as pdfrom apify_client import ApifyClientclient = ApifyClient("YOUR_TOKEN")run = client.actor("crawlplant/tennis-match-dataset").call(run_input={"years": [2025], "tours": ["wta"], "maxItems": 5000})df = pd.DataFrame(client.dataset(run["defaultDatasetId"]).list_items().items)
Use with AI agents
The Actor works as a tool in the Apify MCP server for Claude, ChatGPT, Cursor and others:
https://mcp.apify.com?tools=crawlplant/tennis-match-dataset
Try "which players won the most tiebreaks on clay this season?".
More tennis data
- Flashscore Tennis Scraper: odds from about 20 bookmakers, head-to-head, form, live scores, rankings, player statistics and Elo lists.
- Tennis Match History: every match of one player's career.
- Tennis Point-by-Point Data: every game and point of any match.
FAQ
Is this Jeff Sackmann's data?
No. It's an independent dataset in the same column layout, built from Flashscore results and the official ATP and WTA ranking lists, draws and player profiles. His repositories are a great resource; this Actor gives you the same shape, current to the hour.
How far back does it go?
ATP and WTA from 1990, Challenger from 2008, ITF from 2011. Serve statistics from 2012.
Is it legal?
The data is public sporting results and official rankings, read without logging in. How you use and publish them is your responsibility, so check the sources' terms for your use case.
Privacy
The data is about professional players' public sporting results and profiles (names, countries, ages, hand, height). Each run sends the developer anonymous feature-usage statistics (the options used); your Apify account id is replaced by a one-way hash.