# Tennis Scraper API (`cleanscrape/tennis-match-data`) Actor

Tennis match data from Flashscore. Type a tournament or player name, paste a link, or collect by day. Scores with tiebreaks, statistics with counts, set durations, point-by-point, form and head-to-head. From $1 per 1,000 matches.

- **URL**: https://apify.com/cleanscrape/tennis-match-data.md
- **Developed by:** [CleanScrape](https://apify.com/cleanscrape) (community)
- **Categories:** Sports, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.80 / 1,000 matches

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
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 API

Download tennis match data from Flashscore as CSV, Excel or JSON. Type a tournament or player name, paste a Flashscore link, or leave the list empty to collect matches by day.

Each match comes with the score including tiebreaks, the round and an exact status. Optional extras:

- match statistics with counts
- set durations and the umpire
- point-by-point
- recent form and head-to-head

It is built for people who model, analyse or write about tennis. The numbers keep their counts ("58 of 80 first-serve points won"), and every row says what Flashscore had for that match. Nothing is filled in by guesswork.

No Flashscore account, cookies or API key is needed. This is an independent tool, maintained by CleanScrape.

Here is a walkthrough with Wimbledon 2025: every singles match with statistics, and what the totals say about how Sinner won. Type your own tournament or player for another project.

https://www.youtube.com/watch?v=eDn2dKHsRXA

### Start with a small run

The form opens with **Wimbledon 2025** and a maximum of 20 matches. That gives you the men's final, semi-finals and quarter-finals with statistics. Start it as it is to see the output, then change the list.

1. In **Tournaments, players or matches**, type what you want, one per line. Leave it empty to collect by day.
2. If the list is empty, pick the **Days**.
3. Set **Maximum matches** and start the run. With several lines, the maximum is shared evenly between them.

Open the **Matches**, **Statistics** or **Form and head-to-head** view, then export. Use JSON when you need the nested data (per-set statistics, point-by-point, form lists).

### What you can type

| You type | You get |
| --- | --- |
| `Wimbledon 2025` | The 2025 men's and women's singles draws, including qualifying |
| `Wimbledon` | The latest edition. Set **Editions per tournament** to 5 for the last five years |
| `US Open women 2024`, `Australian Open doubles`, `Wimbledon juniors` | Just those draws |
| `Sinner`, `Swiatek 2025` | The player's matches, newest first, or only that year |
| A Flashscore link | The tournament, player or match it points to. Any page of it works (results, draw, summary), from any Flashscore site, with or without https |
| A match ID such as `Glwf9adK` | That match |

Common names work too: `Roland Garros` finds the French Open and `ATP Finals` finds Turin. The run report shows how each line was read, for example *"Wimbledon 2025" was read as ATP Wimbledon 2025, WTA Wimbledon 2025*. If a name is unclear, the report says so and nothing unrelated is collected. Paste the Flashscore link in that case.

### Collect by day

Leave the list empty and choose **Days**: yesterday, today, tomorrow, the last or next 3 or 7 days, or custom dates. Flashscore's daily lists go 7 days back and 7 days ahead (UTC). For anything older, add the tournament or player to the list.

**Tours**, **Match type** and **Match status** narrow the days. The defaults are ATP and WTA, singles and finished matches. Tours also decide which draws a tournament name gives you. A pasted tournament link, a player or a match link is not limited by tour.

### Examples

Yesterday's ATP and WTA singles with statistics:

```json
{ "dayRange": "yesterday", "maxResults": 100 }
```

The last five Wimbledon editions, men and women:

```json
{ "targets": ["Wimbledon"], "seasons": 5, "maxResults": 3000 }
```

A player's 2026 matches with point-by-point:

```json
{ "targets": ["Sinner 2026"], "pointByPoint": true, "maxResults": 200 }
```

Upcoming matches with form and head-to-head:

```json
{ "dayRange": "next-3-days", "statuses": ["scheduled"], "statistics": false, "setDetails": false, "formAndH2H": true }
```

### What each match contains

| Column | Meaning |
| --- | --- |
| Start (UTC), Status | Scheduled start in UTC. Status is `finished`, `retired`, `walkover`, `scheduled`, `live`, `cancelled`, `postponed`, `interrupted`, `abandoned` or `awarded` |
| Tour, Type, Tournament, Country, Surface, Round, Qualifying, Season | Event details. Round is, for example, `Final` or `1/16-finals`; qualifying rounds start with `Qualification` |
| Home player, Away player | As listed by Flashscore. Doubles show both names and have partner columns |
| Winner, Sets won (home/away), Score | The home player comes first in the score. A tiebreak score in brackets is the points of the player who lost the set: `7-6(5) 6-7(5) 6-2`. Retirements end with `ret.`, walkovers show `w/o` |
| Sets (JSON) | Games, tiebreak points and duration for each set |
| Duration (min), Umpire | Match duration and the chair umpire, when **Set durations and umpire** is on |
| IDs and links | Match, player and tournament IDs and links. Keep ID columns as text in spreadsheets |
| Coverage notes | What was not available for this match, in plain words |

#### Statistics (Match statistics on)

Each statistic has a home and an away column. Ratios keep both numbers, so `firstServePointsWonHome` = 58 and `firstServePointsPlayedHome` = 80.

- **Service:** aces, double faults, first serve in (%), first-serve and second-serve points won and played, break points saved and faced.
- **Return:** first-return and second-return points won and played, break points converted and opportunities.
- **Points and games:** service, return and total points; service, return and total games.
- **Bigger events also have:** winners, unforced errors, net points, average first and second serve speed (km/h) and distance covered (m).

The same statistics for each set are in `setStatistics`. `statisticsLevel` is `full`, `basic` (only a couple of counters) or `none`.

#### Point-by-point (optional)

For every set, each game has its server, its winner, whether serve was broken, and every point score. Points that were break points, set points or match points are marked. Tiebreaks are listed point by point with the server of each point.

#### Form and head-to-head (optional)

- Head-to-head matches and the win count for each side. Each earlier meeting's sets are given from the current match's sides, so `2-1` means the home player won two sets.
- Each player's latest matches, overall and on the current surface: date, tournament, opponent, result and score.
- Summary columns (`homeFormWon` of `homeFormPlayed` and so on) for quick filtering.

Form lists are Flashscore's latest lists at collection time. For a past match they can include matches played after it.

### Coverage

What Flashscore publishes depends on the level of the event:

| Event | Score and status | Statistics | Point-by-point | Form and H2H |
| --- | --- | --- | --- | --- |
| ATP, WTA, Challenger, Grand Slams | Yes | Full | Yes | Yes |
| ITF | Yes | Usually none or basic | Sometimes | Yes |
| Walkover | Status only | None | None | Yes |
| Scheduled | Start time | After the match | After the match | Yes |

Matches from earlier years keep their details on Flashscore, so tournament and player history comes with statistics too. Round names come from the tournament's own lists. A few events, such as the Laver Cup, have no round names.

Every run saves a **Run report**: how each line was read, what it returned, how many matches matched your settings, detail coverage, and why the run stopped. Open it from the Output tab.

### Pricing

You pay per result, with no start fee:

| What is saved | Price per 1,000 |
| --- | --- |
| Match | $1.00 |
| Match details: statistics, set details or point-by-point, when Flashscore has them | $1.50 |
| Form and head-to-head | $0.50 |

Details and form are charged only when they are actually saved for that match. For example:

- 100 matches with statistics cost $0.25.
- A full Grand Slam draw (239 matches) with statistics costs about $0.60.

Apify tier discounts apply. Set a spending limit for large runs; the run stops cleanly when it is reached.

### Common questions

#### Where can I get tennis match statistics?

This Actor exports them from Flashscore: aces, double faults, serve and return points, break points and more, with the counts behind each percentage. Type a tournament or player name, or collect every match by day.

#### Which tours are covered?

ATP, WTA, Challenger, ITF, juniors, team events such as the Davis Cup, and exhibitions. Choose them under **Tours**.

#### Can I get past seasons?

Yes. Add a year (Wimbledon 2019) or set **Editions per tournament** to collect several years. Detailed statistics are mostly available for recent seasons.

#### Does it include betting odds?

No. It collects results, statistics, point-by-point, form and head-to-head.

#### How much does it cost?

$1 per 1,000 matches, plus $1.50 per 1,000 for statistics or other match details and $0.50 per 1,000 for form and head-to-head. 100 matches with statistics cost $0.25. See **Pricing** above.

### Good to know

- Times are UTC.
- Live matches are a snapshot at collection time.
- Bookmaker odds and world rankings are not included.
- The maximum is a limit, not a promise that so many matches exist.
- If Flashscore changes its data feed, the run stops with a clear message instead of saving empty rows.

Questions or a reproducible problem: use the Issues tab or email contact.cleanscrape@gmail.com with the run ID.

CleanScrape is independent of Flashscore, the ATP, the WTA and the ITF. Match data comes from Flashscore's public pages.

### More tools from CleanScrape

- [App Store & Google Play Reviews Scraper](https://apify.com/cleanscrape/app-store-reviews-scraper): reviews from both app stores in one table
- [Google Trends Scraper API](https://apify.com/cleanscrape/google-trends-scraper): search interest over time, by region, related queries and trending searches
- [Map Your Show Exhibitor Scraper API](https://apify.com/cleanscrape/event-exhibitor-monitor): trade show exhibitor lists and booth changes
- [Shopify Product Reviews Scraper API](https://apify.com/cleanscrape/shopify-reviews-scraper): Judge.me and Okendo reviews from Shopify product links
- [Google News Scraper API](https://apify.com/cleanscrape/google-news-scraper): headlines from Google News and publisher feeds
- [Pinterest Scraper API](https://apify.com/cleanscrape/pinterest-scraper): pins from searches, boards and profiles
- [Google Hotels Scraper API](https://apify.com/cleanscrape/google-hotels-scraper): hotel prices and booking offers for your dates
- [TikTok Shop Product Scraper](https://apify.com/cleanscrape/tiktok-shop-product-scraper): US TikTok Shop prices, sellers and ratings
- [Threads Scraper API](https://apify.com/cleanscrape/threads-scraper): public Threads posts from accounts or post links
- [Likee Scraper API](https://apify.com/cleanscrape/likee-scraper): Likee videos and comments

# Actor input Schema

## `targets` (type: `array`):

Type a name or paste a Flashscore link, one per line. For example: Wimbledon 2025, Sinner, Swiatek 2026, US Open women, or a match link. Leave the list empty to collect matches by day instead.

## `dayRange` (type: `string`):

All matches Flashscore lists for these days (UTC), from 7 days back to 7 days ahead.

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

Total for the run, shared evenly across the lines in your list. Start small to check the output. Fewer matches may be available.

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

Used for days, and to choose the draws when you type a tournament name (Wimbledon gives the ATP and WTA singles). A pasted tournament link or a player is not limited by tour.

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

Used for days, players and tournament names. Typing "doubles" with a tournament name also works.

## `statuses` (type: `array`):

Match links are always returned. Each saved match shows its exact status.

## `startDate` (type: `string`):

With Days set to Custom dates. Within the last 7 days or the next 7 days (UTC).

## `endDate` (type: `string`):

Optional. Defaults to the start date.

## `seasons` (type: `integer`):

For a tournament without a year: 1 is the latest edition, 5 adds the four before it. With a year (Wimbledon 2024) only that edition is collected.

## `includeQualifying` (type: `boolean`):

Qualifying matches are marked in the Qualifying and Round columns.

## `since` (type: `string`):

For players. Only matches on or after this date. A year after the name (Sinner 2025) also works. Without either, the newest matches come first until Maximum matches is reached.

## `statistics` (type: `boolean`):

Aces, double faults, serve and return points with counts, break points and games; for bigger events also winners, unforced errors, net points, serve speed and distance. Per match and per set.

## `setDetails` (type: `boolean`):

Duration of each set and the match, tiebreak points and the chair umpire.

## `pointByPoint` (type: `boolean`):

Every game with server and hold or break, and each point with break, set and match points marked. Best exported as JSON.

## `formAndH2H` (type: `boolean`):

Head-to-head record and each player's latest matches, overall and on this surface. Most useful before a match.

## `formMatches` (type: `integer`):

How many recent matches to include for each player when Form and head-to-head is on.

## Actor input object example

```json
{
  "targets": [
    "Wimbledon 2025"
  ],
  "dayRange": "yesterday",
  "maxResults": 20,
  "tours": [
    "atp",
    "wta"
  ],
  "matchType": "singles",
  "statuses": [
    "finished"
  ],
  "seasons": 1,
  "includeQualifying": true,
  "statistics": true,
  "setDetails": true,
  "pointByPoint": false,
  "formAndH2H": false,
  "formMatches": 10
}
```

# Actor output Schema

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

No description

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

No description

## `summary` (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 = {
    "targets": [
        "Wimbledon 2025"
    ],
    "maxResults": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("cleanscrape/tennis-match-data").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 = {
    "targets": ["Wimbledon 2025"],
    "maxResults": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("cleanscrape/tennis-match-data").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 '{
  "targets": [
    "Wimbledon 2025"
  ],
  "maxResults": 20
}' |
apify call cleanscrape/tennis-match-data --silent --output-dataset

```

## MCP server setup

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

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/KE3jGjlJIfaRgbYn7/builds/Ed4b8C80zVI5HfMKm/openapi.json
