Tennis Stats Scraper - ATP & WTA Scores, Rankings
Pricing
Pay per event
Tennis Stats Scraper - ATP & WTA Scores, Rankings
ATP and WTA tennis match results with set and tiebreak scores, seeds, rankings and player profiles for any date range.
What does Tennis Stats Scraper do?
Tennis Stats Scraper returns ATP and WTA tennis matches for any date range as clean, flat rows: tournament, round, players with country and seed, status, winner, the games of every set and the tiebreak points of every tiebreak. The same run can add the current ATP and WTA singles rankings with points and movement, and player profiles with height, playing hand and season record, titles and prize money. It reads the public ESPN tennis JSON API, needs no proxy and no browser, and exports to JSON, CSV or Excel, the Apify API, n8n, Make or AI agents through MCP.
Who is it for?
- Sports bettors and modellers who need results with set and tiebreak scores, retirements and walkovers for back-testing, or the published order of play for the next days.
- Data scientists building win-probability or rating models (Elo, Glicko) from several seasons of tour-level results in one run.
- Journalists and fantasy players who want a player's recent form, a head-to-head record or this week's results across all tournaments.
- Apps and dashboards that show a daily results feed; the only-new mode returns just what changed since the previous run.
Example output
One match row (fields for sets 4 and 5 are null here and shortened):
{"recordType": "match","id": "186257","tour": "ATP","matchType": "singles","tournamentName": "China Open","tournamentId": "959","season": 2026,"grandSlam": false,"round": "Qualifying 1st Round","startTime": "2026-09-28T03:00:00.000Z","status": "finished","player1Name": "Yannick Hanfmann","player1Country": "Germany","player1CountryCode": "GER","player1Seed": null,"player2Name": "Tomas Machac","player2CountryCode": "CZE","winner": 2,"winnerName": "Tomas Machac","score": "7-6(9-7) 6-7(6-8) 6-4","player1SetsWon": 1,"player2SetsWon": 2,"set1Player1": 6, "set1Player2": 7, "set1TiebreakPlayer1": 7, "set1TiebreakPlayer2": 9,"set2Player1": 7, "set2Player2": 6, "set2TiebreakPlayer1": 8, "set2TiebreakPlayer2": 6,"set3Player1": 4, "set3Player2": 6, "set3TiebreakPlayer1": null, "set3TiebreakPlayer2": null,"tiebreaks": 2,"retired": false,"walkover": false,"scoreConsistent": true,"resultNote": "Tomas Machac (CZE) bt Yannick Hanfmann (GER) 7-6 (9-7) 6-7 (6-8) 6-4","sourceUrl": "https://www.espn.com/tennis/scoreboard/tournament/_/eventId/959-2026/competitionType/1"}
| Data type | One row is | Key fields |
|---|---|---|
| matches | a singles or doubles match | tournament, round, start time, status, players, countries, seeds, winner, score, games and tiebreak points per set, retirement, walkover |
| rankings | a ranked player | tour, rank, previous rank, change, points, country and country code, age, ranking date |
| players | a player profile | country, birthplace, date of birth, height, weight, plays, pro debut, rank, season wins, losses, titles and prize money |
How much does it cost?
You pay per row returned (match, ranking or player). Pricing depends on your Apify plan: a small fee when a run starts, then a price per result that is lower on paid plans. The Apify free plan includes monthly credit you can use to try it. Status rows (for example when nothing matched) are never charged, and matches outside your date range or filters are never returned or charged. You can set a maximum spend on the run and the Actor stops when it is reached. It reads a JSON API over plain HTTP, so runs are light on platform resources.
Input
Keep Data types on matches or add rankings and players, pick a period, narrow it down if you need to, and run. For a daily feed, schedule it with Only rows new since my last run.
- Data types –
matches,rankingsand/orplayers. - Date from, Date to – match dates in UTC, YYYY-MM-DD, for example a whole season. Future dates return the order of play that is already published. Leave Date from empty to use Last days.
- Last days – length of the window that ends on Date to (today by default) when Date from is empty; 3 by default.
- Tours – ATP, WTA and/or Mixed; empty for all.
- Players – names or ESPN player IDs. A name only has to be contained in the player's name; accents and case are ignored.
- Head-to-head only – with two or more Players, keep only matches between two of them.
- Tournaments – part of the tournament name or the ESPN tournament ID.
- Match types – singles and/or doubles. Match statuses – scheduled, in_progress, finished, retired, walkover, postponed, cancelled, suspended.
- Include qualifying – switch off for main draws only. Maximum rank – rankings only, for example 10.
- Oldest first – matches come newest first by default, so Maximum results never cuts off today's results; switch this on for date order.
- Only rows new since my last run – see the FAQ. Maximum results – total limit for the run.
Input values that are not recognised are listed in the run status; if a list contains no valid value at all, the run fails with a message.
What data do you get?
Matches
Every match of the ATP and WTA tournaments ESPN covers, including the Grand Slams with mixed doubles, qualifying rounds and doubles. Doubles rows name both partners of each pair. The tour of a match follows its draw: the women's matches of a combined event such as the China Open are WTA, mixed doubles are Mixed. Junior draws and legends (over-35 and over-45) events are skipped and counted in the run status. A match that ESPN lists in both the ATP and the WTA feed is returned once; if the two feeds differ at that moment, the more advanced state (finished over in progress over scheduled) is kept and the run status says so.
Scores and data quality
Scores are given from the winner's point of view with tiebreak points in brackets and a deciding match tiebreak (doubles and mixed doubles) in square brackets such as [10-4]; ret. marks a retirement and w/o a walkover. ESPN writes match tiebreaks in two ways, either as a 1-0 set with tiebreak points or as a plain set such as 10-4; both come out as [10-4], with the points in the tiebreak fields of that set and 1-0 as its games. A plain 10-8 last set at a Grand Slam before 2023 stays as games, because it can be an advantage set. For finished matches, scoreConsistent is false when the published set scores do not add up to the recorded winner or ESPN's result note still says "leads" or "is tied with"; this happens for a few older matches that were never completed at the source. The run status counts how many completed matches carry set scores and how many players have a known country, and the run fails instead of returning empty scores if the source stops publishing them.
Rankings and players
Rankings are the current singles lists from ESPN's ranking endpoint, which holds the top 150 of the ATP and of the WTA (asking for more returns the same 150). The run status gives the share of ranked players with points and with a country code. Player profiles come from ESPN's player records with the season statistics of the player's tour.
Is it legal to scrape this data?
The Actor reads the public JSON endpoints that serve ESPN's own tennis scoreboard, ranking and player pages. It does not log in, use cookies or bypass any access control. Match results, rankings and player facts such as height or date of birth are published facts about professional athletes. How you use the data, for example in a commercial product, is your responsibility. This description is not legal advice.
FAQ
Which tournaments are covered? The ATP and WTA tour events that ESPN lists, from the Grand Slams down to the tour's smaller events, including qualifying and doubles. Challenger and ITF events are not in these feeds. Match data is available from about 2010; for 2005 ESPN lists the tournaments but no matches, and the run status reports tournaments without match data. Tournament names are the names ESPN uses today, also for older editions.
Is court surface, match duration or point-by-point data included? No. The source publishes scores, sets, tiebreaks, seeds and statuses, not surface, serve statistics, odds or point-by-point data.
How long does a run take? ESPN returns only the tournaments that start or end inside the requested dates, so the Actor starts its first request 23 days before Date from; that way a tournament already under way, such as the second week of a Grand Slam, is always included, and matches outside your dates are dropped. It asks for one calendar month per tour in a single request and waits about 0.7 seconds between requests. A daily feed of the last three days is two requests; January 2026 for both tours (1,298 matches, including the Australian Open) is two requests; a full season is about 24.
How does the only-new mode work? The Actor remembers, in a storage on your account, the rows it actually returned to you, separately for each combination of filters. Dates are not part of that combination, so a scheduled run with Last days keeps one feed. A match returned while scheduled or in progress is returned again once it is finished, and a ranking row again when a new ranking is released. Rows dropped by your filters or cut off by Maximum results are not remembered.
How often should I schedule it? Every few hours during tournaments for a results feed, daily for a history archive. A run that finds nothing new returns one free status row.
Which player IDs can I use? The ESPN IDs from the player1Id, player2Id or playerId fields, for example 3623 for Jannik Sinner. Names are looked up among the current top 150 of each tour and among the matches read in the same run.
What happens when the source changes? If the scoreboard changes shape, or set scores, winners, ranking points or season statistics disappear, the run fails with a message instead of returning rows with empty values.
Something looks wrong. Open an issue with the input you used; changes at the source are fixed quickly.
Related Actors
Other public-data Actors from the same publisher are listed on the Store profile.