Tennis Point-by-Point Data - Every Game & Point
Pricing
from $0.70 / 1,000 matches
Tennis Point-by-Point Data - Every Game & Point
Point-by-point data of ATP, WTA, Challenger and ITF matches: every game with its server, the points in order, breaks, and break, set and match points, plus the score, round and serve / return statistics. Any day back to 2012, any match, tournament or player. 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
5
Total users
1
Monthly active users
10 hours 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.
Every game and every point of a tennis match, for ATP, WTA, Challenger and ITF matches back to 2012: who served, the
score after each point (15:0, 30:0, 40:15...), who won the game, whether serve was broken, and how many break,
set and match points it had, tiebreaks point by point included. Ask for a day, a list of matches, a whole
tournament edition or a player's matches. Each row is one match with its score, round, duration, both players' Elo before
the match and serve / return statistics.
Why this one
- Any day, not just today. Matches from the last week are read live; anything older comes from our tennis database, which keeps point-by-point for more than 400,000 matches and is updated every hour.
- Whole tournaments in one run. Wimbledon 2024: 239 matches with qualifying, 237 of them point by point.
- Pressure points counted for you. Each game says how many break, set and match points it had, and whether serve was broken: no parsing of score strings.
- Tiebreaks point by point, in the same list as the games.
- Sackmann's layout on request.
pointByPointFormat: "sackmann"gives each finished match as one row in the columns of Jeff Sackmann'stennis_pointbypointfiles (pbp_id,server1,server2,pbp,score), each string checked point by point against the final score, so the tools built on his files read it as is. - Statistics in the same row: aces, double faults, 1st serve %, points won on 1st and 2nd serve, break points saved and converted, for the match and each set.
- Search the way you think: by day, by match URL, by tournament page, or by player name ("Alcaraz") with an opponent filter.
- See it live. How many matches the database has point by point and today's ATP and WTA singles, from the same database your runs read: crawlplant.com/tennis-point-by-point.
What can you use it for?
- Momentum and pressure models: break points saved, serve holds under pressure, comebacks within a set.
- In-play betting research: how often a player holds after being 0:30 down, or wins a tiebreak after a mini-break.
- Match visualisations and apps: game-by-game charts of any match.
- Coaching and analysis: a player's serve games in finals or against one rival.
Quick start
- Click Try for free with the default input (20 of yesterday's finished ATP and WTA matches).
- Pick a Mode: a day range, match URLs, tournament pages or players.
- Download as JSON (the
pointByPointlist keeps its structure) or open the table view.
Copy to your AI assistant
Paste this into ChatGPT, Claude or any agent so it knows how to use the Actor:
crawlplant/tennis-point-by-point on Apify: every game and point of tennis matches (ATP, WTA, Challenger, ITF, from 2012).Input: mode (matches = days dateFrom..dateTo like "yesterday", "2024-07-14", "-3"; matchIds = Flashscore match ids or URLs;tournaments = tournamentUrls + years; players = playerUrls names), tours (default atp, wta), matchType, status (defaultfinished), tournaments / players (name contains filters), includeStatistics, pointByPointFormat (games = the default;sackmann = finished matches only, one row per match in tennis_pointbypoint columns), maxItems (default 20).Row: one match (date, tournament, round, player1Name, player2Name, score, winnerName, durationMinutes, Elo, statistics)with pointByPoint: [{ set, game, server, winner, breakOfServe, points: ["15:0", ...], breakPoints, setPoints, matchPoints,gamesPlayer1, gamesPlayer2, tiebreak }]. Price: $1.00 per 1,000 matches + $2.00 per 1,000 with point-by-point (Free plan); in sackmann format $2.00 per 1,000 matches.
Ready-to-use examples
1. Yesterday's ATP and WTA matches
{ "mode": "matches", "dateFrom": "yesterday", "tours": ["atp", "wta"], "maxItems": 200 }
2. One match by its URL
{ "mode": "matchIds", "matchIds": ["https://www.flashscore.com/match/Sf0bExdB/"] }
3. A whole tournament edition
{ "mode": "tournaments", "tournamentUrls": ["https://www.flashscore.com/tennis/atp-singles/wimbledon/"], "years": [2024], "maxItems": 300 }
4. Every meeting of two rivals, point by point
{ "mode": "players", "playerUrls": ["Carlos Alcaraz"], "players": ["Djokovic"] }
5. A week of WTA matches
{ "mode": "matches", "dateFrom": "-7", "dateTo": "yesterday", "tours": ["wta"], "maxItems": 1000 }
6. Matches in play right now, points so far
{ "mode": "matches", "dateFrom": "today", "status": "live", "tours": [] }
7. A past Grand Slam fortnight
{ "mode": "matches", "dateFrom": "2025-05-25", "dateTo": "2025-06-08", "tournaments": ["French Open"], "maxItems": 1000 }
8. Wimbledon 2024 in Jeff Sackmann's tennis_pointbypoint layout, one row per match (220 rows, ~$0.44)
{ "mode": "tournaments", "tournamentUrls": ["https://www.flashscore.com/tennis/atp-singles/wimbledon/"], "years": [2024], "pointByPointFormat": "sackmann", "maxItems": 300 }
How to…
Download point-by-point data of a tennis match
Paste the match URL from flashscore.com (example 2): the 8 characters after /match/ also work.
Get point-by-point for a whole tournament
Use the tournament's Flashscore page and the year (example 3). Qualifying matches come with the main draw;
tournamentStage is Qualification for them.
Find break points saved, set points and match points
Each game has breakPoints, setPoints and matchPoints (how many the game had) and breakOfServe (the server lost
it). In the 2024 Wimbledon final Alcaraz broke Djokovic in the first game at his fifth break point (breakPoints: 5),
and the last game is the tiebreak with matchPoints: 1.
Get point-by-point in Jeff Sackmann's tennis_pointbypoint layout
Set pointByPointFormat to sackmann (example 8), in any mode. Each finished match is one row: pbp_id (the match id), date,
tny_name, tour, draw (Main or Qual), server1 and server2 (who served first and second), winner (1 = the first
server), pbp (the whole match as one string: S and R for a point won by the server or the returner, ; after a game,
. after a set, / at each change of server inside a tiebreak), score, adf_flag and wh_minutes. Matches without point-by-point
that replays to the final score are left out and not charged; live and scheduled matches have no row in this layout.
Input options
| Option | Default | Description |
|---|---|---|
mode | matches | matches (days), matchIds, tournaments, players |
dateFrom / dateTo | yesterday / same day | matches mode: today, yesterday, 2024-07-14, -3 |
matchIds | [] | matchIds mode: match ids or URLs |
tournamentUrls / years | [] / latest | tournaments mode: Flashscore tournament pages and editions |
playerUrls | [] | players mode: names or Flashscore player URLs |
tours | atp, wta | Also challenger-men, challenger-women, itf-men, itf-women, teams, other; empty = all |
matchType | singles | singles, doubles, all |
status | finished | finished, live, all |
tournaments, players | [] | Tournament / player name contains |
includeStatistics | true | Serve and return statistics (included in the match price) |
pointByPointFormat | games | games (on each match row) or sackmann (finished matches, one row per match in tennis_pointbypoint columns) |
maxItems | 20 | Maximum matches in total |
Example output
The 2024 Wimbledon final (shortened: the first game and the tiebreak of 29 games, no statistics or player objects):
{"recordType": "match","matchId": "Sf0bExdB","url": "https://www.flashscore.com/match/Sf0bExdB/","date": "2024-07-14","tour": "atp","tournament": "Wimbledon","round": "Final","surface": "grass","player1Name": "Alcaraz C.","player2Name": "Djokovic N.","player1Elo": 2538,"player2Elo": 2564,"winnerName": "Alcaraz C.","score": "6-2 6-2 7-6(4)","durationMinutes": 147,"pointByPoint": [{ "set": 1, "game": 1, "server": "player2", "winner": "player1", "breakOfServe": true, "gamesPlayer1": 1, "gamesPlayer2": 0,"points": ["15:0", "15:15", "30:15", "30:30", "30:40", "40:40", "A:40", "40:40", "40:A", "40:40", "A:40", "40:40", "40:A","40:40", "A:40", "40:40", "A:40", "40:40", "A:40"], "breakPoints": 5, "setPoints": 0, "matchPoints": 0 },{ "set": 3, "game": 13, "tiebreak": true, "server": "player2", "winner": "player1", "breakOfServe": false,"gamesPlayer1": 7, "gamesPlayer2": 6, "points": ["0:1", "1:1", "2:1", "3:1", "3:2", "3:3", "4:3", "5:3", "5:4", "6:4", "7:4"],"breakPoints": 0, "setPoints": 0, "matchPoints": 1 }],"source": "live"}
Output fields
| Field | Description |
|---|---|
matchId, url, date, startTime | Flashscore's match id and page, day (UTC) and start time |
tour, tournament, tournamentStage, round, surface | Where and when in the draw |
player1Name, player2Name, player1, player2 | Both sides with Flashscore ids, URLs and countries |
score, sets, winnerName, durationMinutes | Result and length |
player1Elo, player2Elo, player1EloWinProbability | Elo before the match |
statistics | Serve and return statistics for the match and each set |
pointByPoint[] | One entry per game: set, game, server, winner, breakOfServe, gamesPlayer1 / gamesPlayer2 after it, points (the game score after each point, player1:player2), breakPoints, setPoints, matchPoints; a tiebreak has tiebreak: true and the tiebreak score after each point |
pbp_id, date, tny_name, tour, draw, server1, server2, winner, pbp, score, adf_flag, wh_minutes | With pointByPointFormat: "sackmann": the columns of one tennis_pointbypoint row instead (above) |
scrapedAt, source | When the run read it; cache = our tennis database, live = read from Flashscore in the run |
How much does tennis point-by-point data cost?
Pay per result: platform usage is included. A match row, plus point-by-point when the match has it.
| Event | No discount (Free plan) | Bronze (Starter) | Silver (Scale) | Gold (Business) |
|---|---|---|---|---|
| Match (per 1,000) | $1.00 | $0.90 | $0.80 | $0.70 |
| Point-by-point, per match that has it (per 1,000) | $2.00 | $1.80 | $1.60 | $1.40 |
| Actor start (per run) | $0.00005 | $0.00005 | $0.00005 | $0.00005 |
In Sackmann's layout (example 8) a finished match is one row at the point-by-point price alone, with no match row.
| Example on the Free plan | Matches | Cost |
|---|---|---|
| Default run: 20 of yesterday's matches | 20 | ~$0.06 |
| One match | 1 | ~$0.003 |
| Wimbledon 2024, every match with qualifying | 239 | ~$0.71 |
| A week of WTA tour matches (2026-09-24 to 2026-10-01) | 88 | ~$0.26 |
| Roland Garros 2025, men's and women's singles | 254 | ~$0.76 |
| Wimbledon 2024 in Sackmann's layout (example 8) | 220 | ~$0.44 |
Set a maximum cost per run in the run options to stop a large run at your budget.
Reliability
- Matches older than a week come from our tennis database (updated every hour), so a big historical run doesn't depend on thousands of live requests.
- Each live request that fails is retried; a match whose point-by-point still can't be read keeps every other field and is not charged for point-by-point.
- The run stops starting new requests shortly before its time limit and saves what it has.
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-point-by-point').call({ mode: 'matchIds', matchIds: ['Sf0bExdB'] });const [match] = (await client.dataset(run.defaultDatasetId).listItems()).items;console.log(match.pointByPoint.filter((g) => g.breakOfServe).map((g) => `set ${g.set} game ${g.game}`));
Python:
from apify_client import ApifyClientclient = ApifyClient("YOUR_TOKEN")run = client.actor("crawlplant/tennis-point-by-point").call(run_input={"dateFrom": "yesterday", "maxItems": 100})for m in client.dataset(run["defaultDatasetId"]).iterate_items():breaks = sum(g["breakOfServe"] for g in m.get("pointByPoint", []))print(m["player1Name"], m["player2Name"], m["score"], breaks, "breaks")
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-point-by-point
Try "how many break points did Alcaraz save in the 2024 Wimbledon final?".
Use it when you need matches point by point from 2012 (one match, a day, a tournament or a player), with break, set and match points counted.
Not for a career list without the points (use Tennis Match History).
More tennis data
- Flashscore Tennis Scraper: odds from about 20 bookmakers, head-to-head, form, rankings, player statistics and Elo lists.
- Tennis Match History: every match of a player's career.
- ATP & WTA Tennis Match Dataset: whole seasons in Jeff Sackmann's CSV columns.
FAQ
How far back does point-by-point go?
To 2012 for ATP and WTA matches; Flashscore has none before that. Recent seasons cover almost every tour-level match.
Is it live during a match?
Yes: status: "live" (example 6) returns the games and points played so far.
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 matches you asked for); your Apify account id is replaced by a one-way hash.