Tennis Match History - Career Results & Stats
Pricing
from $0.70 / 1,000 matches
Tennis Match History - Career Results & Stats
Every match of a tennis player's career, newest first: ATP, WTA, Challenger and ITF results back to 1990 with score, round, surface, both players' Elo before the match and serve / return statistics. Type a name, get the whole career. Works as an MCP tool for AI agents.
Pricing
from $0.70 / 1,000 matches
Rating
0.0
(0)
Developer
CrawlPlant
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
2
Monthly active users
16 days ago
Last modified
Share
Independent tool, not affiliated with, endorsed by or connected to Flashscore, Livesport, the ATP, the WTA or the ITF. It reads the public match data that flashscore.com shows to every visitor.
Type a player's name, get every match of their career. ATP, WTA, Challenger and ITF singles and doubles back to 1990, newest first, one row per match: date, tournament, round, surface, opponent, set-by-set score, winner, duration, both players' Elo rating before the match with the Elo win probability, and serve and return statistics (aces, double faults, 1st serve %, points won on 1st and 2nd serve, break points saved and converted).
Why this one
- The whole career, not the last 40 results. Jannik Sinner: 544 singles matches from his first ITF event in 2018 (476 ATP, 42 Challenger, 26 ITF). Novak Djokovic: 1,444. One run, a few seconds.
- Names work. "Jannik Sinner", "Swiatek" or "Alcaraz" finds the player; paste a Flashscore player URL to pick one exactly.
- Statistics included. Serve and return statistics for ATP and WTA matches from 2012 (458 of Sinner's 544 matches), at no extra cost.
- Elo before every match. Overall and per surface, computed from every match since 1990, with the win probability it gave: test a model on years of history without leaking the result into the rating.
- Head-to-head in one filter. Add the opponent's name and get every meeting: Sinner v Alcaraz returns their 18 tour meetings (exhibitions are left out unless you ask for them).
- Updated every hour from a database of more than 900,000 matches (ATP and WTA from 1990, Challenger from 2008, ITF from 2011).
- See it live. A career head-to-head with every meeting and the Elo leaders of both tours, from the same database your runs read: crawlplant.com/tennis-match-history.
What can you use it for?
- Betting and prediction models: years of results per player with pre-match Elo and serve statistics.
- Scouting and journalism: a player's record on a surface, at the Grand Slams or against one rival.
- Fantasy and apps: career data for player pages without maintaining your own scraper.
- AI agents: ask for any player's history as JSON.
Quick start
- Click Try for free with the default input (Jannik Sinner's 200 latest matches).
- Replace Players with the names you want, or raise Max results for whole careers.
- Download the table as CSV, Excel or JSON, or save the input as a task and schedule it to keep a dataset current.
Copy to your AI assistant
Paste this into ChatGPT, Claude or any agent so it knows how to use the Actor:
crawlplant/tennis-match-history on Apify: every match of a tennis player's career (ATP, WTA, Challenger, ITF, back to1990), newest first. Input: playerUrls (names like "Jannik Sinner" or Flashscore player URLs), players (opponent namecontains: head-to-head), surfaces (hard/clay/grass/carpet), tournaments (name contains, e.g. "Wimbledon"), tours,matchType (singles/doubles/all), status (finished/scheduled/live/all), includeStatistics, maxItems (default 200).Row: date, tournament, round, surface, player1Name, player2Name, score, sets, winnerName, durationMinutes, player1Elo,player2Elo (pre-match), player1EloWinProbability, statistics (aces, serve %, break points...), url.Price: $1.00 per 1,000 matches on the Free plan.
Ready-to-use examples
1. A player's whole career
{ "playerUrls": ["Jannik Sinner"], "maxItems": 2000 }
2. Every meeting of two rivals
{ "playerUrls": ["Jannik Sinner"], "players": ["Alcaraz"] }
3. Clay-court matches of several players
{ "playerUrls": ["Carlos Alcaraz", "Iga Swiatek"], "surfaces": ["clay"], "maxItems": 1000 }
4. Grand Slam matches only
{ "playerUrls": ["Novak Djokovic"], "tournaments": ["Australian Open", "French Open", "Wimbledon", "US Open"], "maxItems": 1000 }
5. A player's next scheduled matches
{ "playerUrls": ["Coco Gauff"], "status": "scheduled" }
6. One exact player by URL, Challenger and ITF years only
{ "playerUrls": ["https://www.flashscore.com/player/sinner-jannik/6HdC3z4H/"], "tours": ["challenger-men", "itf-men"] }
7. Doubles career
{ "playerUrls": ["Rajeev Ram"], "matchType": "doubles", "maxItems": 1000 }
How to…
Download a tennis player's match history
Put the name in playerUrls and set maxItems above the career length (example 1): a long career has 1,000-1,500
singles matches. Rows come newest first.
Get the head-to-head of two players, match by match
One player in playerUrls, the other in players (example 2). Each row is one meeting with its score, round and both
players' Elo at the time.
Compare players on one surface or at the Grand Slams
surfaces keeps hard, clay, grass or carpet (example 3); tournaments keeps tournaments whose name contains any of the
words (example 4).
Input options
| Option | Default | Description |
|---|---|---|
playerUrls | Jannik Sinner | Player names or Flashscore player page URLs |
players | [] | Opponent name contains: keep only matches against these players |
surfaces | all | hard, clay, grass, carpet |
tournaments | [] | Tournament name contains, e.g. Wimbledon, Masters |
tours | all but exhibitions | atp, wta, challenger-men, challenger-women, itf-men, itf-women, teams, other |
matchType | singles | singles, doubles, all |
status | finished | finished, scheduled, live, all |
includeStatistics | true | Serve and return statistics (included in the price) |
maxItems | 200 | Maximum matches in total |
Example output
Sinner's 2026 Wimbledon final (shortened: 2 of the match statistics, no player objects):
{"recordType": "match","matchId": "n5DWLXD5","url": "https://www.flashscore.com/match/n5DWLXD5/","date": "2026-07-12","startTime": "2026-07-12T15:10:00.000Z","tour": "atp","tournament": "Wimbledon","round": "Final","surface": "grass","status": "finished","player1Name": "Sinner J.","player2Name": "Zverev A.","player1Elo": 2717,"player2Elo": 2483,"player1EloWinProbability": 0.7613,"winner": "player1","winnerName": "Sinner J.","score": "6-7(7) 7-6(2) 6-3 6-4","durationMinutes": 228,"elo": { "player1": 2717, "player2": 2483, "player1Surface": 2157, "player2Surface": 1988, "basis": "pre-match" },"statistics": {"match": {"aces": { "player1": 15, "player2": 17 },"winners": { "player1": 58, "player2": 49 }}},"source": "cache"}
Output fields
| Field | Description |
|---|---|
matchId, url | Flashscore's match id and page |
date, startTime, endTime | Day (UTC) and times (ISO) |
tour, category, tournament, tournamentStage, round | atp, wta, challenger-men...; "Qualification" or null for the main draw; "Final", "Semi-finals", "Q1" |
surface, indoor, tournamentCountry | hard / clay / grass / carpet, indoor court, host country |
player1, player2, player1Name, player2Name | Both sides with Flashscore ids, URLs and countries (both players of a doubles pair) |
score, sets, setsPlayer1, setsPlayer2 | "6-7(7) 7-6(2) 6-3 6-4" and each set with tiebreak points |
winner, winnerName, status | player1 / player2; finished, retired, walkover, scheduled... |
durationMinutes | Match length |
player1Elo, player2Elo, player1EloWinProbability, elo | Elo before the match, overall and on the surface |
statistics | Serve and return statistics for the match and each set (ATP and WTA from 2012) |
scrapedAt, source | When the run read it; cache = our tennis database, live = read from Flashscore in the run |
How is Elo calculated?
Every singles match since 1990 updates both players' ratings (a K-factor that shrinks with the number of matches played, plus a separate rating per surface). A row keeps the ratings both players had before the match, so a result never counts in its own numbers.
How much does tennis match history data cost?
Pay per result: platform usage is included. One price per match, statistics 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: Sinner's 200 latest matches | 200 | ~$0.20 |
| Sinner's whole career | 544 | ~$0.54 |
| Djokovic's whole career | 1,444 | ~$1.44 |
| Sinner v Alcaraz, every meeting | 18 | ~$0.02 |
Set a maximum cost per run in the run options to stop a large run at your budget.
Reliability
- Careers come from our tennis database, updated every hour; a run doesn't depend on scraping hundreds of pages.
- If the database can't be reached, the run falls back to the player's Flashscore page (about 40 recent results) and says so in the log.
- A name that matches several players takes the best-known one and names the others in the run's 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-history').call({ playerUrls: ['Jannik Sinner'], players: ['Alcaraz'] });const { items } = await client.dataset(run.defaultDatasetId).listItems();console.log(items.map((m) => `${m.date} ${m.tournament} ${m.round}: ${m.winnerName} ${m.score}`));
Python:
from apify_client import ApifyClientclient = ApifyClient("YOUR_TOKEN")run = client.actor("crawlplant/tennis-match-history").call(run_input={"playerUrls": ["Iga Swiatek"], "maxItems": 2000})for m in client.dataset(run["defaultDatasetId"]).iterate_items():print(m["date"], m["tournament"], m["winnerName"], m["score"])
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-history
Try "how has Sinner done against Alcaraz on clay?".
Use it when you need a player's matches from a name: a whole career, one surface or tournament, or every meeting with one rival.
Not for bookmaker odds, form or rankings next to each match (use the Flashscore Tennis Scraper).
More tennis data
- Flashscore Tennis Scraper: every match of any day with odds from about 20 bookmakers, head-to-head, form, rankings, player statistics and Elo lists.
- Tennis Point-by-Point Data: every game and point of matches back to 2012.
- ATP & WTA Tennis Match Dataset: whole seasons in Jeff Sackmann's CSV columns.
FAQ
How far back does it go?
ATP and WTA matches from 1990, Challenger from 2008, ITF from 2011. Serve and return statistics: ATP and WTA from 2012.
Does it include doubles?
Yes, matchType: "doubles" (example 7); both players of each pair are in the row.
Is it legal?
The data is public sporting results read without logging in, at a polite pace. How you use and publish them is your responsibility, so check the source's terms for your use case.
Questions, a missing field or a source you need? Open an issue on this Actor: we reply within a day. If it saved you time, a short review helps others find it.
Privacy
The data is about professional players' public sporting results. Each run sends the developer anonymous feature-usage statistics (the options used, never the players you asked for); your Apify account id is replaced by a one-way hash.