# SofaScore Tennis Scraper: Live Scores, Stats & Odds (`axlymxp/sofascore-tennis-scraper`) Actor

Tennis data from SofaScore for betting models and analysts: every ATP, WTA, Challenger and ITF match by day, live, tournament or player, with set and tiebreak scores, rankings and surface. Optional serve stats, point-by-point and odds. Pay only for results.

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

## Pricing

from $1.50 / 1,000 dataset items

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

## Tennis Scraper — Live Scores, Point-by-Point, Stats & Odds (Sofascore)

Get structured tennis data from Sofascore: every ATP, WTA, Challenger, ITF and UTR match of a day, live scores,
tournament draws, a player's results and upcoming matches. Each match comes with set and tie-break scores, surface,
round and both players' ATP/WTA rankings. Add match statistics, point-by-point, bookmaker odds and momentum when you
need them. It also exports ATP and WTA rankings and player profiles.

Built for betting and trading models, tipsters, tennis analysts, journalists and dashboard or fantasy apps that need
clean, schedulable tennis data without maintaining their own scraper.

### What you get

| Field | Description |
| --- | --- |
| `matchId`, `url` | Sofascore match id and link |
| `status`, `statusDescription` | `notstarted`, `inprogress`, `finished`, `interrupted`, `postponed`, `canceled` + label (e.g. "2nd set", "Ended", "Retired") |
| `startTime` | Scheduled start, UTC ISO-8601 |
| `tour`, `tournament`, `tournamentId`, `season`, `round` | ATP / WTA / Challenger / ITF Men / ITF Women / UTR …, event name, round ("Round of 16", "Final") |
| `surface` | Hardcourt indoor/outdoor, Clay, Red clay, Grass, Carpet … |
| `matchType` | `singles` or `doubles` (doubles rows list both partners) |
| `player1`, `player2` | `id, name, shortName, country, ranking (ATP/WTA, top 500), seed, url, partners` |
| `setsWon`, `sets` | Sets won, and each set's games with tie-break points: `[{"set":2,"p1":7,"p2":6,"tiebreak":[9,7]}]` |
| `winner`, `firstToServe` | `player1` / `player2` |
| `currentGameScore` | Live point score of the current game, e.g. `["40","15"]` |
| `durationSeconds` | Match length (tournament, player and match-URL sources; not in day lists) |
| `statistics` | Optional. Per match and per set: aces, double faults, 1st/2nd serve %, 1st/2nd serve points won, break points saved/converted, service/return games, max points in a row, tiebreaks … |
| `pointByPoint` | Optional. Every game in order: server, game winner, break flag, game score, point sequence `["15-0","30-0","30-15","40-15"]` |
| `odds` | Optional. Markets such as Full time, First set winner, Total games: decimal and fractional, opening and current price, winning flag |
| `momentum` | Optional. Sofascore "tennis power" value per game |
| `scrapedAt` | When the row was collected (UTC) |

**Rankings mode** rows: `rankingType, rank, previousRank, bestRank, points, previousPoints, tournamentsPlayed,
playerId, name, country, updatedAt, url`.
**Players mode** rows: `name, fullName, country, ranking, tour, birthDate, birthplace, residence, heightM,
weightKg, plays, turnedPro, prizeMoneyCurrentYear, prizeMoneyTotal, prizeCurrency, url`.

### Use cases

- **Betting and trading models:** poll live matches every few minutes and store point-by-point, momentum and odds
  movement (opening vs current) for in-play models and backtests.
- **Pre-match analysis:** tomorrow's ATP/WTA fixtures with rankings, surface and odds; a player's recent form from
  their last results with full serve/return statistics.
- **Research and journalism:** a whole tournament's results with statistics; weekly ATP/WTA ranking snapshots for
  movement charts.
- **Apps and dashboards:** scheduled fixtures and live scores for websites, Discord/Telegram bots and fantasy
  games.

### Input

| Field | Description |
| --- | --- |
| `mode` | `matches` (default), `rankings` or `players` |
| `dates` | UTC days: `2026-10-21`, `today`, `yesterday`, `tomorrow`, `+2`, `-3`. Defaults to today when no other source is given |
| `liveOnly` | Only matches in progress right now (a single request, ideal for frequent schedules) |
| `tournaments` | Tournament URLs, ids or names (`Wimbledon`) → current season's fixtures and results |
| `players` | Player URLs, ids or names (`Jannik Sinner`) → recent results and upcoming matches, or profiles in `players` mode |
| `matchIds` | Match URLs or ids |
| `tours`, `matchType`, `statuses` | Filters: tours (ATP, WTA, Challenger, ITF Men/Women, UTR Men/Women, Other), singles/doubles, match status |
| `includeStatistics`, `includePointByPoint`, `includeOdds`, `includeMomentum` | Per-match details (statistics, point-by-point and momentum only exist once a match has started) |
| `rankings`, `rankingsLimit` | `rankings` mode: `atp` / `wta`, top N (max 500) |
| `maxItems` | Maximum rows |
| `proxyConfiguration` | Leave the default: the actor picks working Apify Proxy IPs by itself |

#### Example: yesterday's ATP and WTA results with statistics and point-by-point

```json
{
  "dates": ["yesterday"],
  "tours": ["ATP", "WTA"],
  "statuses": ["finished"],
  "matchType": "singles",
  "includeStatistics": true,
  "includePointByPoint": true,
  "includeOdds": true,
  "maxItems": 100
}
```

#### Example: live scores every 5 minutes

```json
{ "liveOnly": true, "tours": ["ATP", "WTA", "Challenger"], "includeOdds": true, "maxItems": 100 }
```

#### Example output (shortened)

```json
{
  "matchId": 17331293,
  "url": "https://www.sofascore.com/tennis/match/luciano-darderi-james-duckworth/lUpsJlyc#id:17331293",
  "status": "finished",
  "startTime": "2026-10-21T06:30:00Z",
  "tour": "ATP",
  "tournament": "Almaty, Kazakhstan",
  "round": "Round of 16",
  "surface": "Hardcourt indoor",
  "matchType": "singles",
  "player1": {"id": 39711, "name": "James Duckworth", "country": "AU", "ranking": 73, "seed": null},
  "player2": {"id": 308084, "name": "Luciano Darderi", "country": "IT", "ranking": 23, "seed": "2"},
  "winner": "player1",
  "setsWon": [2, 0],
  "sets": [{"set": 1, "p1": 7, "p2": 5}, {"set": 2, "p1": 7, "p2": 6, "tiebreak": [9, 7]}],
  "durationSeconds": 6328,
  "statistics": {"all": {"aces": {"name": "Aces", "p1": 17, "p2": 6},
                         "firstServeAccuracy": {"name": "First serve", "p1": "50/78 (64%)", "p2": "55/79 (70%)",
                                                "p1Value": 50, "p1Total": 78, "p2Value": 55, "p2Total": 79}}},
  "pointByPoint": [{"set": 1, "game": 1, "server": "player1", "winner": "player1", "break": false,
                    "score": [1, 0], "points": ["15-0", "30-0", "30-15", "40-15"]}],
  "odds": [{"market": "Full time", "period": "Match", "choices": [
            {"name": "1", "side": "player1", "decimal": 2.0, "initialDecimal": 2.3, "winning": true},
            {"name": "2", "side": "player2", "decimal": 1.8, "initialDecimal": 1.62, "winning": false}]}],
  "scrapedAt": "2026-10-22T08:00:00Z"
}
```

### Scheduling and integrations

Create a **Schedule** in Apify Console (for example every 5 minutes with `liveOnly`, or daily at 06:00 UTC with
`dates: ["yesterday"]`). Read results through the Apify API (`/v2/datasets/{id}/items?format=json|csv|xlsx`) or
send them on with webhooks and integrations (Google Sheets, Slack, Make, Zapier, Airbyte).

### Use it from AI assistants

The actor works as a tool in AI agents through the Apify MCP server (`https://mcp.apify.com`): ask for "yesterday's
ATP results with statistics" or "Sinner's last 10 matches" and the agent can run it and read the dataset.

### FAQ

**Which matches are covered?** Everything Sofascore lists for tennis: ATP, WTA, Challenger, ITF, UTR, Davis Cup,
Billie Jean King Cup, exhibitions, singles and doubles. A busy day has 500+ matches.

**How fresh is the data?** Each run reads Sofascore at run time. Live rows show the score at that moment, so
schedule the actor for continuous tracking.

**Why are statistics or point-by-point empty for some matches?** They only exist once a match has started, and
Sofascore does not provide them for every lower-tier (ITF/UTR) match. Odds also exist before the start.

**Why is a ranking empty?** Rankings come from the ATP/WTA top 500 singles lists. Doubles players, UTR/ITF players
outside the top 500 and juniors have `ranking: null`.

**Do I need a proxy?** No setup needed. The default Apify Proxy setting is enough; the actor switches IPs by itself
when one is blocked.

**Is this legal?** The actor collects publicly visible sports results and statistics. You are responsible for how
you use the data and for complying with Sofascore's terms in your jurisdiction.

**Something is wrong or missing?** Open an issue on the actor page. Issues are read and answered.

# Actor input Schema

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

Matches (by day, live, tournament, player or match), ATP/WTA rankings, or player profiles.

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

UTC days to collect every match of: YYYY-MM-DD or relative values today, yesterday, tomorrow, +2, -3. Used when no live/tournament/player/match source is given, defaults to today.

## `liveOnly` (type: `boolean`):

Only matches in progress right now (one request, ideal for frequent schedules). Other sources are ignored.

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

Sofascore tournament URLs, ids or names (e.g. 'Wimbledon', https://www.sofascore.com/tennis/tournament/atp/wimbledon/2361). Returns the current season's fixtures and results.

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

Player URLs, ids or names (e.g. 'Jannik Sinner', https://www.sofascore.com/tennis/player/sinner-jannik/206570). Matches mode: their recent results and upcoming matches. Players mode: their profiles.

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

Specific matches, e.g. https://www.sofascore.com/tennis/match/sinner-alcaraz/abc#id:12345678 or 12345678.

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

Keep only these tours. Empty = all.

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

Keep singles, doubles or both.

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

Keep only these statuses. Empty = all.

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

Aces, double faults, 1st/2nd serve %, break points, return points, tiebreaks... per match and per set.

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

Every game in order with server, winner, break flag, game score and the point sequence.

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

Bookmaker odds (decimal and fractional, opening and current): match winner, first set winner, total games...

## `includeMomentum` (type: `boolean`):

Sofascore 'tennis power' momentum value per game.

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

Rankings mode: which rankings to return.

## `rankingsLimit` (type: `integer`):

How many ranked players per ranking (max 500). 'Max rows' also applies.

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

Maximum number of rows to return. You are charged per row.

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

Default (Apify Proxy, automatic) tries datacenter IPs and switches to residential when Sofascore challenges them. Pick a group only if you want to force it.

## Actor input object example

```json
{
  "mode": "matches",
  "dates": [
    "today"
  ],
  "liveOnly": false,
  "matchType": "all",
  "includeStatistics": false,
  "includePointByPoint": false,
  "includeOdds": false,
  "includeMomentum": false,
  "rankings": [
    "atp",
    "wta"
  ],
  "rankingsLimit": 100,
  "maxItems": 20,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `results` (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 = {
    "mode": "matches",
    "dates": [
        "today"
    ],
    "maxItems": 20,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("axlymxp/sofascore-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",
    "dates": ["today"],
    "maxItems": 20,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("axlymxp/sofascore-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",
  "dates": [
    "today"
  ],
  "maxItems": 20,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call axlymxp/sofascore-tennis-scraper --silent --output-dataset

```

## MCP server setup

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