# Tennis Live Scores API 🎾 (ATP, WTA, ITF, set stats) (`tagadanar/tennis-live-scores`) Actor

Live, finished and upcoming tennis matches in one dataset: set-by-set scores with tiebreaks, who is serving, the current game score, surface and winner. ATP, WTA, Challenger, ITF, qualifying, singles and doubles, 7 days back and 2 ahead. Optional per-set serve and break-point statistics. No API key.

- **URL**: https://apify.com/tagadanar/tennis-live-scores.md
- **Developed by:** [Tagada Data](https://apify.com/tagadanar) (community)
- **Categories:** Developer tools, Automation, News
- **Stats:** 2 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Tennis Live Scores API 🎾 (ATP, WTA, ITF, set stats)

Every tennis match of the day in one table: who is playing, what the score is
right now, who is serving, who won, on what surface. ATP, WTA, Challenger, ITF
and qualifying, singles and doubles, from seven days back to two days ahead.

No API key, no account, no browser. A full day of tennis comes back in a couple
of seconds, and you can run it once for the results or every minute for a live
scoreboard.

### What it costs next to the alternatives

Tennis data is normally something you rent by the month. api-tennis.com starts
at $40 a month, Sportradar's tennis feed is enterprise pricing you have to ask
for, and both of them want you to sign a plan before you know whether the data
fits. Here you pay per match and stop whenever you like.

Prices below were read off each actor's own Store page on 17 August 2026.

| Actor | Price | What its own listing covers |
| --- | --- | --- |
| **This actor** | one flat price per match, statistics optional, platform usage included | Live, finished and scheduled matches, set-by-set with tiebreaks, server and current game point, surface, and a full match plus per-set statistics sheet |
| `crawlstone/tennis-scraper` | $8.00 per 1,000 results | SofaScore and Tennis Abstract: live matches, tournaments, point-by-point, player statistics |
| `parseforge/tennisexplorer-scraper` | $7.50 per 1,000 results | TennisExplorer results by date and tour, with odds |
| `extractify-labs/flashscore-tennis-matches` | $1.00 per 1,000 results | Flashscore match listings, set-by-set scores, player rankings |
| `sian.agency/tennis-point-by-point-scraper` | $75.00 per 1,000 matches | Point-by-point rallies plus match and set statistics |

The set-by-set statistics sheet is the part none of the $1 to $8 listings
advertise: aces, double faults, first and second serve percentages, break points
saved and break points converted, for the match and for each set separately, on
finished matches and on live ones. The other thing to know is `matchId`: it is stable, so
the row you polled while a match was live is the same row after it finishes.

### What you get

One row per match.

| Field | What it is |
| --- | --- |
| `matchId` | Stable id, the same across every run. Use it as a primary key. |
| `status` | `live`, `finished` or `scheduled` |
| `statusDetail` | `Set 2`, `Finished`, `Finished / retired`, `Scheduled` |
| `currentSet` | Which set is being played, on live matches |
| `startTime`, `endTime` | ISO 8601 UTC |
| `date` | The UTC day the match belongs to |
| `durationMinutes` | How long a finished match took |
| `tournament`, `tour`, `tournamentCategory` | `Cincinnati`, `ATP`, `ATP - Singles` |
| `tournamentCountry`, `surface` | `USA`, `hard` |
| `isDoubles`, `isQualification` | Flags, so you can filter the noise out |
| `homePlayer`, `awayPlayer` | `Alcaraz C.`, `Sinner J.` |
| `homePlayerSlug`, `awayPlayerSlug` | `alcaraz-carlos`, url-safe, stable |
| `homeCountry`, `awayCountry` | Country the player represents |
| `homeSets`, `awaySets` | Sets won |
| `sets` | Per set: games each, plus tiebreak points when there was a breaker |
| `score` | `6-3, 7-6(4)`, exactly how a scoreboard writes it |
| `winner` | `home`, `away`, or empty while the match is still open |
| `serving` | Who is serving, on live matches |
| `homeGamePoints`, `awayGamePoints` | The current game: `0`, `15`, `30`, `40`, `A` |
| `url`, `tournamentUrl` | Links back to the match and the event |

Tick "Add match statistics" and every played match also carries a full
statistics sheet, for the match and broken down set by set:

aces, double faults, first and second serve percentage, first and second serve
points won, break points saved, break points converted, first and second return
points won, winners, net points won, service points won, return points won,
total points won, service games won, return games won, total games won.

Statistics work on live matches too, so you can watch a serve percentage move
while the match is still going.

### What it is for

**Betting models and quant research.** Set-by-set scores with tiebreaks, plus
serve and break-point statistics per set, is the shape you need to fit anything.
Pull `last7days` once a week and you have a rolling history with the surface,
the tour and the qualifying flag attached.

**Live scoreboards and bots.** Set the status filter to `live` on its own and
schedule the actor: you get a small, cheap payload with the current set, the
game score and the server, and nothing else. That is the whole feed for a
Discord bot, a Telegram alert or a widget on a site.

**Fantasy tennis and pick'em games.** `tomorrow` and `next2days` give you the
order of play before it happens, with both players, the tournament and the
surface, so you can build a card and lock it.

**Media and content sites.** Results for the day, in one call, with the tour and
the country already parsed out. No scraping of results tables, no timezone
guessing: everything is UTC.

**Player and tournament tracking.** Put a surname in "Only these players" and
you get just that player's matches. Put `Challenger` in "Only these tournaments"
and you get the whole Challenger circuit and nothing else.

### Input examples

Today's tennis, everything:

```json
{
  "dates": ["today"]
}
```

A live scoreboard, the cheapest way to run this on a schedule:

```json
{
  "dates": ["today"],
  "matchStatus": ["live"]
}
```

The last week of ATP and WTA results with full statistics:

```json
{
  "dates": ["last7days"],
  "matchStatus": ["finished"],
  "tournaments": ["ATP", "WTA"],
  "includeStatistics": true,
  "maxResults": 2000
}
```

Tomorrow's order of play for one tournament:

```json
{
  "dates": ["tomorrow"],
  "matchStatus": ["scheduled"],
  "tournaments": ["US Open"]
}
```

One player, everything available:

```json
{
  "dates": ["all"],
  "players": ["Swiatek"],
  "includeStatistics": true
}
```

An exact date:

```json
{
  "dates": ["2026-08-31", "2026-09-01"],
  "matchStatus": ["finished"]
}
```

### How far back and forward can it go?

Seven days back and two days ahead of today, UTC. That is the window the source
publishes, and asking for a date outside it is reported in the log rather than
failing the run. If you need a longer history, run the actor on a schedule and
append to the same dataset: `matchId` is stable, so de-duplicating is trivial.

### FAQ

**Do I need an API key or a login?**
No. Nothing to sign up for, nothing to connect, nothing to rotate.

**How fast is it?**
A whole day of tennis, roughly 120 to 250 matches across every tour, takes about
a second. Ten days, close to 2,000 matches, takes about three.

**Does it cover Challenger and ITF, or only the main tour?**
Everything the source lists: ATP, WTA, Challenger, ITF Men, ITF Women, plus
qualifying draws, singles and doubles. Use `tournaments` and the
`isQualification` and `isDoubles` fields to narrow it down.

**Are doubles matches included?**
Yes, with both players in each pair in the `homePlayer` and `awayPlayer` fields
as the source writes them, and `isDoubles` set to true.

**Can I get live scores, point by point?**
You get the live set score, the current game score and who is serving, refreshed
every time you run. Point-by-point history is not published for most matches, so
it is not offered rather than being offered and empty half the time.

**Are betting odds included?**
No. The scores feed does not carry odds, and this actor does not pretend to
supply them.

**What is the round of the match?**
The source does not publish a round in its day-by-day scores feed, so this actor
does not invent one. Everything it does ship comes straight from the source.

**Why are some matches missing a `statusDetail`?**
Because the source used a status code whose meaning is not documented and not
provable from the score. Rather than guess between "walkover" and "cancelled",
the row carries an empty `statusDetail` and the raw `statusCode`, so you can
decide for yourself.

**Which timezone are the dates in?**
UTC, everywhere, on purpose. `2026-08-31` means the same day whoever runs it and
wherever they run it from. Convert to local time from `startTime` if you need to.

**Can I run this on a schedule?**
Yes, and it is designed for it. Set the status filter to `live` and schedule it
every minute or two for a scoreboard, or run it once a night with
`["yesterday"]` to archive results.

**What happens if I ask for a player who is not playing?**
Nothing is charged beyond the run start, the run finishes successfully, and the
status message tells you no match matched.

### Notes on data

Player names come through as the source writes them, `Alcaraz C.` rather than
`Carlos Alcaraz`. The `homePlayerSlug` field carries the url-safe long form
(`alcaraz-carlos`), which is the better join key if you are merging with another
dataset. Scores, sets and tiebreaks are numbers, not strings, so they load
straight into a dataframe. Anything the source did not publish comes back empty
rather than as a zero or a guess.

***

Keywords: tennis scraper, tennis live scores, tennis results API, ATP scraper,
WTA scraper, tennis scores API, live tennis data, tennis match results, tennis
statistics scraper, ATP results, WTA results, Challenger tennis results, ITF
results, tennis fixtures, order of play, tennis odds data alternative, tennis
betting data, tennis dataset, live score API, tennis match statistics, aces and
double faults data, tennis scores JSON, tennis CSV export.

# Actor input Schema

## `dates` (type: `array`):

Which days to pull, one per entry. Use a keyword (<code>today</code>, <code>yesterday</code>, <code>tomorrow</code>, <code>last7days</code>, <code>next2days</code>, <code>all</code>) or an exact date such as <code>2026-08-31</code>. Days are UTC days. Flashscore publishes a rolling window of 7 days back and 2 days ahead, so anything outside that is reported and skipped.

## `matchStatus` (type: `array`):

Keep only matches in these states. Leave every option ticked to get the full day. Picking <code>live</code> alone is the cheap way to run this on a schedule as a live-score poller.

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

Optional. Keep only matches whose tournament, tour or host country contains one of these words. <code>ATP</code> keeps the men's main tour, <code>WTA</code> the women's, <code>Cincinnati</code> one event, <code>Challenger</code> the whole Challenger circuit. Leave empty for everything, including ITF and qualifying.

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

Optional. Keep only matches involving one of these players. Flashscore writes names as <code>Alcaraz C.</code>, but a surname on its own works: <code>Alcaraz</code>, <code>Swiatek</code>, <code>Sinner</code>.

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

Fetch the full statistics sheet for every played match: aces, double faults, first and second serve percentages, break points saved and converted, winners, service and return points won, and the same breakdown per set. Costs one extra request per match, and you are only charged for matches where statistics were actually published. Matches that have not started are skipped for free.

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

Total matches to return across every selected day. A busy day of tennis is roughly 120 to 250 matches across ATP, WTA, Challenger and ITF.

## Actor input object example

```json
{
  "dates": [
    "today"
  ],
  "matchStatus": [
    "live",
    "finished",
    "scheduled"
  ],
  "includeStatistics": false,
  "maxResults": 20
}
```

# Actor output Schema

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

One row per tennis match in the default dataset.

# 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 = {
    "dates": [
        "today"
    ],
    "matchStatus": [
        "live",
        "finished",
        "scheduled"
    ],
    "includeStatistics": false,
    "maxResults": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("tagadanar/tennis-live-scores").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 = {
    "dates": ["today"],
    "matchStatus": [
        "live",
        "finished",
        "scheduled",
    ],
    "includeStatistics": False,
    "maxResults": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("tagadanar/tennis-live-scores").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 '{
  "dates": [
    "today"
  ],
  "matchStatus": [
    "live",
    "finished",
    "scheduled"
  ],
  "includeStatistics": false,
  "maxResults": 20
}' |
apify call tagadanar/tennis-live-scores --silent --output-dataset

```

## MCP server setup

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

```

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/ZdTIBG7vcvdBYjlRv/builds/RiHsMhBu6UOfFPKzY/openapi.json
