# Tennis Scraper (`crawlstone/tennis-scraper`) Actor

Extract normalized tennis data from SofaScore and Tennis Abstract, including live matches, tournaments, point-by-point records, and player statistics.

- **URL**: https://apify.com/crawlstone/tennis-scraper.md
- **Developed by:** [Crawl Stone](https://apify.com/crawlstone) (community)
- **Categories:** Developer tools, Automation
- **Stats:** 6 total users, 3 monthly users, 94.4% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $6.00 / 1,000 successful results

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 a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
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.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js/docs.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python/docs.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/integrations/mcp.md).

If your project is in a different language, use 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 Scraper

We built Tennis Scraper to provide a straightforward, query-based way to collect clean tennis data. This Actor connects to **SofaScore** and **Tennis Abstract** to gather public data on-demand, resolving complex player-mapping logic internally so you can focus on data analysis.

Instead of writing custom browser automation or managing complex proxy rotation setups, you can use our pre-built modes to retrieve structured results on-demand.

> **Unofficial Actor:** This is an independent tool and is not affiliated with or endorsed by SofaScore or Tennis Abstract.

> **Looking for real-time WebSocket streaming?** This Actor is designed for run-based Dataset outputs. If you need a continuous WebSocket stream of live events for matches currently in progress rather than static snapshots, use our companion product: [SofaScore Tennis Data Stream](https://apify.com/crawlstone/sofascore-tennis-data-stream).

---

### What you can do with this Actor

We designed six focused ways into the data so you can run specific tennis-data jobs without getting bogged down in boilerplate code. You can daisy-chain these modes together using the IDs returned in your datasets.

#### 1. Track live matches and odds (`liveMatches`)

Run the scraper with no configuration to get a current snapshot of matches currently available in the active SofaScore live tennis feed. You get the current set, game, point, and tiebreak scores, along with date-level match odds.

#### 2. Discover scheduled tournaments (`tournaments`)

Pass a specific date in `YYYY-MM-DD` format to find tournaments scheduled on that day. This returns the unique tournament ID, surface type, and UTC event counts.

#### 3. Explore tournament history and draws (`tournamentSeasons` and `tournamentDetails`)

Take a unique tournament ID (like Wimbledon `2361`) to:

* Find available seasons (`tournamentSeasons`).
* Pull tournament metadata, historical champions, and a chronological list of matches for a specific season (`tournamentDetails`).

#### 4. Inspect match anatomy (`pointByPoint`)

Pass a unique match ID to extract the set, game, and point-by-point progression. It identifies who was serving, who won each point, and includes period-level match statistics.

#### 5. Deep-dive into historical player statistics (`playerDetails`)

Pass a SofaScore player ID (from your match results). The Actor matches it against our built-in player map, fetches their Tennis Abstract profile, and parses their performance splits. You get:

* Historical results, rankings, and Elo ratings.
* Surface-specific splits (clay, hard, grass).
* Serving, returning, and match charting statistics.

---

### Quick Start

1. Click **Try for free** on the Actor page.
2. Under **Scraper mode**, choose **Live matches**.
3. Click **Start**.
4. Open the **Dataset** tab when the run finishes.

For your first run, you can use the default live match mode with no extra input:

```json
{
  "mode": "liveMatches"
}
````

If no matches are live when you run the Actor, the run succeeds with an empty dataset.

***

### Data Structure & Examples

Each dataset row represents a single tennis record. The Apify Dataset stores nested arrays and objects, and JSON exports preserve this rich hierarchical structure. (Tabular exports like CSV or Excel will flatten or serialize these fields).

#### Live Match Snippet (`liveMatches`)

*This is an abridged preview of selected fields, not a complete schema-valid Dataset row.*

```json
{
  "id": 16385412,
  "slug": "coco-gauff-karolina-muchova",
  "status": "3rd set",
  "tournamentName": "Wimbledon",
  "homePlayerName": "Karolina Muchova",
  "awayPlayerName": "Coco Gauff",
  "score": {
    "home": 2,
    "away": 1,
    "sets": [
      { "set": 1, "home": 6, "away": 2, "durationSeconds": 2357 },
      { "set": 2, "home": 1, "away": 6, "durationSeconds": 2127 }
    ]
  },
  "odds": {
    "home": { "fractional": "3/1", "decimal": 4.0 },
    "away": { "fractional": "2/9", "decimal": 1.22 }
  }
}
```

#### Player Profile Snippet (`playerDetails`)

*This is an abridged preview of selected fields, not a complete schema-valid Dataset row.*

```json
{
  "id": 14882,
  "fullName": "Jannik Sinner",
  "country": "Italy",
  "handedness": "Right-handed",
  "currentRank": 1,
  "eloRank": 1,
  "eloRating": 2245,
  "recentResults": [
    {
      "date": "2026-07-09",
      "tournament": "Wimbledon",
      "surface": "Grass",
      "result": "W",
      "opponentName": "Alexander Zverev",
      "score": "6-4 6-3 6-2",
      "dominanceRatio": 1.45,
      "acePercentage": 11.2
    }
  ]
}
```

***

### Operational & Billing Details

#### Managed Proxies

To handle rate limits and source blocks, SofaScore requests on Apify are automatically routed through residential proxies. No user-side proxy configuration or credentials are required. Note that while this helps protect runs from immediate blocking, it does not guarantee uninterrupted source availability.

#### Pay-per-Event Billing

We utilize Apify's Pay Per Event (PPE) model, which charges you based on successful operations rather than compute time or row counts:

- **One Successful Result Event:** A successful run charges exactly one custom `successful-result` event, regardless of how many rows are returned.
- **Uncharged Scrape Failures:** If a scraping operation fails due to network or source errors, the Actor writes a diagnostic error row with relevant request context when available, before failing the run. These scrape failures do not charge the `successful-result` event.
- **Empty Successes:** If a run returns zero results because no matches are live or no tournaments are scheduled on your selected date, it is considered a valid successful operation and charges one `successful-result` event.

Check the Actor's **Pricing** tab for current rates.

***

### Use with Apify MCP

If you are integrating tennis data into AI workflows, you can run this Actor through Apify's hosted Model Context Protocol (MCP) server to connect directly to compatible AI clients (such as Claude Desktop):

```text
https://mcp.apify.com?tools=crawlstone/tennis-scraper
```

Once connected, you can run tennis-data queries using natural language through your AI assistant.

***

### Frequently Asked Questions

#### What should I do when a run returns zero rows?

This is normal. The scraper returns empty datasets when there are no live matches in progress (e.g., late at night between tournament days) or when no tournaments are scheduled for a given date. If you need regular updates, configure an Apify Schedule to run the Actor periodically.

#### Can I pass a Tennis Abstract URL directly?

No. Provide the positive SofaScore `playerId` found in match-bearing results (such as live matches or the nested `matches` array in tournament details). You cannot extract a player ID from generic tournament discovery rows. Our built-in player map automatically translates the SofaScore ID to find and parse the corresponding Tennis Abstract profile. Unmapped player IDs will return a descriptive error row and fail the run without charging the `successful-result` event.

#### Can I get continuous real-time updates?

No. This Actor is optimized for static, on-demand datasets. For live WebSocket streaming of active match events, use our companion product: [SofaScore Tennis Data Stream](https://apify.com/crawlstone/sofascore-tennis-data-stream).

***

### Support

If you need assistance, please open a ticket on this Actor's **Discussion** tab and include:

- Your Apify **Run ID**.
- The selected **Mode** and the **Input** values you provided.
- A description of what you expected versus what occurred.

Do not share your Apify API Token or other private credentials in support tickets.

# Actor input Schema

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

Select the data operation. The form preselects liveMatches. Known fields from another mode are accepted by the runtime and ignored; mode-specific requirements are checked when the run starts.

## `maxMatches` (type: `integer`):

Used only when Mode is liveMatches. Optional non-negative cap; omit it to return all current live matches. A value of 0 means no explicit cap.

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

Used only when Mode is tournaments. A real calendar date in YYYY-MM-DD format is required; this field discovers tournaments scheduled on that date.

## `matchId` (type: `integer`):

Used only when Mode is pointByPoint. A positive match ID is required; get it from a match returned by liveMatches or tournamentDetails.

## `tournamentId` (type: `integer`):

Used only when Mode is tournamentSeasons or tournamentDetails. A positive tournament ID is required; get it from tournaments or liveMatches.

## `seasonId` (type: `integer`):

Used only when Mode is tournamentDetails. Optional positive season ID; get it from tournamentSeasons, or omit it to resolve the newest season.

## `playerId` (type: `integer`):

Used only when Mode is playerDetails. A positive player ID is required; get it from match participants returned by liveMatches or tournamentDetails.

## Actor input object example

```json
{
  "mode": "liveMatches",
  "maxMatches": 10,
  "date": "2026-07-10",
  "matchId": 16385412,
  "tournamentId": 2361,
  "seasonId": 63966,
  "playerId": 206570
}
```

# Actor output Schema

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

The direct rows produced by the selected mode. Collection successes may produce zero or many rows, singleton successes produce one row, and failed scrape operations produce one error row when it can be saved.

# 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": "liveMatches"
};

// Run the Actor and wait for it to finish
const run = await client.actor("crawlstone/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": "liveMatches" }

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

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).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": "liveMatches"
}' |
apify call crawlstone/tennis-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=crawlstone/tennis-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Tennis Scraper",
        "description": "Extract normalized tennis data from SofaScore and Tennis Abstract, including live matches, tournaments, point-by-point records, and player statistics.",
        "version": "2.0",
        "x-build-id": "cSEqXzKU9l6K3YI3f"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/crawlstone~tennis-scraper/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-crawlstone-tennis-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/crawlstone~tennis-scraper/runs": {
            "post": {
                "operationId": "runs-sync-crawlstone-tennis-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/crawlstone~tennis-scraper/run-sync": {
            "post": {
                "operationId": "run-sync-crawlstone-tennis-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "required": [
                    "mode"
                ],
                "properties": {
                    "mode": {
                        "title": "Mode",
                        "enum": [
                            "liveMatches",
                            "tournaments",
                            "pointByPoint",
                            "tournamentSeasons",
                            "tournamentDetails",
                            "playerDetails"
                        ],
                        "type": "string",
                        "description": "Select the data operation. The form preselects liveMatches. Known fields from another mode are accepted by the runtime and ignored; mode-specific requirements are checked when the run starts."
                    },
                    "maxMatches": {
                        "title": "Maximum live matches",
                        "minimum": 0,
                        "type": "integer",
                        "description": "Used only when Mode is liveMatches. Optional non-negative cap; omit it to return all current live matches. A value of 0 means no explicit cap."
                    },
                    "date": {
                        "title": "Tournament date",
                        "pattern": "^(\\d{4})-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01])$",
                        "type": "string",
                        "description": "Used only when Mode is tournaments. A real calendar date in YYYY-MM-DD format is required; this field discovers tournaments scheduled on that date."
                    },
                    "matchId": {
                        "title": "Match ID",
                        "minimum": 1,
                        "type": "integer",
                        "description": "Used only when Mode is pointByPoint. A positive match ID is required; get it from a match returned by liveMatches or tournamentDetails."
                    },
                    "tournamentId": {
                        "title": "Tournament ID",
                        "minimum": 1,
                        "type": "integer",
                        "description": "Used only when Mode is tournamentSeasons or tournamentDetails. A positive tournament ID is required; get it from tournaments or liveMatches."
                    },
                    "seasonId": {
                        "title": "Season ID",
                        "minimum": 1,
                        "type": "integer",
                        "description": "Used only when Mode is tournamentDetails. Optional positive season ID; get it from tournamentSeasons, or omit it to resolve the newest season."
                    },
                    "playerId": {
                        "title": "Player ID",
                        "minimum": 1,
                        "type": "integer",
                        "description": "Used only when Mode is playerDetails. A positive player ID is required; get it from match participants returned by liveMatches or tournamentDetails."
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
