Tennis Point-by-Point & Odds Timeline
Pricing
from $5.00 / 1,000 match point-by-points
Tennis Point-by-Point & Odds Timeline
Point-level tennis match data (every point, server, break/set/match points) plus timestamped bookmaker odds snapshots for ATP, WTA, Challenger and ITF matches. Data tool for analytics and research - not a results scraper.
Pricing
from $5.00 / 1,000 match point-by-points
Rating
0.0
(0)
Developer
hyeonsu kim
Maintained by CommunityActor stats
0
Bookmarked
3
Total users
2
Monthly active users
2 days ago
Last modified
Categories
Share
A Flashscore scraper for tennis point by point data and tennis odds history: every single point of a match, who served it, whether it was a break/set/match point — plus timestamped bookmaker odds snapshots that scheduled runs turn into an odds movement timeline. Covers ATP, WTA data, Challenger and ITF matches, delivered as clean JSON you can treat as a tennis analytics API: run it on demand or on a schedule and read the dataset from your code.
Most tennis scrapers stop at schedules, final scores and H2H tables. This actor collects what those don't:
- Point-by-point progression — the full set → game → point sequence: server of every game, the exact score path (
0:15, 0:30, 30:30, 40:30, ...), break/set/match points flagged per point, and tiebreaks expanded point-by-point with mini-break markers. - Odds snapshots — every listed bookmaker's pre-match odds (current + opening value) for moneyline, totals, handicap, correct-score and odd/even markets, stamped with the collection time.
This is a data tool for analytics, modeling, research and journalism. It reports publicly displayed sports records and publicly displayed odds numbers — it does not place bets, link to betting sites, or provide betting advice.
Sample output
One dataset record per match. This is a real (shortened) record — Comesana vs. Cobolli, US Open first round:
{"matchId": "xYnEsNM6","mode": "both","capturedAt": "2026-09-02T13:54:49.778Z","tournament": { "name": "US Open, hard", "category": "ATP - SINGLES", "tour": "atp", "country": "USA" },"round": "1/64-finals","status": "finished","startTimestamp": 1788275400,"home": { "name": "Francisco Comesana", "country": "Argentina", "ranking": { "tour": "ATP", "position": 119 } },"away": { "name": "Flavio Cobolli", "country": "Italy", "ranking": { "tour": "ATP", "position": 6 } },"pointByPoint": {"totalGames": 46, "totalPoints": 271, "breaks": 12,"sets": [{"set": 1,"games": [{"gameNumber": 1, "server": "home", "winner": "home","isBreak": false, "scoreAfter": { "home": 1, "away": 0 },"breakPointCount": 4,"points": [{ "home": "0", "away": "40", "breakPoint": true, "setPoint": false, "matchPoint": false },{ "home": "40", "away": "A", "breakPoint": true, "setPoint": false, "matchPoint": false }]}],"tiebreak": {"finalScore": { "home": 7, "away": 9 },"points": [{ "pointNumber": 11, "scoreAfter": { "home": 5, "away": 6 }, "server": "away","winner": "away", "isMiniBreak": false, "setPoint": true, "markerFor": "away" }]}}]},"pbpAvailable": true,"odds": {"capturedAt": "2026-09-02T13:54:51.892Z","bookmakerCount": 4, "marketCount": 34,"markets": [{"bookmaker": "DraftKings", "betType": "HOME_AWAY", "betScope": "FULL_TIME","outcomes": [{ "side": "home", "odds": 4.27, "openingOdds": 4.33, "active": true },{ "side": "away", "odds": 1.21, "openingOdds": 1.21, "active": true }]}]},"oddsAvailable": true}
(Arrays shortened here; real records contain every set, every game, every point, and all ~30+ markets across bookmakers: HOME_AWAY, OVER_UNDER, ASIAN_HANDICAP, CORRECT_SCORE, ODD_OR_EVEN, each also per FIRST_SET/SECOND_SET scope where offered.)
Field guide
| Field | Meaning |
|---|---|
matchId | Flashscore's 8-character match ID — the stable join key across repeated runs |
capturedAt | When this record was collected (ISO 8601) — the timeline axis for scheduled runs |
home / away | Player name, country, and current ATP/WTA ranking position |
pointByPoint.sets[].games[] | Per game: server, winner, break flag, score after, and the point-by-point array |
points[].home/away | The score after that point, from the server's perspective conventions of the scoreboard |
points[].breakPoint/setPoint/matchPoint | Pressure-point flags on the individual point |
sets[].tiebreak | Tiebreak expanded point-by-point with server, mini-break and set/match-point markers |
pbpAvailable | false when the source has no point feed for the match (section is null, not charged) |
odds.markets[] | One entry per bookmaker × bet type × scope, with current odds and openingOdds per outcome |
oddsAvailable | false when no bookmaker odds are listed (section is null, not charged) |
Modes
| Mode | What it collects | Typical use |
|---|---|---|
point_by_point | Full point sequences | Post-match analytics, serve/pressure modeling |
odds_snapshot | All bookmakers' current + opening odds, timestamped | Odds movement timelines via scheduled runs |
both (default) | Both sections in one record | One-stop match archive |
Targeting matches
- Explicit match IDs — pass Flashscore match IDs (the 8-character ID in match URLs) in
matchIds. Metadata (round, rankings, full names) is read from each match page. - Auto-discovery by day — leave
matchIdsempty and setdayOffset(0 = today, -1 = yesterday, range ±7). Filter by tour (atp,wta,challenger,itf,other), singles/doubles, and status (finished,live,scheduled,all).
Recipe: building an odds timeline
Odds pages expose only the current and opening value of each market — the path between them is not published anywhere. But if you run this actor on a schedule, each run stamps a fresh snapshot, and the snapshots line up into a proper time series per match. This is the actor's core recurring use case.
Step 1 — create the task input. Snapshot tomorrow's schedule (auto-discovery):
{"mode": "odds_snapshot","dayOffset": 1,"statusFilter": "scheduled","tours": ["atp", "wta"],"includeDoubles": false,"maxMatches": 50}
Or pin specific matches you want a dense timeline for (e.g. a final):
{"mode": "odds_snapshot","matchIds": ["xYnEsNM6", "AbCdEf12"]}
Step 2 — schedule it. In Apify Console: Schedules → Create new, attach this actor (or a saved Task with the input above), and set a cron like 0 */4 * * * (every 4 hours). Every 2–6 hours is a sensible cadence for pre-match markets; the dayOffset: 1 window means each match is captured repeatedly from roughly a day out until it starts.
Step 3 — join the snapshots. All runs can push to the same named dataset (set one on the Task), or you read runs' default datasets via the Apify API. Group records by matchId, sort by odds.capturedAt, and each market's odds values form the movement curve — with openingOdds as the anchor point at the start.
Each scheduled run charges only per match that actually returns an odds snapshot (see pricing), so an off-day with no matching matches costs almost nothing.
Pricing (pay-per-event)
You are charged only for data actually delivered:
| Event | Charged when |
|---|---|
match-pbp | Per match with point-by-point data returned |
match-odds | Per match with an odds snapshot returned |
actor-start | Once per run (very small flat fee) |
Matches without point-by-point coverage or without bookmaker odds are pushed with that section set to null and are not charged for that section. No data, no charge.
FAQ
Is this legal? The actor reads only publicly displayed pages — sports records (scores, point sequences, rankings) and publicly displayed odds numbers, the same data any visitor sees without logging in. Player names are public match participants; no personal data beyond that is collected. If the source ever answers with bot-protection responses, the run stops and reports instead of attempting any bypass.
Am I charged when a match has no data? No. match-pbp and match-odds are charged only when that section is actually delivered. A match with pbpAvailable: false or oddsAvailable: false costs nothing for the missing section, and a failed run charges nothing beyond the tiny actor-start fee.
Is this a betting tool? No. It is a data collection tool for research, modeling and journalism. It does not place bets, does not link to betting operators, and provides no betting advice. Odds are reported as published numbers, like any other public sports statistic.
Where do I get help? Open an issue on the actor's Issues tab in Apify Console — include the matchId and your input if it's about a specific match. Feature requests are welcome.
Limitations
- Point-by-point coverage depends on the source; lower-tier ITF and some doubles matches may have no PBP feed (delivered as
pbpAvailable: false, not charged). - Odds are pre-match markets; the bookmaker portfolio varies by the configured region (
geoIpCode, default US). - Historical odds before the actor's first snapshot of a match cannot be reconstructed — start the schedule before the matches you care about.
Input reference
See the Input tab. Key fields: mode, matchIds, dayOffset, tours, includeDoubles, statusFilter, maxMatches, plus politeness controls (requestDelayMs, default 600 ms with jitter; maxRetries) and odds region (geoIpCode, geoIpSubdivisionCode).
Not affiliated with Flashscore or any bookmaker.