# Tennis Scraper: Live Scores, Results & Odds from 40+ Books (`sports-odds-lab/tennis-odds-results`) Actor

Every ATP, WTA, Challenger and ITF match: live scores, results with tiebreaks, current rankings, opening and closing odds from up to 42 bookmakers, no-vig consensus and line movement. Optional serve stats and point-by-point. Recent matches or full tournament history with odds back to 2009.

- **URL**: https://apify.com/sports-odds-lab/tennis-odds-results.md
- **Developed by:** [Sports Odds Lab](https://apify.com/sports-odds-lab) (community)
- **Categories:** Developer tools, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-usage

## 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, Results & Odds from 40+ Bookmakers

> 🎁 **Free until 8 October 2026.** Press **Start** — the prefilled input returns 30 matches with odds in under a minute.

![Sample output: Wimbledon 2024 with fair odds, line movement and best prices](https://api.apify.com/v2/key-value-stores/CEtWcytR8Vm5hiMr1/records/preview-tennis.png?signature=e2aorbcAu90eMd1cVme6)

Get **every ATP, WTA, Challenger and ITF match** with **opening and closing match-winner odds from up to 42 bookmakers**, a **no-vig market consensus**, **line movement**, **live scores** (server and points of the current game), **current ATP/WTA rankings**, set scores with **tiebreaks**, optional **serve/return statistics** and optional **point-by-point** progression — one clean row per match, ready for a spreadsheet, a database or a model.

Most tennis scrapers give you scores. Most odds scrapers cover US sports. This Actor joins the two for tennis, including the **ITF and Challenger levels** where historical odds are hardest to find.

**Two modes:**

- **Recent matches** — every tour, from 7 days back to 7 days ahead (upcoming matches with current prices)
- **Tournament history** — complete past editions of any tournament (qualifying included), with **odds back to 2009**; `ATP: all` covers every ATP tournament

### What you get for each match

- **Match**: start time (UTC), tour (ATP / WTA / Challenger / ITF / team events), singles or doubles, tournament, surface, players and their countries, Flashscore IDs and link
- **Result**: status (finished, retired, walkover, cancelled, live, scheduled), set-by-set score with tiebreak points, sets won, winner, end time
- **Live**: for matches in progress, who is serving and the points of the current game (`live`) — run it every few minutes for a live scoreboard
- **Rankings**: current ATP / WTA singles ranking of both players (`player1Rank`, `player2Rank`), recent matches only
- **Odds per bookmaker**: opening price and latest price for both players, plus the bookmaker margin. Once a match has started the latest price is the **closing line**
- **Market consensus** (median across bookmakers, margin removed):
  - fair opening and closing odds for both players
  - win probability at the open and at the close
  - **line movement** in percentage points
  - best available odds at the open and at the close
  - median bookmaker margin
- **Statistics** *(optional)*: aces, double faults, first-serve %, first/second serve points won, break points saved/converted, return points won, service and return games won and more
- **Point-by-point** *(optional)*: every game — server, winner, break or hold, and the score before each point with **break, set and match points** marked — plus every tiebreak point, for finished and live matches

### Tournament history: odds back to 2009

List tournaments and years and get every match of those editions — main draw and qualifying — with the opening and closing odds each bookmaker published at the time.

| Edition | Bookmakers per match (examples) |
|---|---|
| 2009 – 2013 | 7–9 (Wimbledon ATP and WTA) · 6–7 (Challenger Genova) |
| 2015 – 2019 | 8–10 (Wimbledon, French Open WTA, Challenger Genova) |
| 2023 | up to 10 (ITF M15 Monastir) |
| 2024 and later | 17–18 (Wimbledon) |

Tournaments are named as on Flashscore: `ATP: Wimbledon`, `WTA: French Open` (Roland Garros), `ATP: Indian Wells`, `Challenger Men: Genova`, `ITF Women: W15 Monastir` — or paste the tournament's Flashscore URL. `ATP: all` expands to every ATP tournament. Years can be single (`2024`) or ranges (`2015-2024`).

A Grand Slam edition (239 matches with qualifying) takes about 2 minutes. For many tournaments and years, raise the run timeout (the default is 6 hours).

### Coverage

Measured on a sample of 250 matches (all levels, singles and doubles):

| Level | Share of matches with odds | Bookmakers per match (avg.) |
|---|---|---|
| ATP singles | 100% | ~35 |
| WTA singles | 100% | ~37 |
| Challenger singles | 100% | ~36 |
| ITF singles | 98% | ~20 |
| Doubles (all levels) | 93–100% | 10–36 |

Bookmakers include 1xBet, 7BetUK, Betway, bwin, BetMGM, Betclic, Betfred, Betano, bet365, DraftKings, FanDuel, Fanatics, Interwetten, Ladbrokes, Midnite, Neobet, Skybet, Stake, STS, Superbet, Winamax and more (up to 42 listings, depending on the match). Brands that appear under several licences (Betano .br / .ca / .de / .dk, BetMGM .uk / .us…) are counted **once** in the consensus (`brandCount`), so they cannot outvote the rest.

### Use cases

- **Build and backtest tennis models** — train on closing-line probabilities, the most efficient price the market produces
- **Closing Line Value (CLV)** — compare the price you bet with the consensus close to measure whether you beat the market
- **Line movement and steam** — find matches where the market moved sharply between open and close
- **Market research** — compare bookmaker margins, find the best price, study how efficient ITF markets are
- **Live dashboards and alerts** — poll live matches (`statuses: ["live"]`) for the score, the server and the current game
- **Point-by-point models** — hold/break rates, pressure points (break, set and match points) and tiebreak performance per player
- **Your own odds history** — schedule the Actor daily and keep appending to a dataset; after a few weeks you own a history nobody else sells for these levels

### How to use it

1. Pick the days (up to 7 back and 7 ahead), tours, singles/doubles and statuses. With **Max matches** set, the most important matches are kept first (ATP and WTA, then Challenger, team events and ITF).
2. Click **Start**. A typical day of ATP + WTA + Challenger finishes in about a minute; a full day including ITF in a few minutes.
3. Download the results as JSON, CSV or Excel, or read them through the Apify API.

**Tip — build a history automatically:** create a [Schedule](https://docs.apify.com/platform/schedules) that runs every morning with `daysBack: 1`, `daysAhead: 0`, `statuses: ["finished"]`. Each run adds yesterday's matches with their closing lines.

### Ready-made examples

One click, preconfigured — open, press **Start**, adjust if you like:

- [Wimbledon odds history since 2009](https://apify.com/sports-odds-lab/tennis-odds-results/examples/wimbledon-odds-history)
- [Grand Slam odds history (ATP and WTA)](https://apify.com/sports-odds-lab/tennis-odds-results/examples/grand-slam-odds-history)
- [Every ATP tour match of a season with closing odds](https://apify.com/sports-odds-lab/tennis-odds-results/examples/atp-tour-closing-odds-full-season)
- [Yesterday's tennis results with closing odds](https://apify.com/sports-odds-lab/tennis-odds-results/examples/yesterday-tennis-closing-odds)
- [Today's tennis matches with odds from 40+ bookmakers](https://apify.com/sports-odds-lab/tennis-odds-results/examples/todays-tennis-odds-compare)
- [ITF and Challenger odds with results](https://apify.com/sports-odds-lab/tennis-odds-results/examples/itf-challenger-odds)

#### Input example — recent matches

```json
{
  "daysBack": 1,
  "daysAhead": 0,
  "tours": ["ATP", "WTA", "CHALLENGER"],
  "formats": ["SINGLES"],
  "statuses": ["finished"],
  "includeOdds": true,
  "includeStats": true,
  "onlyWithOdds": true
}
```

#### Input example — tournament history

```json
{
  "mode": "tournaments",
  "tournaments": ["ATP: Wimbledon", "WTA: Wimbledon", "ATP: Indian Wells"],
  "years": ["2015-2024"],
  "includeQualifying": false,
  "includeStats": true
}
```

#### Output example (shortened)

```json
{
  "matchId": "8079sqDc",
  "startTime": "2026-09-23T03:05:00Z",
  "tour": "WTA",
  "format": "SINGLES",
  "tournament": "Seoul (South Korea)",
  "surface": "hard",
  "player1": "Tararudee L.",
  "player2": "Park S.",
  "player1Rank": 92,
  "player2Rank": 187,
  "status": "finished",
  "score": "7-6 6-0",
  "sets": [[7, 6], [6, 0]],
  "tiebreaks": [[7, 4], null],
  "winner": 1,
  "endTime": "2026-09-23T04:41:12Z",
  "live": null,
  "consensus": {
    "bookmakerCount": 39,
    "consensusOpeningProb1": 0.8283,
    "consensusLastProb1": 0.8393,
    "fairLastOdds1": 1.192,
    "fairLastOdds2": 6.221,
    "probMovementPts": 1.09,
    "bestLastOdds1": 1.14,
    "bestLastOdds2": 7.2,
    "medianMarginPct": 6.09
  },
  "odds": [
    {"bookmaker": "1xBet", "opening1": 1.15, "opening2": 5.4, "last1": 1.13, "last2": 5.9, "lastMarginPct": 5.44},
    {"bookmaker": "7BetUK", "opening1": 1.13, "opening2": 5.0, "last1": 1.13, "last2": 5.0, "lastMarginPct": 8.5}
  ],
  "stats": {
    "aces": {"player1": 2, "player2": 0},
    "firstServePointsWon": {"player1": {"pct": 78, "won": 25, "total": 32}, "player2": {"pct": 57, "won": 17, "total": 30}}
  },
  "pointByPoint": [
    {"set": 1, "type": "game", "score": "0-1", "server": 2, "winner": 2, "break": false, "points": ["0:15", "15:15", "15:30", "15:40"]},
    {"set": 1, "type": "game", "score": "1-1", "server": 1, "winner": 1, "break": false, "points": ["15:0", "30:0", "30:15", "40:15"]},
    {"set": 1, "type": "tiebreak", "score": "7-4", "winner": 1, "points": ["1:0", "…", "6:4 SP"], "servers": [1, 2, "…"]}
  ]
}
```

A live match looks like this: `"status": "live_set_2", "score": "6-4 3-2", "live": {"server": 1, "points1": "30", "points2": "15", "currentSetSince": "2026-09-25T17:18:20Z"}`.

### Pricing

🎾 **Free until 8 October 2026** — during the launch period you only pay Apify's platform usage, typically well under $0.01 per run.

**From 9 October 2026 you pay per match saved — platform usage included**, nothing else:

| Apify plan | Price per 1,000 matches |
|---|---|
| Free | $5.00 |
| Starter | $4.50 |
| Scale | $4.00 |
| Business and higher | $3.50 |

💡 **On Apify's free plan the monthly $5 platform credit covers about 1,000 matches** — enough to try the data properly before paying anything.

Every match comes with the odds of all bookmakers that priced it, the consensus and (optionally) the statistics, at no extra cost. Nothing is charged for matches you filter out: turn on **Skip matches without odds** to pay only for priced matches. Set a spending limit on the run if you want a hard cap — the Actor stops as soon as the limit is reached.

### FAQ

**What is the "closing line"?** The last price before the match starts. For finished and live matches the `last1` / `last2` fields are the closing odds; for scheduled matches they are the current prices.

**How is the consensus calculated?** For each bookmaker the margin is removed proportionally, then the median is taken across bookmaker brands (each brand once, via its lowest-margin listing). The median keeps one stale or mispriced book from skewing the result.

**How far back can I go?** In *Recent matches* mode 7 days back and 7 ahead. In *Tournament history* mode every edition Flashscore lists — odds go back to 2009 (fewer bookmakers in older editions, see the table above).

**Which time zone?** All timestamps are UTC.

**How fresh are live scores?** As fresh as the Flashscore scoreboard at the moment of the run. For a live feed, schedule the Actor every few minutes with `statuses: ["live"]`, `daysBack: 0`, `includeOdds: false`.

**Which ranking is `player1Rank`?** The current ATP or WTA singles ranking at the time of the run (not added to doubles or to past tournament editions).

**Is Pinnacle included?** No — the source does not list Pinnacle. The consensus of 20–40 books is a solid reference, and the Actor reports the best available price as well.

**Where does the data come from?** From publicly available match and odds information shown on Flashscore. This Actor is not affiliated with Flashscore or any bookmaker.

### More from Sports Odds Lab

- ⚽ [Football Odds Scraper](https://apify.com/sports-odds-lab/football-odds-results) — 1,000+ competitions, 1X2, over/under, BTTS and Asian handicap, seasons back to 2005
- 🏀 [NBA, NFL, MLB & NHL Odds](https://apify.com/sports-odds-lab/nba-nfl-mlb-nhl-odds) — moneyline, spread and total history back to 2009, DraftKings, FanDuel, BetMGM
- 📊 [Polymarket & Kalshi Scraper](https://apify.com/sports-odds-lab/polymarket-kalshi-odds) — prediction market odds, price history and results in one table, full Kalshi archive since 2021

⭐ **Did this Actor save you time?** A short review on this page helps other bettors and analysts find it — and tells us what to build next.

### Changelog

- **0.4** — **Live scores** (server and points of the current game), **current ATP/WTA rankings**, **tiebreak points** per set, match **end time**, optional **point-by-point** (games, breaks, break/set/match points, tiebreak points); new *Live scoreboard* dataset view.
- **0.3** — **Tournament history mode**: complete past editions of any tournament with opening/closing odds back to 2009, `ATP: all`, year ranges; `season` and `qualification` fields; *Include qualifying* option.
- **0.2** — faster and cheaper runs (about 2× less CPU per request); the consensus counts each bookmaker brand once (`brandCount`); **Max matches** keeps the most important matches first; new tour option *Other* (exhibitions, juniors).
- **0.1** — first release (free launch period): ATP, WTA, Challenger, ITF and team events; singles and doubles; opening/closing odds from up to 42 bookmakers; no-vig consensus and line movement; optional match statistics.

# Actor input Schema

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

Recent matches: every tour from up to 7 days back to 7 days ahead (upcoming matches with current odds). Tournament history: complete past editions of the tournaments you list below, odds back to 2009 — add them in the 'Tournament history' section.

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

Finished includes retirements and walkovers. Live matches come with the server and the points of the current game (run it every few minutes for a live scoreboard). Other = cancelled or postponed.

## `daysBack` (type: `integer`):

How many past days to include (0 = today only). Up to 7.

## `daysAhead` (type: `integer`):

How many upcoming days to include. Up to 7. Upcoming matches carry the current prices instead of closing lines.

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

Which levels of play to include.

## `formats` (type: `array`):

Singles, doubles or both.

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

'Tour: Tournament' as shown on Flashscore — e.g. 'ATP: Wimbledon', 'WTA: French Open', 'ATP: Indian Wells', 'Challenger Men: Genova', 'ITF Women: W15 Monastir' — or the tournament's Flashscore URL. 'ATP: all' = every ATP tournament. Tours: ATP, WTA, ATP Doubles, WTA Doubles, Challenger Men, Challenger Women, ITF Men, ITF Women.

## `years` (type: `array`):

Optional. Editions to scrape: single years (2024) or ranges (2015-2024). Leave empty to use 'Last editions'.

## `lastYears` (type: `integer`):

Used when Years is empty: the latest edition of each tournament plus this many minus one before it.

## `includeQualifying` (type: `boolean`):

Qualifying rounds of tournaments (both modes).

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

Opening and latest (closing) match-winner odds per bookmaker plus the no-vig consensus.

## `bookmakers` (type: `array`):

Optional. Names or parts of names, e.g. bet365, DraftKings, Betfred. Leave empty for all available (up to 42).

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

Do not save (and do not pay for) matches that no bookmaker priced.

## `includeStats` (type: `boolean`):

Aces, double faults, first/second serve points won, break points and more for finished matches (full detail on ATP/WTA/Challenger, partial on ITF).

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

Every game of finished and live matches: server, winner, breaks and the score before each point (break, set and match points marked), plus every tiebreak point. One extra request per match.

## `includeRankings` (type: `boolean`):

Current ATP/WTA singles ranking of both players. Recent matches only — past tournament editions don't get it, because today's ranking would be misleading there.

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

Stop after this many matches (0 = no limit). With a limit the most important matches are kept first: ATP and WTA, then Challenger, team events and ITF.

## Actor input object example

```json
{
  "mode": "recent",
  "statuses": [
    "finished",
    "scheduled"
  ],
  "daysBack": 1,
  "daysAhead": 1,
  "tours": [
    "ATP",
    "WTA",
    "CHALLENGER",
    "ITF",
    "TEAMS",
    "OTHER"
  ],
  "formats": [
    "SINGLES"
  ],
  "tournaments": [
    "ATP: Wimbledon",
    "WTA: Wimbledon"
  ],
  "years": [
    "2024"
  ],
  "lastYears": 1,
  "includeQualifying": true,
  "includeOdds": true,
  "onlyWithOdds": false,
  "includeStats": false,
  "includePointByPoint": false,
  "includeRankings": true,
  "maxMatches": 30
}
```

# Actor output Schema

## `overview` (type: `string`):

One row per match: players, tournament, score and the no-vig consensus.

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

Full records including the odds of every bookmaker and match statistics.

# 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": "recent",
    "statuses": [
        "finished",
        "scheduled"
    ],
    "daysBack": 1,
    "daysAhead": 1,
    "tours": [
        "ATP",
        "WTA",
        "CHALLENGER",
        "ITF",
        "TEAMS",
        "OTHER"
    ],
    "formats": [
        "SINGLES"
    ],
    "tournaments": [
        "ATP: Wimbledon",
        "WTA: Wimbledon"
    ],
    "years": [
        "2024"
    ],
    "maxMatches": 30
};

// Run the Actor and wait for it to finish
const run = await client.actor("sports-odds-lab/tennis-odds-results").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": "recent",
    "statuses": [
        "finished",
        "scheduled",
    ],
    "daysBack": 1,
    "daysAhead": 1,
    "tours": [
        "ATP",
        "WTA",
        "CHALLENGER",
        "ITF",
        "TEAMS",
        "OTHER",
    ],
    "formats": ["SINGLES"],
    "tournaments": [
        "ATP: Wimbledon",
        "WTA: Wimbledon",
    ],
    "years": ["2024"],
    "maxMatches": 30,
}

# Run the Actor and wait for it to finish
run = client.actor("sports-odds-lab/tennis-odds-results").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": "recent",
  "statuses": [
    "finished",
    "scheduled"
  ],
  "daysBack": 1,
  "daysAhead": 1,
  "tours": [
    "ATP",
    "WTA",
    "CHALLENGER",
    "ITF",
    "TEAMS",
    "OTHER"
  ],
  "formats": [
    "SINGLES"
  ],
  "tournaments": [
    "ATP: Wimbledon",
    "WTA: Wimbledon"
  ],
  "years": [
    "2024"
  ],
  "maxMatches": 30
}' |
apify call sports-odds-lab/tennis-odds-results --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,sports-odds-lab/tennis-odds-results"
        }
    }
}
```

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/JLoVqmUto4bFqUVMP/builds/InLtB9rqug3RT8tuh/openapi.json
