# Flashscore Tennis Scraper & API: Live Scores, Stats, H2H & Odds (`devarq/tennis-scraper`) Actor

Flashscore tennis data API: fixtures, live scores and results for ATP, WTA, Challenger and ITF matches, with optional match stats, point-by-point, head-to-head (H2H) and betting odds. From $1.50 per 1,000 matches.

- **URL**: https://apify.com/devarq/tennis-scraper.md
- **Developed by:** [Devarq](https://apify.com/devarq) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.50 / 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.

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

## Flashscore Tennis Scraper & API: Live Scores, Stats, H2H & Odds

**Flashscore tennis data: fixtures, live scores and results for ATP, WTA, Challenger and ITF matches, with optional full match statistics, point-by-point, head-to-head and betting odds. From $1.50 per 1,000 matches.**

Tell it a date, a match or a player. It returns clean, ready-to-use match data as JSON, CSV or Excel. There's no account to create on any tennis site, nothing to install and no proxies to configure. Data comes from Flashscore.

***

### Why use this Actor?

**Everything in one run.** Most tennis scrapers give you either a basic match list *or* one kind of detail. This one returns fixtures, live scores, results, match statistics, point-by-point, head-to-head, odds and player history from a single Actor, all in the same format.

**You only pay for what you actually get.**

- Stats on a match that hasn't started? Not charged.
- A match that fails to load? Not charged.
- Matches removed by your filters? Not charged.
- A typo in a player's name? The run explains what went wrong and charges no results.

The only fixed cost is the $0.002 run start fee, which Apify charges for every run.

**Verified data, never guessed.** Player rankings are the current ATP and WTA singles rankings (top 100), and odds are real bookmaker prices. Anything that can't be verified is left empty rather than filled with a guess.

**Built for automation and AI agents.** An empty input already returns today's matches. Dates can be written as `"yesterday"` or `"+2"`, any Flashscore link works, and every run ends with a plain-English status message.

#### How it compares with other tennis Actors on the Store

| | **This Actor** | [Tennis Scraper](https://apify.com/crawlstone/tennis-scraper) (crawlstone) | [Flashscore Tennis Matches](https://apify.com/extractify-labs/flashscore-tennis-matches) (extractify-labs) | [Tennis Abstract Scraper](https://apify.com/parseforge/tennis-abstract-scraper) (ParseForge) |
|---|:---:|:---:|:---:|:---:|
| Fixtures, live scores, results | ✅ | ✅ | ✅ | ❌ history only |
| ATP, WTA, Challenger, ITF | ✅ | ✅ | ✅ | ATP & WTA |
| Match statistics | ✅ whole match + each set | per period, in point-by-point mode | ❌ | ✅ serve & return |
| Point-by-point | ✅ | ✅ | ❌ | ❌ |
| Head-to-head & per-surface record | ✅ | ❌ | ❌ | ❌ |
| Betting odds | ✅ with opening odds & movement | ✅ | ❌ | ❌ |
| Player results & upcoming matches | ✅ | ✅ | ❌ | ✅ |
| Rounds, seeds, umpire, set durations | ✅ | partial | partial | partial |
| "Only what changed" monitoring | ✅ | ❌ | ❌ | ❌ |
| Data source | Flashscore | Sofascore + Tennis Abstract | Flashscore | Tennis Abstract |
| **Price per 1,000 results** | **$1.50** (+$2 per add-on used) | $8 (at least 1 result charged per run) | $1 | $21 + $0.0035 per run |

*Compared on 2 October 2026 using each Actor's public Store page. Other Actors may have changed since; check their pages for current details.*

***

### Quick start

1. Click **Try for free**.
2. Leave the input as it is, or pick a date and tours.
3. Click **Start**. In about 10 seconds you'll have today's ATP and WTA matches in the **Output** tab, ready to download as JSON, CSV or Excel.

***

### What can you do with it? (recipes)

Each recipe shows the input to use. Copy it into the **JSON** input tab, or send it through the API.

#### 1. Today's matches (default)

```json
{}
```

You get every ATP and WTA singles match today: scheduled, live and finished. If there are none today (for example in the December off-season), you get the nearest day's matches instead, and the status message tells you which day.

#### 2. Yesterday's results with full statistics

```json
{ "date": "yesterday", "tours": ["ATP", "WTA"], "status": ["finished"], "includeStats": true }
```

You get each finished match plus aces, double faults, serve percentages, break points and more, for the whole match and each set.

#### 3. Tomorrow's matches with betting odds

```json
{ "date": "tomorrow", "tours": ["ATP"], "includeOdds": true }
```

You get the schedule plus pre-match home/away odds (current, opening, and whether they've moved up or down).

#### 4. A specific match, in full detail

```json
{ "mode": "match", "matchIds": ["https://www.flashscore.com/match/ldWaJXYa/"], "includeH2H": true }
```

Paste any Flashscore match link or ID. You always get statistics and point-by-point; add `includeH2H` for the head-to-head record.

#### 5. A player's recent results and upcoming matches

```json
{ "mode": "player", "player": "Sinner", "maxItems": 50 }
```

Type a name ("Sinner", "Świątek" — accents optional) or paste a Flashscore player link. Matches come newest first, so upcoming matches are at the top. Add `"status": ["scheduled"]` for upcoming matches only.

#### 6. A week of Challenger and ITF results

```json
{ "dateFrom": "-7", "dateTo": "yesterday", "tours": ["Challenger", "ITF"], "maxItems": 0 }
```

`maxItems: 0` means "no limit". Your Apify maximum charge per run still protects you. A full week like this is over 1,000 matches: it takes about 5 minutes and costs about $1.70.

#### 7. Live score monitoring (only changes)

```json
{ "date": "today", "status": ["live", "finished"], "onlyChanges": true }
```

Schedule this every 10–15 minutes. The first run returns everything; later runs return **only matches whose score or status changed**, so you only pay for updates.

***

### What you get back

One row per match. A real example (De Minaur vs Navone, Beijing, 1 October 2026):

```json
{
  "match_id": "ldWaJXYa",
  "match_url": "https://www.flashscore.com/match/tennis/ldWaJXYa/",
  "start_time_utc": "2026-10-01T03:10:00+00:00",
  "match_status": "FINISHED",
  "tour": "ATP",
  "category": "singles",
  "tournament_name": "Beijing",
  "tournament_country": "China",
  "surface": "hard",
  "round_name": "Round of 32",
  "home_player_names": "De Minaur A.",
  "away_player_names": "Navone M.",
  "score": "7-6 (7-4), 6-2",
  "winner": "home",
  "result_type": "normal",
  "seeds": { "home": "5" },
  "duration_minutes": 115,
  "umpire": "Finke T."
}
```

Each row also includes `home_players` and `away_players` (name, ID, country, ranking where available), `sets` (games and tiebreak points per set), and, while a match is live, `current_game_score` and `current_server`.

#### Add-ons (only when you switch them on)

**`includeStats`** adds `stats`: every statistic for the whole match (`match`) and for each set (`set_1`, `set_2`…). Each one has the original label plus parsed numbers:

```json
"first_serve_percentage": { "home": { "raw": "59%", "pct": 59 }, "away": { "raw": "60%", "pct": 60 } },
"break_points_converted": { "home": { "raw": "3/7", "won": 3, "total": 7 }, "away": { "raw": "1/10", "won": 1, "total": 10 } }
```

Included: aces, double faults, first and second serve won %, break points saved and converted, return points won, service and return games won, total points and games, and more.

**`includePointByPoint`** adds `point_by_point`: every game in every set, showing who served, who won, whether it was a break, and each point's score, with break, set and match points marked.

**`includeH2H`** adds `h2h`: overall wins for each player, the record per surface (clay, grass, hard), past meetings with scores, and each player's recent form.

**`includeOdds`** adds `odds`: the bookmaker, current home/away odds, opening odds and the direction of movement:

```json
{ "bookmaker": { "name": "bet365" }, "home": { "value": "1.33", "opening": "1.29", "change": "up" }, "away": { "value": "3.4", "opening": "3.75", "change": "down" } }
```

#### Ready-made tables

In the **Output** tab you can switch between **Matches** (an overview), **Stats** (key statistics as columns) and a **flat CSV/Excel** view that opens cleanly in spreadsheets.

***

### Pricing

Pay per result. No subscription and no proxy fees.

| You get | Price |
|---|---|
| Each run | $0.002 |
| Each match | $0.0015 ($1.50 per 1,000) |
| + statistics and/or point-by-point | $0.002 per match that has them |
| + head-to-head | $0.002 per match that has it |
| + odds | $0.002 per match that has them |

**What typical jobs cost:**

| Job | About |
|---|---|
| Today's ATP + WTA matches (~25) | $0.04 |
| 100 finished matches with full statistics | $0.35 |
| One match with stats, point-by-point and H2H | $0.008 |
| A player's last 50 matches | $0.08 |
| Monitoring live scores every 15 minutes for a day | about $0.20 + changed matches |

Apify's free plan includes $5 of credit every month, enough for about 125 daily match lists or 900 detailed single-match lookups.

***

### Use it as an API

One HTTP request runs the scraper and returns the matches as JSON in the same response. Use your Apify API token (Console → Settings → API & Integrations):

```bash
curl -X POST "https://api.apify.com/v2/acts/devarq~tennis-scraper/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"date": "yesterday", "tours": ["ATP", "WTA"], "includeStats": true, "maxItems": 50}'
```

Or in Python (`pip install apify-client`):

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("devarq/tennis-scraper").call(run_input={"date": "today", "includeOdds": True})
matches = client.dataset(run["defaultDatasetId"]).list_items().items
```

Each call starts a short run, so expect a response in roughly 10–60 seconds depending on the add-ons, not milliseconds. For live tracking, schedule a run every few minutes with `"onlyChanges": true` and you'll only receive (and pay for) matches whose score or status changed.

***

### Use with AI agents (MCP)

Add this Actor to Claude, ChatGPT, Cursor or any MCP client through the Apify MCP server, then just ask:

- "What ATP matches are on tomorrow, with odds?"
- "Get yesterday's WTA results with full statistics."
- "Show Carlos Alcaraz's last 20 matches and his record on clay."
- "Give me the point-by-point of this match: https://www.flashscore.com/match/ldWaJXYa/"

Agents don't need perfect input. Relative dates, names instead of IDs, any Flashscore link and common field aliases are all understood. If something can't be found, the run says so plainly and charges no results (only the $0.002 run start fee).

***

### FAQ

**Which matches are included?** ATP, WTA, Challenger and ITF; singles by default, doubles with `"matchType": "doubles"`. Grand Slams, Davis Cup and Billie Jean King Cup can be selected as `tours` too. Junior, wheelchair and exhibition matches appear as tour `"Other"` (select them with `"tours": ["Other"]`).

**How far back and ahead can I go?** 7 days back and 7 days ahead in date mode. Player mode returns a player's longer match history.

**Are walkovers and retirements handled?** Yes. Each has its own status (`WALKOVER`, `RETIRED`, `CANCELLED`, `INTERRUPTED`) and `result_type`. The `finished` status filter returns only matches that were actually played (including retirements); use `cancelled` for walkovers, cancellations and postponements.

**What time zone is used?** All times are UTC, and dates select UTC calendar days. A few matches starting right around midnight UTC may be listed on the neighbouring day.

**How live are live scores?** Usually within a few minutes of the real score.

**Are rankings always filled in?** Current ATP and WTA top-100 singles rankings are included. Lower-ranked players and Challenger/ITF players may have no ranking.

**Are odds always available?** Only when bookmakers offer them for that match. They're never made up. For finished matches you get the closing pre-match odds, which is useful for backtesting betting models.

**What if something breaks?** The Actor checks every run. If the data source stops returning valid data, the run fails clearly instead of giving you bad data, and no results are charged.

**Is this an official Flashscore product?** No. It's an independent tool, not affiliated with or endorsed by Flashscore. Every row links to its source page.

**Found a bug or need a field?** Open an issue on this page or email support@devarq.dev.

***

### Settings reference

#### Modes

| `mode` | What to provide | What you get |
|---|---|---|
| `matches` (default) | a date or date range | all matching fixtures, live matches and results |
| `match` | `matchIds` (IDs or Flashscore links) | those matches with stats and point-by-point |
| `player` | `player` (name or Flashscore link) | that player's matches, newest first (upcoming matches on top) |

#### All inputs

| Field | Works in | Type | Default | Description |
|---|---|---|---|---|
| `mode` | – | string | `matches` | `matches`, `match` or `player` |
| `date` | matches | string | `today` | `YYYY-MM-DD`, `today`, `yesterday`, `tomorrow`, or `-7` … `+7` |
| `dateFrom`, `dateTo` | matches | string | – | a date range (same formats), within 7 days of today |
| `tours` | matches | list | `["ATP", "WTA"]` | ATP, WTA, Challenger, ITF, ITF Men/Women, Challenger Men/Women, Grand Slam, Davis Cup, Billie Jean King Cup, Teams, Other |
| `matchType` | matches | string | `singles` | `singles` or `doubles` |
| `status` | matches, player | list | all | `scheduled`, `live` (incl. suspended), `finished` (played to the end or retired), `cancelled` (walkovers, cancellations, postponements) |
| `tournament` | matches, player | string | – | only tournaments whose name contains this text |
| `player` | player, matches | string | – | **player mode:** who to look up (name or Flashscore link). **matches mode:** optional filter on player name |
| `matchIds` | match | list | – | match IDs or Flashscore links |
| `maxItems` | all | integer | `200` | maximum matches returned; `0` = no limit |
| `includeStats` | all | boolean | `false` | add match and per-set statistics (always on in match mode) |
| `includePointByPoint` | all | boolean | `false` | add point-by-point (always on in match mode) |
| `includeH2H` | all | boolean | `false` | add head-to-head and recent form |
| `includeOdds` | all | boolean | `false` | add pre-match odds |
| `onlyChanges` | all | boolean | `false` | on repeated runs with the same input, return only matches that changed |

Inputs that don't apply to the chosen mode are simply ignored.

#### Output fields

**Always present**

| Field | Example | Meaning |
|---|---|---|
| `match_id` | `ldWaJXYa` | Flashscore match ID |
| `match_url` | `https://www.flashscore.com/match/tennis/ldWaJXYa/` | link to the match |
| `start_time_utc` | `2026-10-01T03:10:00+00:00` | scheduled start, UTC |
| `match_status` | `FINISHED` | `SCHEDULED`, `LIVE`, `FINISHED`, `RETIRED`, `WALKOVER`, `CANCELLED`, `INTERRUPTED` |
| `tour` | `ATP` | `ATP`, `WTA`, `Challenger`, `ITF`, `Teams` or `Other` |
| `category` | `singles` | `singles` or `doubles` |
| `tournament_name`, `tournament_country` | `Beijing`, `China` | tournament and host country |
| `surface` | `hard` | `hard`, `clay`, `grass`, `hard (indoor)`… |
| `round_name` | `Round of 32` | round, when known |
| `home_player_names`, `away_player_names` | `De Minaur A.` | names as text (doubles: `A / B`) |
| `home_players`, `away_players` | list | per player: name, ID, country, ranking (top 100), photo link |
| `score` | `7-6 (7-4), 6-2` | score as text |
| `sets` | list | per set: games for each side, tiebreak points, duration |
| `winner` | `home` | `home`, `away` or empty |
| `result_type` | `normal` | `normal`, `retired`, `walkover`, `cancelled`… |
| `seeds`, `entries` | `{"home": "5"}` | seeds, and qualifier / lucky loser / wild card flags |
| `duration_minutes`, `umpire` | `115`, `Finke T.` | when available |
| `current_game_score`, `current_server` | `{"home": "30", "away": "15"}`, `home` | live matches only |
| `source_url`, `scraped_at` | – | source link and collection time (UTC) |

**Only with add-ons:** `stats` (`includeStats`), `point_by_point` (`includePointByPoint`), `h2h` (`includeH2H`), `odds` (`includeOdds`). If one of them couldn't be loaded for a match, the row still arrives with `details_error`, `h2h_error` or `odds_error` explaining why, and that add-on isn't charged.

***

### Changelog

**0.1** (October 2026): first release. Date, match and player modes; statistics, point-by-point, head-to-head, odds, rounds and rankings; change-only monitoring; Matches, Stats and CSV/Excel views.

# Actor input Schema

## `mode` (type: `string`):

matches = fixtures/live/results for a date or range (default). match = specific matches by ID or URL, always with stats and point-by-point. player = one player's recent results and upcoming matches.

## `player` (type: `string`):

Player mode: the player to look up (name like Sinner, or a Flashscore player URL). Matches mode: optional filter on player name.

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

Match mode only. Flashscore match IDs (e.g. ldWaJXYa) or match URLs, one per item.

## `date` (type: `string`):

Matches mode only. YYYY-MM-DD, today, tomorrow, yesterday, or an offset such as -3 or +2 (within 7 days of today). Days are UTC calendar days.

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

Matches mode only. Range start (same formats as date), within 7 days of today.

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

Matches mode only. Range end (same formats as date), within 7 days of today.

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

Matches mode only. Tours to include, e.g. \["ATP", "WTA"]. Also: Challenger, Challenger Men, Challenger Women, ITF, ITF Men, ITF Women, Grand Slam, Davis Cup, Billie Jean King Cup, Teams, Other (juniors, wheelchair, exhibitions). Default: ATP and WTA.

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

Matches mode only. singles (default) or doubles.

## `status` (type: `array`):

Matches and player modes. Statuses to include: scheduled, live (includes suspended matches), finished (played to the end or retired), cancelled (walkovers, cancellations, postponements). Empty = all.

## `tournament` (type: `string`):

Matches and player modes. Only tournaments whose name contains this text (case-insensitive).

## `maxItems` (type: `integer`):

All modes. Maximum matches returned after filters. 0 = no limit (your maximum charge per run still applies).

## `includeStats` (type: `boolean`):

All modes. Add match and per-set statistics (charged only when the match has them). Always on in match mode.

## `includePointByPoint` (type: `boolean`):

All modes. Add every game and point with breaks marked (charged only when available). Always on in match mode.

## `includeH2H` (type: `boolean`):

All modes. Add head-to-head record, per-surface record and recent form.

## `includeOdds` (type: `boolean`):

All modes. Add pre-match home/away bookmaker odds when available.

## `onlyChanges` (type: `boolean`):

All modes. On repeated runs with identical input, return only matches whose status or score changed. Use with a schedule.

## Actor input object example

```json
{
  "mode": "matches",
  "date": "today",
  "tours": [
    "ATP",
    "WTA"
  ],
  "matchType": "singles",
  "maxItems": 200,
  "includeStats": false,
  "includePointByPoint": false,
  "includeH2H": false,
  "includeOdds": false,
  "onlyChanges": false
}
```

# Actor output Schema

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

One row per match: players, tournament, round, surface, status, set scores and tiebreaks, plus stats, point-by-point, head-to-head and odds when requested.

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

Date, tour, tournament, round, players, score and status.

## `stats` (type: `string`):

Aces, double faults, first-serve and break-point figures per match (needs includeStats).

## `csv` (type: `string`):

One flat row per match for spreadsheet exports.

# 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 = {
    "mode": "matches",
    "date": "today",
    "tours": [
        "ATP",
        "WTA"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("devarq/tennis-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 = {
    "mode": "matches",
    "date": "today",
    "tours": [
        "ATP",
        "WTA",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("devarq/tennis-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 '{
  "mode": "matches",
  "date": "today",
  "tours": [
    "ATP",
    "WTA"
  ]
}' |
apify call devarq/tennis-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,devarq/tennis-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/raa9S979ep9wcnLxe/builds/uBM9LNSB0UGcvzYOE/openapi.json
