# Tennis Explorer Stats Scraper (`fabri.ce/tennis-stats-scraper`) Actor

Get ATP and WTA match results, upcoming fixtures with odds, rankings, and player profiles by name, not an internal ID. No login needed. Built for fantasy tennis players, bettors, analysts, and journalists who need clean, accurate tennis data fast.

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

## Pricing

from $5.46 / 1,000 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/actors/running/actors-in-store.md#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 Stats Scraper

Get ATP and WTA match results, upcoming fixtures, betting odds, rankings and full player
profiles. Search by player name, not an internal ID. No login, no API key.

### Who uses it?

| Who | Why |
|---|---|
| Fantasy tennis players | Fresh results and player form to set a lineup. |
| Bettors | Odds alongside results to backtest a model, plus pre-match odds on upcoming fixtures. |
| Analysts | Rankings, head to head records and match detail for building historical datasets. |
| Journalists | Daily results and player bios to write up quickly, without digging through a site by hand. |

### What it does

- **Match results**, by date or date range, filtered by tour (ATP or WTA, singles or doubles). Every row has the score set by set, who won, and the odds that were listed for both players.
- **Upcoming fixtures**, the same way, for matches that have not been played yet, with pre-match odds where the market is already open.
- **Rankings**, ATP and WTA, current or from a past year.
- **Player profiles**, searched by name: country, height, weight, age, plays which hand, and current plus career-best rank in both singles and doubles.
- An optional **match detail** add-on for Match results and Upcoming fixtures: the court surface, the round, the head to head record between the two players, and average plus best odds across every bookmaker tracked. This is the one thing none of the other tennis scrapers on Apify pull together in a single run.

### What data you get

| Mode | Row shape |
|---|---|
| Match results | Tournament, date, time, both players, score, winner, odds. With match detail: surface, round, head to head, aggregated odds. |
| Upcoming fixtures | Same shape as Match results, before the match is played: no score or winner yet. |
| Rankings | Rank, move since last update, player, country, points, tour, year. |
| Player profiles | Name, country, height, weight, age, plays, current and career-best rank in singles and doubles, photo. |

### Use cases

**A daily results feed.**

```json
{ "mode": "results", "date": "yesterday", "tour": "atp-single" }
```

**Betting research with odds aggregated across every bookmaker tracked.**

```json
{ "mode": "results", "date": "2026-08-09", "tour": "atp-single", "includeMatchDetail": true }
```

Every row also carries `best_odds_player1` / `best_odds_player2` (the best price found across every bookmaker tracked) and `avg_odds_player1` / `avg_odds_player2` (the average across all of them), not just one bookmaker's number.

**Tomorrow's card with pre-match odds.**

```json
{ "mode": "upcoming", "date": "today", "tour": "wta-single" }
```

**A player's current form.**

```json
{ "mode": "players", "playerNames": ["Carlos Alcaraz", "Iga Swiatek"] }
```

**Where a player stood at a specific point in history.**

```json
{ "mode": "rankings", "rankingTour": "atp-men", "rankingYear": 2020 }
```

### How the input is organised

| Section | Applies to | Key fields |
|---|---|---|
| Mode | Every run | `mode`: results, upcoming, rankings, or players. |
| Match results and Upcoming fixtures | `results` and `upcoming` | `date` (or `dateFrom`/`dateTo`), `tour`, `includeMatchDetail`. |
| Rankings | `rankings` | `rankingTour`, `rankingYear` (optional, leave empty for the current table). |
| Player profiles | `players` | `playerNames` (searched by name) or `playerIds` (a known ID from a previous run). |

`maxItems` applies to every mode and caps how many rows the run writes.

### Limits and responsible use

- Match statistics like aces or double faults are not tracked by this Actor. The source does not consistently publish them for every match, especially lower-tier events, so a field that is empty most of the time was left out rather than shipped half-working.
- Match detail's odds are a summary (best price and average across tracked bookmakers), not a full line-movement history for every bookmaker. That level of detail is a lot of nested data for a use case almost nobody needs; if you do need it, say so and it can be added.
- Odds are whatever was listed at scrape time. Pre-match odds move right up until a match starts, and this Actor does not guarantee it caught the final price.
- This Actor reads only public pages. It respects reasonable request rates and does not require or store any login.

### Local development

```bash
bun install
bun test              # unit tests against saved sample pages
bun run src/main.ts   # local run, reads storage/key_value_stores/default/INPUT.json
```

# Actor input Schema

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

Caps how many rows this run writes to the dataset. Applies to every mode below.

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

Match results and Upcoming fixtures both need a date. Rankings and Player profiles do not.

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

A single day as YYYY-MM-DD, or the words today or yesterday. If left empty, Match results defaults to yesterday and Upcoming fixtures defaults to today.

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

First day of a range, YYYY-MM-DD (or today/yesterday). Every day through Date to is collected. Leave Date empty when using a range.

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

Last day of the range, YYYY-MM-DD (or today/yesterday). If you only set Date from, that single day is collected.

## `tour` (type: `string`):

Which draw to pull matches from.

## `includeMatchDetail` (type: `boolean`):

Fetches one extra page per match for the court surface, the round, the head to head record between the two players, and average plus best odds across every bookmaker tracked. Costs one extra request per match, so it takes longer on a large run. Off returns only what is already on the results or upcoming list: score, sets, and the single listed price for each player.

## `rankingTour` (type: `string`):

Which ranking table to pull.

## `rankingYear` (type: `integer`):

Leave empty for the current ranking table, or set a past year (e.g. 2020) to get the archived table as it stood that year.

## `playerNames` (type: `array`):

Full player names, searched by name so you never need to know an internal player ID. Matches ATP and WTA, active and retired players.

## `playerIds` (type: `array`):

A player ID from a previous run's player\_id field (e.g. alcaraz-5ab70), for when you already have one and want to skip the name search.

## Actor input object example

```json
{
  "maxItems": 10,
  "mode": "results",
  "date": "yesterday",
  "tour": "atp-single",
  "includeMatchDetail": true,
  "rankingTour": "atp-men",
  "playerNames": [
    "Carlos Alcaraz",
    "Jannik Sinner",
    "Novak Djokovic",
    "Aryna Sabalenka",
    "Coco Gauff"
  ]
}
```

# Actor output Schema

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

One row per match, ranking entry, or player profile, depending on the selected mode.

# 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 = {
    "maxItems": 10,
    "mode": "results",
    "date": "yesterday",
    "tour": "atp-single",
    "includeMatchDetail": true,
    "rankingTour": "atp-men",
    "playerNames": [
        "Carlos Alcaraz",
        "Jannik Sinner",
        "Novak Djokovic",
        "Aryna Sabalenka",
        "Coco Gauff"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("fabri.ce/tennis-stats-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 = {
    "maxItems": 10,
    "mode": "results",
    "date": "yesterday",
    "tour": "atp-single",
    "includeMatchDetail": True,
    "rankingTour": "atp-men",
    "playerNames": [
        "Carlos Alcaraz",
        "Jannik Sinner",
        "Novak Djokovic",
        "Aryna Sabalenka",
        "Coco Gauff",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("fabri.ce/tennis-stats-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 '{
  "maxItems": 10,
  "mode": "results",
  "date": "yesterday",
  "tour": "atp-single",
  "includeMatchDetail": true,
  "rankingTour": "atp-men",
  "playerNames": [
    "Carlos Alcaraz",
    "Jannik Sinner",
    "Novak Djokovic",
    "Aryna Sabalenka",
    "Coco Gauff"
  ]
}' |
apify call fabri.ce/tennis-stats-scraper --silent --output-dataset

```

## MCP server setup

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