Skip to content

Tennis Scraper

Try for free

Collect public tennis matches, tournaments, point-by-point scores, and player statistics from SofaScore and Tennis Abstract.

🎾 Tennis Scraper

Get live tennis matches, daily tournament lists, season and match tables, point-by-point scoring, and player profiles as clean structured data. It reads public SofaScore feeds and public Tennis Abstract profiles, with no account and no API key.

✨ Why use Tennis Scraper?

  • 🔴 Live matches — set scores, current game points, and featured match odds when the feed has them, with filters for singles or doubles, gender, and tournament level.
  • 📅 Tournaments by date — every tournament scheduled on a UTC date, with tour, surface, ranking points, and match count.
  • 🏟️ Seasons and matches — open a tournament's seasons, then its matches.
  • 📊 Point by point — full point-by-point scoring and period statistics for one match id.
  • 👤 Player profiles — recent results, season tables, splits, and charting tables from the public Tennis Abstract profile.
  • 💸 Very low row fee — $0.10 per 1,000 saved rows, plus platform usage.

🎯 Use cases

  • 📡 Live scoreboards — snapshot the matches currently on the public live feed.
  • 🎰 Odds and model inputs — feed live scores and odds into your own models or dashboards.
  • 🗓️ Schedule tracking — list the tournaments on a date, then drill into one event.
  • 🔬 Match analysis — pull point-by-point data and period statistics for a single match.
  • 🧑‍🎓 Player research — compare form, surfaces, and splits using the profile tables.

🚀 How to use

  1. Pick a Mode: live matches, tournaments, tournament seasons, tournament details, point-by-point, or player details.
  2. For tournaments, optionally set a Date (empty means today in UTC). For live matches, optionally filter by match type, gender, and level.
  3. For the other modes, paste an id copied from an earlier row: tournament, season, match, or player id.
  4. Click Start.
  5. Open the dataset and export it as JSON, CSV, or Excel.

📥 Input

FieldTypeWhat it does
modestringliveMatches, tournaments, tournamentSeasons, tournamentDetails, pointByPoint, or playerDetails.
maxResultsintegerRows to save for lists. Default 10. 0 saves everything listed.
datestringYYYY-MM-DD for tournaments. Empty means today in UTC.
matchType, gender, tournamentLevelarrayFilters for live matches. Empty includes everything.
tournamentIdintegerFor seasons or tournament matches. Wimbledon men's singles is 2361.
seasonIdintegerOptional season for tournament matches. Empty uses the newest.
matchIdintegerFor point-by-point. Copy it from a live or tournament match row.
playerIdintegerFor player details. Jannik Sinner is 206570.
proxyConfigurationobjectResidential proxy by default (the match feed blocks datacenter addresses).

Tournaments on a date:

{ "mode": "tournaments", "maxResults": 10 }

Live matches:

{
  "mode": "liveMatches",
  "maxResults": 10,
  "matchType": ["singles"],
  "gender": ["male"],
  "tournamentLevel": ["atp1000", "challenger"]
}

Other modes take ids from earlier rows:

{ "mode": "tournamentDetails", "tournamentId": 18508, "seasonId": 96709, "maxResults": 10 }
{ "mode": "pointByPoint", "matchId": 17304354 }
{ "mode": "playerDetails", "playerId": 206570 }

📤 Output

Each mode saves its own kind of row.

ModeRow
tournamentsOne row per tournament on that UTC date: name, tour, surface, ranking points, event count.
liveMatches, tournamentDetailsOne row per match: players, round, status, set score, current points, odds when available.
tournamentSeasonsOne row per season.
pointByPointOne row with pointByPoint and statistics.
playerDetailsOne row with profile, recent results, season tables, splits, and charting tables.

Matches that have not started have firstToServe, winnerSide, and score set to null.

Real live match item (trimmed):

{
  "id": 17304354,
  "url": "https://www.sofascore.com/tennis/match/carlos-maria-zarate-gonzalo-villanueva/Cr",
  "startTime": "2026-10-14T00:25:00Z",
  "matchType": "singles",
  "roundSlug": "round-of-32",
  "surface": "Red clay",
  "status": "2nd set",
  "tournamentId": 18508,
  "tournamentName": "Santa Cruz 2, Bolivia",
  "firstToServe": "away",
  "homePlayerId": 85877,
  "homePlayerName": "Gonzalo Villanueva",
  "awayPlayerId": 511702,
  "awayPlayerName": "Carlos Maria Zarate",
  "score": {
    "home": 0,
    "away": 1,
    "sets": [
      { "set": 1, "home": 3, "away": 6, "durationSeconds": 2396 },
      { "set": 2, "home": 6, "away": 5 }
    ],
    "homePoint": "30",
    "awayPoint": "30"
  },
  "odds": {
    "home": { "fractional": "8/13", "decimal": 1.62 },
    "away": { "fractional": "6/5", "decimal": 2.2 }
  }
}

Real tournament item:

{
  "date": "2026-10-14",
  "id": 2519,
  "name": "Shanghai",
  "url": "https://www.sofascore.com/tennis/tournament/atp/shanghai/2519",
  "categoryName": "ATP",
  "surface": "Hardcourt outdoor",
  "tennisPoints": 1000,
  "eventCount": 4
}

💰 Pricing

$0.10 per 1,000 rows, which is $0.0001 per saved row.

Worked example: 1,000 rows cost $0.10 in row fees. Platform usage is billed separately at standard rates, and residential proxy traffic ($2 per GB) is used by default. A 10-match live run at the default 256 MB costs about $0.002 in total, most of it row fees, compute and log writes.

❓ FAQ

Is it legal? The actor reads public feeds and pages only. You are responsible for using the data in line with the sources' terms and local law.

Do I need an account or my own proxy? No account. Residential proxy is selected by default because the match feed blocks datacenter addresses.

Why does a live run sometimes save nothing? The live feed can be empty between sessions. The run still succeeds. Use tournaments for a date when you want a list that does not depend on matches being in progress.

What happens on a bad id or date? The run fails with a clear status message and saves no rows, so nothing is charged. The reason is also stored in the OUTPUT key-value record (failedInputs). A date like February 31, a missing tournament id, or an unknown player id is rejected.

Where do player statistics come from? The player is matched to the public Tennis Abstract profile by name. Recent results, seasons, splits, and charting tables are parsed from that page.

Can I schedule it or use the API? Yes. Use schedules in the console, or start runs and read the dataset through the API.

⚠️ Limits

  • Public data only. The real ceiling for list rows is what the source lists (a busy day has roughly 50–150 live matches or tournaments).
  • Datacenter proxy does not work for the live feed, so runs use residential traffic.
  • A player run saves the full profile as one row and looks up at most 40 opponent names; opponent ids are left out when a name search is ambiguous.
  • Tournament discovery counts matches whose start time falls on the requested UTC date.
  • Tours without a level tag in the live feed are reported as others.
  • Short runs have a fixed start-up time. We measured about 10 seconds for 10 live matches and about 30 seconds for 10 tournaments.

Related actors

G2
4.7/5
Excellent
Trustpilot

The internet is full of data.
Start owning yours.

Scrape.ly gives you the infrastructure, the tooling, and the team to extract, process, and operationalize web data at any scale. Setup takes minutes. The advantage lasts forever.

No credit card. No setup calls.