# TennisExplorer Scraper: Tennis Results & Betting Odds (`axlymxp/tennisexplorer-scraper`) Actor

Tennis results and betting odds from TennisExplorer: every ATP, WTA, Challenger, ITF and UTR match since 2005 with set scores, retired/walkover status and closing odds. Optional per-bookmaker odds with opening price and line movement, H2H, fixtures, player seasons, rankings.

- **URL**: https://apify.com/axlymxp/tennisexplorer-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.00 / 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

## TennisExplorer Scraper: Tennis Results & Betting Odds

Get every professional tennis match from [TennisExplorer](https://www.tennisexplorer.com): ATP, WTA, Challenger, ITF and
UTR, singles and doubles, **back to 2005**, with set and tiebreak scores, retired/walkover status, UTC start times and the
**average closing odds**. Turn on match details to add **odds from ~15 bookmakers** for match winner, over/under, Asian
handicap and correct score, each with its **opening price and full line movement**, plus head-to-head and player bios.

Built for tennis bettors and modellers who need historical results and odds for backtesting, tipster and odds-comparison
sites, and sports-data analysts. One run covers a full date range; no proxy or login needed.

### What you get

| Field | Description |
| --- | --- |
| `matchId`, `matchUrl` | TennisExplorer match id and link |
| `date`, `startTimeLocal`, `startTimeUtc` | Day (Central European calendar, as on the site), local time and UTC start |
| `matchType` | `atp-single`, `wta-single`, `atp-double`, `wta-double` |
| `tournament` | `name`, `slug`, `url`, `year`, `countryCode`, `level` (`tour`, `challenger`, `itf`, `utr`) |
| `player1`, `player2` | `name`, `slug`, `url`, `seed` (doubles: a list of two players) |
| `status` | `finished`, `retired`, `walkover`, `scheduled` |
| `winner` | `1` or `2` |
| `setsWon`, `sets`, `score` | Sets won, games per set with tiebreak points, score text `7-6(5), 6-4` |
| `odds` | Average odds for player 1 / player 2 over the listed bookmakers (closing odds for finished matches) |
| `h2h` | Head-to-head record before the match (fixtures) |

With **Include bookmaker odds history, H2H and player bios**:

| Field | Description |
| --- | --- |
| `round`, `surface` | e.g. `quarterfinal`, `clay` / `hard` / `indoors` / `grass` |
| `player1/2.rank`, `birthdate`, `heightCm`, `weightKg`, `plays`, `fullName` | Player bio at match time |
| `h2hRecord` | Wins each and the list of meetings with scores, surface and round (includes this match once it is played) |
| `bookmakerOdds.matchWinner[]` | Per bookmaker: current odds, `opening`, `openingTime`, `lastChangeTime`, `trend`, `best`, `history[]` (every move with UTC time) |
| `bookmakerOdds.overUnder[]` | Sets and games totals lines, over/under odds per bookmaker |
| `bookmakerOdds.asianHandicap[]` | Set and game handicaps per bookmaker |
| `bookmakerOdds.correctScore[]` | Correct score odds per bookmaker |
| `bookmakerOdds.summary` | Number of bookmakers, average, best and average opening odds per player |

**Players mode** returns one row per match of a player's season (date, tournament, surface, round, opponent, result,
score, odds for player and opponent) plus the player's profile (country, height, weight, birthdate, current and highest
rank). **Rankings mode** returns rank, movement, player, country and points for ATP/WTA singles, doubles or race,
for any published ranking date back to 1998.

### Use cases

- **Betting model backtests**: years of results with closing odds in one dataset; compare opening vs closing prices to
  measure line movement and closing-line value.
- **Odds monitoring**: schedule fixtures every hour to track pre-match line moves per bookmaker.
- **Tipster and media sites**: daily results, upcoming matches with H2H and odds.
- **Player and surface research**: full season match lists per player with surface and round.
- **Rankings history**: weekly ATP/WTA ranking snapshots for trend analysis.

### Input

| Field | Default | Notes |
| --- | --- | --- |
| `mode` | `matches` | `matches` (results by date range), `fixtures` (upcoming), `players`, `rankings` |
| `startDate`, `endDate` | yesterday | `YYYY-MM-DD` or `today`, `yesterday`, `tomorrow`, `-7`, `+1`; up to 366 days per run |
| `matchTypes` | ATP + WTA singles | add doubles if needed |
| `levels` | all | `tour`, `challenger`, `itf`, `utr` |
| `statuses` | all | `finished`, `retired`, `walkover`, `scheduled` |
| `onlyWithOdds` | false | skip matches without odds |
| `tournaments` | — | name filter, e.g. `Wimbledon` |
| `players` | — | names, slugs or URLs: filter (results/fixtures) or source (players mode) |
| `seasons` | current | players mode, e.g. `["2024", "2025"]` |
| `includeMatchDetails` | false | bookmaker odds history, H2H, bios (one extra page per match) |
| `oddsHistory` | true | false keeps only current + opening odds per bookmaker |
| `rankingTours`, `rankingKind`, `rankingDate`, `maxRank` | ATP+WTA, singles, latest, 100 | rankings mode |
| `maxItems` | 500 | cap on rows |

Example: all tour-level matches of Wimbledon 2025 with bookmaker odds

```json
{
  "mode": "matches",
  "startDate": "2025-06-30",
  "endDate": "2025-07-13",
  "matchTypes": ["atp-single", "wta-single"],
  "tournaments": ["Wimbledon"],
  "includeMatchDetails": true,
  "maxItems": 300
}
```

### Example output

```json
{
  "matchId": 3353241,
  "matchUrl": "https://www.tennisexplorer.com/match-detail/?id=3353241",
  "matchType": "atp-single",
  "date": "2026-10-20",
  "startTimeUtc": "2026-10-20T14:40:00Z",
  "tournament": {"name": "Almaty", "slug": "almaty", "year": 2026, "countryCode": "kz", "level": "tour"},
  "player1": {"name": "Khachanov K.", "slug": "khachanov", "seed": "5", "rank": 25, "birthdate": "1996-05-21", "heightCm": 198, "plays": "right"},
  "player2": {"name": "Sakamoto R.", "slug": "sakamoto-9c9d5", "seed": null, "rank": 124, "birthdate": "2006-06-24", "plays": "right"},
  "status": "finished",
  "winner": 1,
  "setsWon": {"player1": 2, "player2": 0},
  "sets": [{"player1": 6, "player2": 2, "player1Tiebreak": null, "player2Tiebreak": null},
           {"player1": 6, "player2": 4, "player1Tiebreak": null, "player2Tiebreak": null}],
  "score": "6-2, 6-4",
  "odds": {"player1": 1.36, "player2": 3.07},
  "round": "1. round",
  "surface": "indoors",
  "bookmakerOdds": {
    "matchWinner": [{
      "bookmaker": "10Bet",
      "player1": {"odds": 1.35, "opening": 1.48, "openingTime": "2026-10-18T17:22:00Z", "trend": "down",
                  "history": [{"time": "2026-10-18T17:22:00Z", "odds": 1.48}, {"time": "2026-10-19T06:21:00Z", "odds": 1.45}]},
      "player2": {"odds": 3.0, "opening": 2.55, "openingTime": "2026-10-18T17:22:00Z", "trend": "up"}
    }],
    "summary": {"bookmakers": 15, "player1": {"average": 1.361, "max": 1.39, "openingAverage": 1.437},
                "player2": {"average": 3.069, "max": 3.33, "openingAverage": 2.67}}
  }
}
```

### Scheduling and integrations

Schedule the actor in Apify (e.g. `mode: matches`, `startDate: yesterday` every morning, or `mode: fixtures` hourly) and
send results to Google Sheets, Make, Zapier, n8n, a webhook or your database through the Apify API. Interrupted runs
resume where they stopped and never duplicate matches.

### Use with AI assistants (MCP)

The actor works as a tool in Apify's MCP server, so Claude, ChatGPT or Cursor can call it ("get yesterday's ATP results
with closing odds") and receive the dataset directly.

### FAQ

**How far back does the data go?** Results to 2005. Spot checks found odds on every tour-level match back to 2008;
lower-level events (ITF, early years) often have none. Rankings to 1998.

**Which odds are in `odds`?** The site's average over the bookmakers it lists. For finished matches this is the
closing price. `bookmakerOdds` gives each bookmaker separately with opening price and every move.

**How are retired and walkover detected?** The site doesn't label them. A result with no games played is a walkover; a
result whose final set isn't complete (e.g. `3-6, 6-4, 4-0`) is a retirement.

**Exhibitions with short sets?** Formats with sets to 4 games (Next Gen Finals, Fast4) are marked finished when a
player wins 3 sets; a 2-set short-format match can show as `retired`.

**Which time zone are dates in?** `date` follows the site's calendar (Central European time). `startTimeUtc` and all odds
timestamps are UTC.

**Do I need a proxy?** No. The actor runs without proxy; you can still set one.

**Is it legal?** The actor reads public pages at a polite rate (about one page per second). Check TennisExplorer's terms
and your local rules for your use case, especially for commercial redistribution.

**Something broken or missing?** Open an issue on the Issues tab.

# Actor input Schema

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

<b>Results</b>: every match of a date range (archive back to 2005) with scores and closing odds. <b>Fixtures</b>: scheduled matches with pre-match odds and H2H. <b>Players</b>: a player's full season(s). <b>Rankings</b>: ATP/WTA rankings.

## `startDate` (type: `string`):

YYYY-MM-DD, or relative: today, yesterday, tomorrow, -7, +1. Days are Central European days (the site's calendar). Default: yesterday (results) / today (fixtures).

## `endDate` (type: `string`):

Last day, inclusive (same formats). Empty = start date only. Max 366 days per run.

## `matchTypes` (type: `array`):

ATP/WTA singles and doubles. Challenger, ITF and UTR events are included in each tour; narrow them with Levels.

## `levels` (type: `array`):

Keep only these tournament levels. Empty = all.

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

Keep only these statuses. Empty = all. Retired and walkover are derived from the score.

## `onlyWithOdds` (type: `boolean`):

Drop matches that have no bookmaker odds (common in low-level ITF events).

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

Keep only tournaments whose name contains one of these (e.g. Wimbledon, Almaty, challenger).

## `includeMatchDetails` (type: `boolean`):

+1 page per match: odds of ~15 bookmakers for match winner, over/under, Asian handicap and correct score with opening price and every line move, head-to-head, round, surface, player rank/age/height/hand. Charged as a separate 'match details' event.

## `oddsHistory` (type: `boolean`):

Off = only current and opening odds per bookmaker (much smaller rows).

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

Names (Jannik Sinner), TennisExplorer slugs (sinner-8b8e8) or player URLs. Players mode: whose matches to scrape. Results/fixtures mode: keep only their matches.

## `seasons` (type: `array`):

Players mode: years to scrape (e.g. 2024, 2025). Empty = current season.

## `playerMatchType` (type: `string`):

Players mode: singles or doubles matches.

## `rankingTours` (type: `array`):

Rankings mode.

## `rankingKind` (type: `string`):

Rankings mode.

## `rankingDate` (type: `string`):

YYYY-MM-DD of a published ranking (Mondays), back to 1998. Empty = latest.

## `maxRank` (type: `integer`):

Rankings mode: scrape positions 1..N.

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

Hard cap on dataset rows. You are charged per row.

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

Optional. TennisExplorer works without a proxy.

## Actor input object example

```json
{
  "mode": "matches",
  "startDate": "yesterday",
  "matchTypes": [
    "atp-single",
    "wta-single"
  ],
  "onlyWithOdds": false,
  "includeMatchDetails": false,
  "oddsHistory": true,
  "players": [],
  "playerMatchType": "singles",
  "rankingTours": [
    "atp-men",
    "wta-women"
  ],
  "rankingKind": "singles",
  "maxRank": 100,
  "maxItems": 50,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# 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",
    "startDate": "yesterday",
    "matchTypes": [
        "atp-single",
        "wta-single"
    ],
    "players": [],
    "maxItems": 50,
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("axlymxp/tennisexplorer-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",
    "startDate": "yesterday",
    "matchTypes": [
        "atp-single",
        "wta-single",
    ],
    "players": [],
    "maxItems": 50,
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("axlymxp/tennisexplorer-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",
  "startDate": "yesterday",
  "matchTypes": [
    "atp-single",
    "wta-single"
  ],
  "players": [],
  "maxItems": 50,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call axlymxp/tennisexplorer-scraper --silent --output-dataset

```

## MCP server setup

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