# Tennis Scraper (`automly/tennis-data-scraper`) Actor

Tennis Scraper: Scrape tennis matches on every tour (ATP, WTA, Challenger, ITF, UTR): live scores, results with set-by-set scores, fixtures, match statistics, head-to-head, player profiles and ATP/WTA rankings.

- **URL**: https://apify.com/automly/tennis-data-scraper.md
- **Developed by:** [Automly](https://apify.com/automly) (community)
- **Categories:** Sports
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.70 / 1,000 matches

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Tennis Scraper - Live Scores, Results, Stats & Rankings

### What is Tennis Scraper?

**Tennis Scraper** is a tool that lets you **scrape tennis matches, results, live scores, match statistics, player profiles and rankings** for every tour: ATP, WTA, ATP Challenger, WTA 125, ITF Men and Women, UTR Pro Tennis Tour, Davis Cup, Billie Jean King Cup, United Cup and exhibitions. Get **set-by-set scores, tiebreaks, retirements and walkovers**, **aces, double faults, first serve %, break points, winners and unforced errors**, **head-to-head records**, and **ATP, WTA and UTR rankings**. Pick a date, a tournament or a player, click **Start**, and download the data as Excel, CSV or JSON.

- ⚡ **Fast:** a full week of matches on every tour (over 3,000 matches) in about a minute; 900 matches with full statistics in about a minute (our tests)
- 🎾 **Every tour:** from Grand Slams to ITF and UTR events, singles and doubles
- 📊 **Deep match statistics:** serve, return, break point and tiebreak numbers for the whole match and for each set
- 🔓 **No login, no API key:** public scores and statistics only
- 💸 **Free to try:** the $5 of free usage every Apify account gets each month covers about 5,000 matches

### What can Tennis Scraper do?

- **Scrape tennis results by date** or for a date range of up to a year, on every tour at once or only the tours you choose
- **Get live tennis scores** with the current set and game score
- **Scrape every match of a tournament**: Wimbledon, Roland Garros, US Open, Australian Open, Masters 1000, ATP 250/500, WTA 1000, Challengers and ITF events, including qualifying, for this year or earlier editions (Grand Slams back to 2009)
- **Get a tennis player's match history and upcoming fixtures**, for example the last 100 matches of Jannik Sinner
- **Get the tennis head-to-head of two players**: every match Sinner and Alcaraz have played against each other
- **Scrape tennis match statistics**: aces, double faults, first and second serve %, points won on serve and return, break points saved and converted, service games, tiebreaks, winners and unforced errors where tracked
- **Scrape tennis player profiles**: age, country, height, playing hand, turned pro, ranking, prize money, this season's record and titles, statistics per surface
- **Download ATP and WTA rankings**, live rankings and UTR ratings (top 500 each)
- Export to Excel, CSV, JSON, HTML or XML, or send the data to Google Sheets, Make, Zapier and more

### What tennis data can you extract?

| | | |
|---|---|---|
| 🏆 Tournament, round, surface, level | 🎾 Players, countries, seeds, ranks | 📅 Start time (UTC) |
| 🔢 Set-by-set score and tiebreaks | ✅ Winner, retirements, walkovers | 🔴 Live set and game score |
| 🚀 Aces and double faults | 🎯 First and second serve % | 💪 Points won on serve and return |
| 🛡️ Break points saved and converted | ⏱️ Match duration and court | 🤝 Head-to-head record |
| 🧑 Age, height, playing hand, turned pro | 📈 ATP/WTA rank, points, best rank | 💰 Prize money, season and career |
| 🏅 Titles and record per season | 🌍 Statistics per surface | 📋 ATP, WTA, live and UTR rankings |

### How to scrape tennis data

1. [Create a free Apify account](https://console.apify.com/sign-up).
2. Open **Tennis Scraper**.
3. Choose what to scrape: **Matches and results**, **Player profiles** or **Rankings**.
4. For matches, pick dates, or add tournaments, players or head-to-heads, and turn on **Include match statistics** if you need serve and return numbers.
5. Click **Start**, then download the data as Excel, CSV, JSON or HTML.

### How much does it cost to scrape tennis data?

You pay only for the rows you get: per match, per match with statistics (turn on **Include match statistics**), per player profile and per ranking row.

| Apify plan | Matches per 1,000 | Matches with statistics per 1,000 | Player profiles per 1,000 | Ranking rows per 1,000 |
|---|---|---|---|---|
| Free | $1.00 | $2.50 | $2.00 | $0.50 |
| Starter (Bronze) | $0.90 | $2.25 | $1.80 | $0.45 |
| Scale (Silver) | $0.80 | $2.00 | $1.60 | $0.40 |
| Business (Gold) and higher | $0.70 | $1.75 | $1.40 | $0.35 |

Each run also has a start fee of $0.005 per run at the default settings. Platform usage is included in these prices, so there is nothing else to pay. For example, 1,000 matches cost $1.00 on the Free plan and $0.70 on Business, and the ATP and WTA top 500 (1,000 ranking rows) cost $0.50 on the Free plan.

Every Apify account gets **$5 of free usage each month**, so you can try it at no cost. **Max results** caps the rows (and the cost) of a run, and you can also set a maximum cost per run. See the **Pricing** tab for details.

### ⬇️ Input

| Setting | What it does |
|---|---|
| **What to scrape** | Matches and results, Player profiles, or Rankings: one kind of data per run |
| **From date** and **To date** | The days to get every match of (UTC). Leave both empty for today |
| **Live matches** | The matches being played right now |
| **Tournaments** | Tournament names or IDs, such as `Wimbledon`, `Wimbledon Women`, `US Open Men Doubles`, `Beijing` |
| **Season (year)** | The tournament edition to get; empty for the latest |
| **Players** | Player names or IDs, such as `Jannik Sinner` or `Swiatek`: their matches, or their profiles |
| **Head-to-head** | Two players per line: `Sinner vs Alcaraz` |
| **Match IDs** | Matches to get again, for example once they have finished |
| **Include match statistics** | Adds serve, return, break point and tiebreak statistics for both players, per match and per set, with duration, court and head-to-head record |
| **Tours**, **Singles or doubles**, **Match status** | Keep only some tours, only singles or doubles, only results, live matches or fixtures |
| **Rankings** | The ranking lists to get, or whose top players' profiles to get |
| **Max results** | The most rows to save in the run, across all inputs |

Connection settings: the default works for most runs.

Today's results on the ATP and WTA tours, with statistics:

```json
{
  "dataType": "matches",
  "tours": ["atp", "wta"],
  "status": "finished",
  "includeStatistics": true
}
```

Every match of the 2026 US Open, men and women:

```json
{
  "dataType": "matches",
  "tournaments": ["US Open Men", "US Open Women"],
  "season": 2026
}
```

A player's matches in a date range, and a head-to-head:

```json
{
  "dataType": "matches",
  "players": ["Jannik Sinner"],
  "headToHead": ["Sinner vs Alcaraz"],
  "dateFrom": "2026-01-01",
  "dateTo": "2026-09-30"
}
```

Profiles of the top 100 ATP and WTA players, and the rankings:

```json
{ "dataType": "players", "rankings": ["atp", "wta"], "maxResults": 200 }
```

```json
{ "dataType": "rankings", "rankings": ["atp", "wta", "atp-live", "utr-men"] }
```

### ⬆️ Output

A finished match with statistics (shortened):

```json
{
  "type": "match",
  "id": 17206248,
  "name": "Alexander Bublik vs Jakub Menšik",
  "tour": "ATP",
  "date": "2026-10-01T12:30:00Z",
  "summary": "Jakub Menšik won 6-4 7-6(5) (Beijing, Round of 32)",
  "status": "finished",
  "statusDetail": "Ended",
  "resultType": "completed",
  "tournament": "Beijing",
  "season": 2026,
  "surface": "Hardcourt outdoor",
  "tournamentPoints": 500,
  "round": "Round of 32",
  "matchType": "singles",
  "player1": "Alexander Bublik",
  "player1Country": "Kazakhstan",
  "player1Rank": 21,
  "player2": "Jakub Menšik",
  "player2Country": "Czechia",
  "player2Seed": "8",
  "winner": "Jakub Menšik",
  "player1Sets": 0,
  "player2Sets": 2,
  "score": "4-6 6-7(5)",
  "sets": [
    {"set": 1, "player1Games": 4, "player2Games": 6, "player1Tiebreak": null, "player2Tiebreak": null},
    {"set": 2, "player1Games": 6, "player2Games": 7, "player1Tiebreak": 5, "player2Tiebreak": 7}
  ],
  "durationMinutes": 97,
  "court": "Diamond Court",
  "headToHead": {"player1Wins": 1, "player2Wins": 3},
  "player1Stats": {
    "aces": 7, "doubleFaults": 1,
    "firstServesIn": 56, "firstServesTotal": 82, "firstServePct": 68.3,
    "firstServePointsWon": 40, "firstServePointsPlayed": 56, "firstServePointsWonPct": 71.4,
    "secondServePointsWon": 11, "secondServePointsPlayed": 26, "secondServePointsWonPct": 42.3,
    "breakPointsSaved": 5, "breakPointsFaced": 7, "breakPointsSavedPct": 71.4,
    "breakPointsConverted": 1, "breakPointChances": 3, "breakPointsConvertedPct": 33.3,
    "totalPointsWon": 69, "servicePointsWon": 51, "returnPointsWon": 18,
    "serviceGamesPlayed": 11, "tiebreaksWon": 0, "winners": null, "unforcedErrors": null
  },
  "player2Stats": {"aces": 10, "doubleFaults": 2, "firstServePct": 73.5, "...": "..."},
  "statsBySet": [{"set": 1, "player1": {"aces": 4, "...": "..."}, "player2": {"aces": 4, "...": "..."}}],
  "input": "2026-10-01",
  "scrapedAt": "2026-10-02T11:03:38Z"
}
```

A player profile has `name`, `country`, `birthDate`, `age`, `birthplace`, `heightCm`, `weightKg`, `plays`, `turnedPro`, `rank`, `rankingPoints`, `bestRank`, `liveRank`, `utrRating`, `prizeMoneySeason`, `prizeMoneyCareer`, `seasonStats` (matches, wins, titles, aces, serve and return percentages, break points, tiebreaks, winners and unforced errors), `seasonStatsBySurface` and `titlesBySeason`. A ranking row has `rankingName`, `rank`, `previousRank`, `rankChange`, `points`, `bestRank`, `tournamentsPlayed`, `name` and `country`.

The **Overview** tab shows every row with its name, tour, date and a one-line summary. The **Matches**, **Match statistics**, **Players** and **Rankings** tabs show each kind as its own table. The run report lists any player, tournament or match that does not exist.

### Which statistics come with which tour?

| Statistics | Grand Slams | ATP, WTA, Challenger, WTA 125 | ITF |
|---|---|---|---|
| Aces, double faults, break points converted, points won, return points | ✅ | ✅ | ✅ (most matches) |
| First serve %, second serve %, break points saved, service games | ✅ | ✅ | points on serve only |
| Statistics per set, match duration | ✅ | ✅ | some matches |
| Winners and unforced errors | main draw | | |

Statistics are published once a match starts. Upcoming matches, walkovers and many UTR and exhibition matches have none: those rows come without them.

### How can I use tennis data?

- **Tennis analytics and models:** build datasets of results and serve and return statistics for every tour and surface
- **Live score apps and widgets:** live set and game scores for every match being played
- **Fantasy tennis and media:** player form, head-to-head records and rankings for previews and articles
- **Player scouting:** follow young players on the ITF, Challenger and UTR tours, with their records and ranking moves
- **Ranking tracking:** save the ATP, WTA and UTR rankings every week and compare
- **Research:** years of tournament results for academic and sports science projects

### How many matches can I get?

A single day has 200 to 800 matches across all tours, and a run can cover up to a year of days. Tournaments are read to the last match of the draw, qualifying included. A player's history goes back as far as their matches are recorded, often several hundred matches. A match that two inputs list (a player's match in a tournament you also asked for) is saved once.

### Scrape more sports data

| Actor | What it gets |
|---|---|
| [Google News Scraper API - Full Text, Monitoring & RSS](https://apify.com/automly/google-news-scraper-api) | Tennis news by player or tournament name, with full article text |
| [X (Twitter) Scraper - Profile Tweets & Posts](https://apify.com/automly/x-twitter-profile-tweets-scraper) | Tweets from players, tours and tournaments with views, likes and media |
| [Reddit Scraper - Posts, Comments, Users, Subreddits & Search](https://apify.com/automly/reddit-ultimate-scraper) | Posts and comments from r/tennis and other sports subreddits |
| [YouTube Search Scraper - Videos, Channels & Playlists](https://apify.com/automly/youtube-search-results-scraper) | Match highlight videos by keyword, with views and upload dates |

### ❓FAQ

#### How much does it cost to scrape tennis data?

$1.00 per 1,000 matches, $2.50 per 1,000 matches with statistics, $2.00 per 1,000 player profiles and $0.50 per 1,000 ranking rows on the Free plan, less on higher plans. See "How much does it cost to scrape tennis data?" above for every plan.

#### Does it include betting odds?

No. It collects public scores, results, statistics, player profiles and rankings only.

#### How far back can I go, and how much can one run get?

Up to 366 days of matches per run, any earlier tournament edition with **Season (year)** (Grand Slams back to 2009), and the top 500 of each ranking list. A player's history goes back as far as their matches are recorded.

#### What time zone are the dates in?

All times are UTC (ISO 8601 with a `Z`). A date range covers the matches that start on those days in UTC.

#### How do retirements and walkovers show?

`status` is `finished` and `resultType` says `retired`, `walkover` or `defaulted`. A retirement keeps the score at the moment the match stopped; a walkover has no score.

#### Can I get live scores continuously?

Yes. Schedule the actor every few minutes with **Live matches** on, or call its real-time API at `/matches?live=true`. Each call returns the current score of every live match.

#### Can I get results in real time?

Yes. Besides normal runs, the actor has a real-time API that answers with the rows directly, without starting a run:

```
GET https://<your-actor-standby-url>/matches?date=2026-10-01&tour=atp,wta
GET https://<your-actor-standby-url>/players?name=Jannik%20Sinner
GET https://<your-actor-standby-url>/rankings?ranking=atp
Authorization: Bearer <YOUR_API_TOKEN>
```

#### Can I connect Tennis Scraper to other tools or AI agents?

Yes. Start runs and download results with the Apify API or the Python and JavaScript clients, send results to Make, Zapier, n8n, Google Sheets or Slack with Apify integrations and webhooks, or let AI assistants and agents run it through the Apify MCP server at [mcp.apify.com](https://mcp.apify.com).

#### Do I need an account or an API key?

No. You only need a free Apify account. The actor reads the same public scores and statistics anyone can see without signing in.

#### Is it legal to scrape tennis data?

This actor only collects public tennis results, statistics, rankings and player profiles that anyone can see. It does not collect private data such as email addresses or phone numbers. Player profiles still contain personal data such as names, birth dates and birthplaces, which may be protected by laws such as GDPR, so only scrape it for a legitimate reason and ask a lawyer if you are unsure. You can read more in [Is web scraping legal?](https://blog.apify.com/is-web-scraping-legal/)

Tennis Scraper is an independent tool. It is not affiliated with, endorsed by or sponsored by the ATP, the WTA, the ITF, UTR or any tournament.

#### Something isn't working, or I need another field?

Open the **Issues** tab with your input and what you expected. We read every one and usually reply within a day.

### ⭐ Your feedback

Have an idea or found a problem? Tell us on the **Issues** tab. If Tennis Scraper saved you time, a short review on the **Reviews** tab helps other tennis fans find it and tells us what to improve.

# Actor input Schema

## `dataType` (type: `string`):

Choose one kind of data per run: matches (results, live scores, fixtures, optional match statistics), player profiles, or rankings.

## `dateFrom` (type: `string`):

First day of matches to get (dates are in UTC). Leave empty for today. Through the API you can also send today, yesterday, tomorrow, +3 days or 3 days ago.

## `dateTo` (type: `string`):

Last day of matches to get. Leave empty for a single day. At most 366 days per run.

## `live` (type: `boolean`):

Get the matches being played right now on every tour, with the current set and game score.

## `tournaments` (type: `array`):

Tournament names or IDs, one per line, for example Wimbledon (men's singles), Wimbledon Women, US Open Men Doubles, Beijing. Every match of the tournament's latest edition is returned, including qualifying; set Season for an earlier year.

## `season` (type: `integer`):

Year of the tournament edition to get, for example 2025. Leave empty for the latest edition.

## `players` (type: `array`):

Player names or IDs, one per line (Jannik Sinner, Swiatek). For matches: each player's latest matches and upcoming fixtures. For player profiles: each player's profile.

## `headToHead` (type: `array`):

Two players per line as Player A vs Player B, for example Sinner vs Alcaraz. Returns every match the two have played against each other.

## `matchIds` (type: `array`):

Match IDs (the id field of a match row), one per line, to get those matches again, for example with statistics once they have finished.

## `includeStatistics` (type: `boolean`):

Add each finished or live match's statistics for both players: aces, double faults, first and second serve %, points won on serve, break points saved and converted, return points, service games, tiebreaks, plus winners and unforced errors where the tournament tracks them, for the whole match and for each set, with the match duration, court and head-to-head record. Lower tours (ITF, UTR) rarely have statistics: those matches come without them.

## `tours` (type: `array`):

Keep only matches from these tours. Leave empty for every tour.

## `matchType` (type: `string`):

Keep only singles or only doubles matches.

## `status` (type: `string`):

Keep only finished matches (results, including retirements and walkovers), live matches, or upcoming matches (fixtures).

## `rankings` (type: `array`):

For rankings: the lists to get (top 500 each). For player profiles with no players listed: the profiles of the top-ranked players of these lists.

## `maxResults` (type: `integer`):

The most rows to save in this run, across all inputs (you are charged for no more than this); with several rankings it is shared evenly between them. 0 saves all of them (for player profiles from rankings: the top 100 of each ranking).

## `proxyConfiguration` (type: `object`):

The default works for most runs. If results come back empty, try a different proxy option.

## Actor input object example

```json
{
  "dataType": "matches",
  "live": false,
  "includeStatistics": false,
  "matchType": "all",
  "status": "all",
  "rankings": [
    "atp",
    "wta"
  ],
  "maxResults": 100,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `overview` (type: `string`):

No description

## `matches` (type: `string`):

No description

## `statistics` (type: `string`):

No description

## `players` (type: `string`):

No description

## `rankings` (type: `string`):

No description

## `results` (type: `string`):

No description

## `report` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "dataType": "matches",
    "maxResults": 100,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("automly/tennis-data-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "dataType": "matches",
    "maxResults": 100,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("automly/tennis-data-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "dataType": "matches",
  "maxResults": 100,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call automly/tennis-data-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,automly/tennis-data-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/bSXa1QCTnydwfZqud/builds/LezVDBtZbKb1j3TAC/openapi.json
