Tennis Match Stats Scraper - ATP & WTA
Pricing
from $16.15 / 1,000 match records
Tennis Match Stats Scraper - ATP & WTA
Scrape ATP and WTA tennis data from Sofascore by player name or match ID: results with set scores, serve and return statistics, point-by-point sequences, head-to-head history, betting odds and live rankings. Export CSV, Excel, JSON. No login or API key. Six record types per run.
Pricing
from $16.15 / 1,000 match records
Rating
0.0
(0)
Developer
RecordsData
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
0
Monthly active users
19 hours ago
Last modified
Categories
Share
๐พ Tennis Match Stats Scraper: ATP & WTA Results, Stats, Odds & Rankings
Tennis Match Stats Scraper collects ATP and WTA match data from Sofascore by player name or match ID: set scores, optional serve statistics, point-by-point, head-to-head, odds and rankings. Export CSV, Excel or JSON, no login. Verified 2026-10-04: 10 matches for $0.19 with default settings (statistics are opt-in, billed per line).
Tennis Match Stats Scraper turns a player name such as "Novak Djokovic" into one row per recent or upcoming match, with up to six kinds of data attached. In our verified run on 2026-10-04, 10 matches came back in about 22 seconds, and one match with statistics switched on returned 20 serve and return statistic lines. It is built for analysts, bettors, journalists and developers who need structured tennis data without maintaining their own scraper.
๐พ What does Tennis Match Stats Scraper do?
- Scrape tennis match results for any ATP or WTA player by name
- Fetch specific matches directly by Sofascore match ID
- Collect every live tennis match at this moment with one switch
- Extract serve, return, points and games statistics for both players
- Extract the point-by-point sequence of every game in a match
- Get head-to-head wins between the two players, bookmaker odds and the current ATP or WTA ranking
๐ What data can you extract from Sofascore tennis?
| Field | Description |
|---|---|
recordType | match or ranking |
matchId, slug | Sofascore match ID and URL slug |
tournament, round, surface | Event, round and court surface |
player1, player2 and their IDs | The two players (home and away on Sofascore) |
setsWonPlayer1, setsWonPlayer2 | Sets won |
scoreBySet | Games per set with tiebreak points when a set went to a tiebreak |
status, statusType, startTime | Finished, scheduled or live, and start time in UTC |
statistics | Group, stat name and both players' values (aces, double faults, first serve %, break points and more) |
pointByPoint | Points inside every game of every set |
h2hRecord | Wins for each player in their history |
odds | Bookmaker result market, as fractional odds per outcome |
ranking, points, bestRanking, tour | Current rank, points, career-best rank and ATP or WTA tour (ranking rows) |
scrapedAt | UTC timestamp of the scrape |
Fields you did not switch on (including statistics, which is off by default) carry the text Not Requested. Data a match does not have carries N/A, so you always know why a field has no data.
๐งพ Sample output
Real row from a cloud run on 2026-10-04 with the input {"playerNames": ["Novak Djokovic"], "maxItems": 1, "includeStatistics": true}. The statistics list is shortened here to 3 of its 20 lines. With the default settings the statistics, pointByPoint, h2hRecord and odds fields carry Not Requested instead.
{"imageUrl": "https://www.sofascore.com/api/v1/team/14882/image","recordType": "match","matchId": 14956421,"tournament": "Athens, Greece","round": "Final","surface": "Hardcourt indoor","player1": "Novak Djokovic","player2": "Lorenzo Musetti","setsWonPlayer1": 2,"setsWonPlayer2": 1,"scoreBySet": [{ "set": 1, "player1Games": 4, "player2Games": 6 },{ "set": 2, "player1Games": 6, "player2Games": 3 },{ "set": 3, "player1Games": 7, "player2Games": 5 }],"status": "Ended","statusType": "finished","startTime": "2025-11-08T15:00:00.000Z","statistics": [{ "group": "Service", "stat": "Aces", "player1": "6", "player2": "10" },{ "group": "Service", "stat": "Double faults", "player1": "3", "player2": "3" },{ "group": "Service", "stat": "First serve", "player1": "63/97 (65%)", "player2": "63/96 (66%)" }],"pointByPoint": "Not Requested","h2hRecord": "Not Requested","odds": "Not Requested","scrapedAt": "2026-10-04T07:27:47.026Z"}
Ranking row (switch on includeRanking), from an earlier verified run:
{ "recordType": "ranking", "playerName": "Novak Djokovic", "tour": "ATP", "ranking": 12, "bestRanking": 1, "points": 2980, "tournamentsPlayed": 18 }
๐ฐ How much does it cost to scrape tennis matches?
This Actor uses pay-per-event pricing. You pay only for records that were saved to your dataset. Error rows, "not found" rows and empty results are never billed, and a run that fails or finds nothing bills no result events. Prices below are the live Free-plan prices; higher Apify plans get discounts on match and point-by-point records.
| Event | Price | What counts as one event |
|---|---|---|
| Match record | $0.019 | One match row |
| Match statistics record | $0.01 | One statistic line (a match with full stats has 20 to 42 lines). Off by default |
| Point-by-point record | $0.018 | One set with its point sequence |
| Head-to-head record | $0.01 | One match with a head-to-head tally |
| Betting odds record | $0.01 | One odds outcome (a normal match has 2) |
| Ranking record | $0.01 | One unique player per run |
Real examples from our test runs on 2026-10-04: the default run (10 matches, statistics off) billed 10 match records, which is $0.19 on the Free plan. A run with one match and includeStatistics switched on billed 1 match record and 20 statistic lines, which is $0.219. Match statistics are off by default because they bill per statistic line, not per match: a tennis match has about 20 to 42 lines, so switching them on adds roughly $0.20 to $0.42 per match. Match results alone cost $0.019 each, so 1,000 match rows with the defaults cost $19. Switch statistics on only when you need serve and return numbers.
Use the Max total charge setting on the run to cap spending. The Actor stops cleanly at your limit and keeps the data saved so far.
๐ How to scrape tennis data in 3 steps
- Open the Actor and type a player name, for example
Novak Djokovic, or paste Sofascore match IDs. - Switch on the extras you want: statistics (billed per line), point-by-point, head-to-head, odds, ranking.
- Click Start, then download the dataset as CSV, Excel, JSON or XML.
โ๏ธ Input
| Field | Type | Default | Meaning |
|---|---|---|---|
playerNames | list | ["Novak Djokovic"] | Players to look up. Their recent and upcoming matches are collected |
eventIds | list | empty | Sofascore match IDs to fetch directly. When set, the default player is ignored |
liveOnly | boolean | false | Fetch every tennis match live right now |
maxItems | integer | none | Maximum matches. Free users get 10 |
includeStatistics | boolean | false | Serve and return statistics. Off by default: billed per statistic line (about 20 to 42 lines per match) |
includePointByPoint | boolean | false | Point sequence per set |
includeH2h | boolean | false | Head-to-head tally |
includeOdds | boolean | false | Bookmaker odds |
includeRanking | boolean | false | ATP or WTA ranking, once per unique player |
{"playerNames": ["Novak Djokovic"],"maxItems": 10,"includeStatistics": false,"includeH2h": true,"includeOdds": true,"includeRanking": true}
๐ค Output
Results are stored in the default dataset with an Overview table view (players, tournament, surface, sets, date) and a full view with every nested field. Match rows and ranking rows share one dataset and are told apart by recordType. If no match is found for your input the run ends successfully with a clear status message and bills nothing. If Sofascore blocks every request the run fails with an explicit message instead of finishing "successfully" with no data.
๐ Tennis Match Stats Scraper vs alternatives
Prices read from the public Apify Store on 2026-10-03.
| Actor | Match price | Includes |
|---|---|---|
| crawlstone/tennis-scraper | $0.008 per result | Results (1,035 users in 30 days) |
| tagadanar/tennis-live-scores | $0.004 per match, $0.01 per match with statistics | Live scores and match statistics |
| sian.agency/tennis-point-by-point-scraper | $0.075 per match plus $0.05 per run start | Point-by-point |
| This Actor | $0.019 per match | Results, statistics, point-by-point, head-to-head, odds and rankings in one run |
We are not the cheapest. A results-only scraper costs less per match, and tagadanar bills statistics once per match while we bill per statistic line (which is why statistics are opt-in here). What the extra cost buys here is six data types from one run, the full point sequence and rankings computed once per player, not once per match. Choose a cheaper actor if you only need scores.
๐ผ Use cases
- Betting and odds analysis: compare bookmaker odds with real serve and return statistics.
- Performance analysis: study break-point conversion and momentum from point-by-point data.
- Sports media: feed head-to-head history and current rankings into match previews.
- Prediction models: train on match, surface and statistic data across many players.
๐ Run via API, schedule and integrations
Call the Actor from any language with the Apify API:
curl -X POST "https://api.apify.com/v2/acts/RecordsData~tennis-match-stats-scraper/runs?token=YOUR_TOKEN" \-H "Content-Type: application/json" \-d '{"playerNames": ["Iga Swiatek"], "maxItems": 10, "includeRanking": true}'
Schedule runs during a tournament, send results to Google Sheets, Zapier, Make or n8n, or call the Actor from an AI agent through the Apify MCP server.
โ๏ธ Is it legal to scrape tennis data?
The Actor reads publicly available match data and does not log in, bypass a paywall or collect private personal data. Player names and results are public sporting facts. You are responsible for using the data in line with Sofascore's terms and the laws that apply to you. Odds are informational only and are not betting advice.
โ Frequently asked questions
How do I scrape ATP and WTA match results?
Enter a player name in playerNames and run. You get one row per recent or upcoming match for that player.
Do I need a login or API key? No. You only need an Apify account to run the Actor.
Why did I get 0 results? The name did not match a tennis player on Sofascore, or the player has no matches to list. Use the full name as Sofascore spells it. Nothing is billed for an empty run.
Why is my run expensive?
Most likely includeStatistics is on. It bills one event per statistic line, and a full match has 20 to 42 lines (about $0.20 to $0.42 per match on the Free plan). It is off by default; switch it off to pay only $0.019 per match, or set Max total charge to cap spending.
Is point-by-point available for every match?
Only where Sofascore recorded it. Otherwise the field shows N/A and no point-by-point event is billed.
Why is a ranking billed once per player? A ranking is one fact about a player, so it is billed once per unique player in the run, not once per match.
Can I get live matches?
Yes. Switch on liveOnly to fetch every tennis match in progress.
What do Not Requested and N/A mean?
Not Requested means you did not switch that extra on. N/A means the source has no data for that match.
๐ Want more sports data? Other PunkRecordsData scrapers
- Sofascore Match Stats Scraper for football scores, odds and lineups
- Sports Reference Stats Scraper for NBA and NHL statistics
๐ Support
Found a problem or need an extra field? Open the Issues tab on this Actor or write to contact.punkrecordsdata@gmail.com. This is an independent tool, not affiliated with Sofascore, the ATP or the WTA.
Last updated: 2026-10-04