# Tennis Match Stats Scraper - ATP & WTA (`recordsdata/tennis-match-stats-scraper`) Actor

Scrape ATP and WTA tennis data from Sofascore by player name or match ID: results with set scores, serve and return statistics, point-by-point sequences, head-to-head history, betting odds and live rankings. Export CSV, Excel, JSON. No login or API key. Six record types per run.

- **URL**: https://apify.com/recordsdata/tennis-match-stats-scraper.md
- **Developed by:** [RecordsData](https://apify.com/recordsdata) (community)
- **Categories:** Sports, Developer tools
- **Features:** Limited permissions
- **Stats:** 2 total users, 0 monthly users, 96.4% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $16.15 / 1,000 match records

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?

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

<p align="center">
  <img src="https://api.apify.com/v2/key-value-stores/AAm3a1h3Z9nYfrvh9/records/banner?v=3" alt="RecordsData" width="100%" />
</p>

## 🎾 Tennis Match Stats Scraper: ATP & WTA Results, Stats, Odds & Rankings

> **Tennis Match Stats Scraper collects ATP and WTA data from Sofascore: matches by player name, match ID, tournament season or calendar day (set scores, serve statistics, point-by-point, head-to-head, odds, seeds, rankings), plus player profiles, tournament profiles, name search and tournament lists. Filter by date, tour, singles or doubles and status, monitor only new matches, and export CSV, Excel or JSON. No login. $0.019 per match; extras are opt-in.**

Tennis Match Stats Scraper by RecordsData turns a player name such as "Novak Djokovic", a tournament such as "Roland Garros" or a day such as 2026-10-10 into one row per match, with up to nine kinds of data attached. In our cloud verification of build 0.1.24 on 2026-10-10, 18 of 19 small runs succeeded (the one failure was a network error on our test machine) and the median run took 14.6 seconds. Blocked requests are retried with a fresh IP.

### 🎾 What does Tennis Match Stats Scraper do?

- Scrapes matches for any ATP or WTA player by name or Sofascore match ID, or a **day schedule** of up to 14 days (`dateFrom`, `dateTo`, no player), biggest tournaments first.
- Reads whole tournament seasons with `tournaments` and `tournamentSeason`.
- Monitors with `onlyNew`: delivered matches are skipped before any request and not charged.
- Adds serve and return statistics (optionally per set), point-by-point, head-to-head, odds with opening price and movement, fan votes, seeds and rankings.
- Returns player profiles, tournament profiles, name search and tournament lists.

### 📋 What data can you extract from Sofascore tennis?

| Field | Description |
|---|---|
| `recordType` | `match`, `ranking`, `player`, `tournament` or `search` |
| `matchId`, `url`, `tournament`, `tour`, `season`, `round`, `surface` | Match ID, link, event, tour, season, round, surface |
| `player1`, `player2`, `player1Ranking`, `player1Seed` | The two players, live ranking and seed |
| `setsWonPlayer1`, `setsWonPlayer2`, `scoreBySet`, `winner` | Sets, games per set, winner |
| `status`, `startTime`, `durationMinutes` | Status, UTC start time, duration |
| `statistics`, `setStatistics`, `pointByPoint` | Serve and return lines, points of every game |
| `h2hRecord`, `h2hMeetings`, `odds`, `votes` | Head-to-head, earlier meetings, odds, fan votes |
| `ranking`, `points`, `bestRanking`, `utrRating`, `heightCm`, `prizeMoneyCareer` | Ranking and player profile |
| `titleHolder`, `mostTitles`, `prizeMoney`, `champions` | Tournament profile |

The dataset has 103 columns (build 0.1.24) in five views. Match extras you did not switch on carry `Not Requested`; data a match does not have carries `N/A`. On profile, search and list rows, empty fields are left out.

### 🧾 Sample output

Abridged real row from a cloud run on 2026-10-04 with `{"playerNames": ["Novak Djokovic"], "maxItems": 1, "includeStatistics": true}`; `statistics` is shortened to 2 of 20 lines.

```json
{
  "recordType": "match",
  "matchId": 14956421,
  "tournament": "Athens, Greece",
  "round": "Final",
  "surface": "Hardcourt indoor",
  "player1": "Novak Djokovic",
  "player2": "Lorenzo Musetti",
  "setsWonPlayer1": 2,
  "setsWonPlayer2": 1,
  "scoreBySet": [{ "set": 1, "player1Games": 4, "player2Games": 6 }, { "set": 2, "player1Games": 6, "player2Games": 3 }],
  "status": "Ended",
  "startTime": "2025-11-08T15:00:00.000Z",
  "statistics": [
    { "group": "Service", "stat": "Aces", "player1": "6", "player2": "10" },
    { "group": "Service", "stat": "Double faults", "player1": "3", "player2": "3" }
  ],
  "scrapedAt": "2026-10-04T07:27:47.026Z"
}
```

### 💰 How much does it cost to scrape tennis matches?

Pay per event, only for saved records. Error rows, empty results, skipped already-seen matches and failed runs are never billed. Prices are the live Free-plan prices read on 2026-10-10; higher Apify plans pay less for match and point-by-point records (Gold: $0.01615 and $0.0153); the other events cost the same on every plan.

- Match record: $0.019 per match
- Match statistics record: $0.01 per statistic line (20 to 42 per match). Off by default
- Set statistics line: $0.005, off by default
- Point-by-point record: $0.018 per set with its point sequence
- Head-to-head record: $0.01 per match; past meeting: $0.005 each
- Betting odds record: $0.01 per odds line (a normal match has 2)
- Fan votes: $0.01 per match
- Ranking record: $0.01 per unique player per run
- Profile record: $0.003 per player or tournament profile
- Search record: $0.0015 per result row
- Player result record $0.0001, season statistics record $0.0005, champion record $0.0005: one line each, all off by default

Tournament lists have no billed event yet and are free for now. Date windows, day schedules, seasons and only-new mode add no event.

**Worked examples:** 1,000 match rows with the defaults cost $19. A one-day ATP schedule capped at `maxItems: 5` costs 5 x $0.019 = $0.095. Five matches with fan votes cost 5 x $0.019 + 5 x $0.01 = $0.145. A match with 5 earlier meetings adds 5 x $0.005 = $0.025 to its $0.019 row. **Match statistics are off by default because they bill per line:** roughly $0.20 to $0.42 extra per match. One player profile with 30 recent result lines costs $0.003 + 30 x $0.0001 = $0.006. `oddsMarkets: all` costs about three times `main`.

Use **Max total charge** to cap spending; the Actor stops cleanly and keeps saved data.

### 🚀 How to scrape tennis data in 3 steps

1. Type a player name, pick a tournament, or set `dateFrom` and `dateTo`.
2. Narrow with `tour`, `matchType` and `matchStatus`; switch on extras.
3. Click **Start**, then download the dataset as CSV, Excel, JSON or XML.

### ⚙️ Input

| Field | Default | Meaning |
|---|---|---|
| `playerNames` | `["Novak Djokovic"]` | Players to look up; recent and upcoming matches |
| `eventIds`, `liveOnly` | empty, `false` | Sofascore match IDs; every match live now |
| `tournaments`, `tournamentSeason` | empty, `latest` | Name, id or `id:seasonId`; `latest` or a year |
| `dateFrom`, `dateTo`, `gameWindow` | empty, `both` | UTC YYYY-MM-DD (day schedule up to 14 days if no player); `played`, `upcoming`, `both` |
| `playerProfiles`, `tournamentProfiles` | empty | Name, id or URL; one profile row each |
| `searchQueries`, `searchType`, `listTournaments` | empty, `all`, `false` | Name search; tournament calendar |
| `includePlayerResults`, `playerResultsLimit` | `false`, `30` | Results per profiled player, 1 to 150 |
| `includeSeasonStatistics`, `includeChampions`, `championSeasons` | `false`, `5` | Season totals; champions, 1 to 30 seasons |
| `tour`, `matchType`, `matchStatus` | `any` | `atp`, `wta`, `challenger`, `itf`, `utr`; `singles`, `doubles`; `finished`, `live`, `upcoming` |
| `maxItems` | none | Maximum rows. Free users get 10 |
| `includeStatistics`, `includeSetStatistics` | `false` | Statistic lines, whole match or per set |
| `includePointByPoint`, `includeH2h`, `includeH2hMeetings` | `false` | Point sequence, tally, earlier meetings |
| `includeOdds`, `oddsMarkets` | `false`, `main` | Odds; `main` or `all` |
| `includeVotes`, `includeRanking` | `false` | Fan votes; ranking once per unique player |
| `onlyNew`, `stateKey`, `resetSeen` | `false`, empty | Only undelivered matches; monitor label; clear state |

Day schedule of one ATP day (the default player is treated as untouched when you type dates, so this gives the schedule, not Djokovic's matches):

```json
{ "dateFrom": "2026-10-10", "dateTo": "2026-10-10", "tour": "atp", "matchType": "singles", "matchStatus": "upcoming", "maxItems": 5, "includeOdds": true }
```

Monitor mode: rerun the same input on a schedule with `"onlyNew": true`. A match counts as new once per state: upcoming, finished, and while live every new game score. State lives in a store named `tennis-seen`.

### 📤 Output

Results go to the default dataset with an **Overview** view and a full view, told apart by `recordType`. If nothing matches, the run ends successfully and bills nothing. If Sofascore blocks every request the run fails with an explicit message.

### 📊 Tennis Match Stats Scraper vs alternatives

Read on 2026-10-10 from the public Apify Store pages. "Not shown" means the page does not mention it.

| Capability | This Actor | crawlstone/tennis-scraper | devarq/tennis-scraper |
|---|---|---|---|
| Day schedule | Up to 14 days, with filters | Tournaments by date | 7 days back and ahead |
| Statistics, point-by-point | Both, plus per set | Point-by-point with period stats | Both |
| Head-to-head, only-new | Both | Not shown | Both |
| Odds | One bookmaker, opening and movement | Date-level, live matches only | One entry per bookmaker |
| Profiles, search | Yes; no Elo or titles list | Yes, with Elo and champions | Top-100 rankings, player mode |
| Price per 1,000 | $19 per 1,000 matches | from $6.00 per successful result | from $1.20 ($1.50 Free) plus add-ons |

Also read: tagadanar/tennis-live-scores ($4.00 per 1,000 matches, $2.80 on Gold, statistics $10 per 1,000, no odds or point-by-point) and extractify-labs/flashscore-tennis-matches (from $1.00, no statistics).

We are not the cheapest: every rival above costs less per match. We lack Elo ratings, player title lists and year-end rankings, and return one bookmaker's odds. The price buys one run joining schedule, statistics, point-by-point, head-to-head, odds, seeds and profiles. Pick a cheaper actor if you only need scores.

### 💼 Use cases

Betting analysis, daily tournament feeds, match previews and prediction-model data.

### 🔌 Run via API, schedule and integrations

```bash
curl -X POST "https://api.apify.com/v2/acts/RecordsData~tennis-match-stats-scraper/runs?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"dateFrom": "2026-10-10", "dateTo": "2026-10-10", "tour": "wta", "maxItems": 10, "onlyNew": true}'
```

Send results to Google Sheets, Zapier, Make or n8n, or use the Python and JavaScript Apify clients.

### ⚖️ Is it legal to scrape tennis data?

The Actor reads publicly available match data, does not log in and collects no private personal data. Use the data in line with Sofascore's terms and applicable laws. Odds are informational only and not betting advice.

### 🤖 Tennis Match Stats Scraper MCP server for AI agents

Agents can call this Actor through the Apify MCP server at `https://mcp.apify.com?tools=RecordsData/tennis-match-stats-scraper`, which loads only this Actor as a tool. Use it as the server URL in Claude Desktop, Cursor or VS Code.

Example agent prompt:

> Use the Tennis Match Stats Scraper to list the first 5 ATP singles matches scheduled for 2026-10-10 with odds, and tell me which favourite has the shortest price.

Tool description: use this tool when you need structured ATP or WTA match results, serve statistics, point-by-point, odds, head-to-head or rankings for a named player, a tournament season or a calendar day. It takes the same fields as the Input table and also covers profiles, name search and tournament lists. Statistics, meetings, votes and `oddsMarkets: all` raise the cost.

### ❓ Frequently asked questions

#### How do I scrape ATP and WTA match results with the Tennis Match Stats Scraper?

Enter a player name such as "Novak Djokovic" in `playerNames` and click Start. You get one row per recent or upcoming ATP or WTA match from Sofascore, with tournament, round, surface, set scores, status and start time. Use `eventIds` for specific matches.

#### Does the Tennis Match Stats Scraper need a login or API key?

No. It reads publicly available Sofascore match data and does not log in, so you need no Sofascore account or API key. You only need an Apify account to run the actor, export CSV, Excel, JSON or XML, or call the Apify API.

#### Why did my Tennis Match Stats Scraper run return 0 results?

The name did not match a tennis player on Sofascore, the player has no matches to list, or your filters excluded everything. Use the full name as Sofascore spells it, for example "Iga Swiatek". An empty run finishes successfully with a clear status message and bills nothing.

#### Why is my Tennis Match Stats Scraper run expensive?

Most likely `includeStatistics` is on, or `oddsMarkets` is set to `all`. Statistics bill one $0.01 event per statistic line, and a full match has 20 to 42 lines, so about $0.20 to $0.42 extra per match on the Free plan. All are off by default; set Max total charge to cap spending.

#### Is point-by-point data available for every tennis match?

Only where Sofascore recorded it. With `includePointByPoint` on, you get the point sequence of every game in a set where Sofascore has it. Otherwise the field shows `N/A` and no point-by-point event is billed. Each set with data bills one $0.018 point-by-point record.

#### Why is a player ranking billed once per player?

A ranking is one fact about a player, so the Tennis Match Stats Scraper bills one $0.01 ranking record per unique player in the run, not one per match. If a player has 10 matches in your dataset, you pay for that ranking only once.

#### Can the Tennis Match Stats Scraper get live tennis matches?

Yes. Switch on `liveOnly` to fetch every tennis match in progress on Sofascore at that moment, or set `matchStatus` to `live`. Each live match is one match record at $0.019 on the Free plan, with the status field showing it is live. Combine it with any extra.

#### What do Not Requested and N/A mean in the output?

`Not Requested` means you did not switch that extra on, such as statistics, point-by-point, head-to-head or odds. `N/A` means you did request it but Sofascore has no data for that match, or the match has not been played yet, so you can tell the two cases apart.

#### How do I get every tennis match of one day?

Set `dateFrom` and `dateTo` and leave player names, tournaments and match IDs empty. You get the day schedule: every tennis match of those UTC days, up to 14 days, biggest tournaments first, so `maxItems` keeps ATP and WTA main draws before Challenger and ITF.

#### How does only-new mode work?

Switch on `onlyNew` and run the same search again. The actor remembers delivered matches in a store named `tennis-seen` in your account and skips them before any request, so skipped matches are not charged. A match counts as new once per state: upcoming, finished, and while live every new game score. `stateKey` separates monitors; `resetSeen` starts over.

#### How do I get a tennis player or tournament profile?

Fill `playerProfiles` with a name, a Sofascore id or a player page URL, or `tournamentProfiles` with a tournament name, id or URL. You get one profile row each: ranking, UTR rating, hand, height and prize money for players; tour, surface, points, host city and title holder for tournaments. A profile row bills one $0.003 record; optional lines bill small separate events.

#### What can the Tennis Match Stats Scraper not do yet?

It has no Elo ratings, no list of a player's titles and finals, no year-end rankings and no backhand type, because Sofascore does not send them. It returns the odds of one bookmaker only, and `gameWindow` does not apply to the day schedule. Player history is capped at 150 matches and champions at 30 seasons.

### 🔗 Want more sports data? Other RecordsData scrapers

- [Sofascore Match Stats Scraper](https://apify.com/RecordsData/sofascore-match-stats-scraper) for football scores, odds and lineups
- [Sports Reference Stats Scraper](https://apify.com/RecordsData/sports-reference-stats-scraper) for NBA and NHL statistics

### 🆘 Support

Problems or missing fields? Use the Issues tab or write to contact.punkrecordsdata@gmail.com. Independent tool, not affiliated with Sofascore, the ATP or the WTA.

Last updated: 2026-10-11

# Actor input Schema

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

Players whose recent and upcoming matches you want. Example: Novak Djokovic, Iga Swiatek. Ignored if you use profiles, name search or the tournament calendar below.

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

Whole tournament seasons. Use a name (Roland Garros, Madrid), a Sofascore tournament ID (2519 = Shanghai) or id:seasonId. A name can match men, women and doubles, so combine it with Tour and Singles or doubles.

## `tournamentSeason` (type: `string`):

Season to list for the tournaments above: latest or a year such as 2025.

## `eventIds` (type: `array`):

Fetch specific matches directly by Sofascore match ID. Example: 12345678, the number after id: in a Sofascore match URL. When set, the default player name is ignored.

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

Ignores players and match IDs and returns every tennis match being played right now.

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

Only matches starting on or after this UTC date. Example: 2026-11-03.

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

Only matches starting on or before this UTC date. Use the same date as Matches from for a single day.

## `gameWindow` (type: `string`):

Which matches of a player or tournament to list. Without dates, a player returns the latest 30 played and the next 30 upcoming.

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

Keep only one tour: ATP, WTA, Challenger (includes WTA 125), ITF or UTR. Also applies to the tournament calendar.

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

Keep only singles or only doubles. Also applies to the tournament calendar.

## `matchStatus` (type: `string`):

Keep only finished, live or not-yet-started matches.

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

Serve, return, points and games stats for both players (aces, break points, first-serve %). Billed per statistic line, about 20 to 42 lines per match, on top of the match row.

## `includeSetStatistics` (type: `boolean`):

Same stats as Match statistics, set by set. Billed per statistic line.

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

Every point of every game and set. Large output.

## `includeH2h` (type: `boolean`):

Historical wins and losses between the two players.

## `includeH2hMeetings` (type: `boolean`):

Earlier matches between the two players (date, tournament, round, surface, winner, score) and wins per surface. One extra row per past meeting.

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

Bookmaker odds for the match result. Informational data only, not betting advice.

## `oddsMarkets` (type: `string`):

Main = match result only. All = every listed market (match winner, first set winner, total games), each with opening odd and movement.

## `includeVotes` (type: `boolean`):

How many Sofascore users picked each player to win. One extra row per match with votes.

## `includeRanking` (type: `boolean`):

Current ranking snapshot, computed once per unique player in the run, not per match.

## `playerProfiles` (type: `array`):

One profile row per player with ranking, UTR rating, country, hand, height, birth date and prize money. Accepts a name (Carlos Alcaraz), a Sofascore player ID (275923) or a player page URL.

## `includePlayerResults` (type: `boolean`):

Latest finished matches of every profile: date, tournament, round, surface, opponent, score. One extra row per result.

## `playerResultsLimit` (type: `integer`):

How many recent results to add per player. 30 is one Sofascore page.

## `includeSeasonStatistics` (type: `boolean`):

Aces, double faults, first-serve %, break points and tie-breaks for the player's 10 most recent tournaments. One extra row per tournament.

## `tournamentProfiles` (type: `array`):

One profile row per tournament with tour, surface, points, host city, prize money, title holder and most titles. Accepts a name (Roland Garros), a tournament ID (2480) or a page URL.

## `includeChampions` (type: `boolean`):

Winner, runner-up, final score and date for the latest seasons. One extra row per champion.

## `championSeasons` (type: `integer`):

How many of the latest seasons to look up per tournament (seasons whose final is not played yet give no row).

## `searchQueries` (type: `array`):

Names to search, at least 2 characters. Example: Alcaraz. One row per player or tournament found, with ID, country or tour, link and relevance.

## `searchType` (type: `string`):

Which kind of search result to keep.

## `searchFullProfiles` (type: `boolean`):

Fetch the full profile of each result instead of one short row. Slower, more requests, one profile record per result.

## `listTournaments` (type: `boolean`):

One row per tournament. Without dates: the season calendar of the chosen Tour. With dates (up to 14 days): tournaments that have matches on those days.

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

Maximum rows: matches, profiles, search results or tournaments. Example: 100. Free plan: 10 rows (preview). Paid plans: up to 1,000,000. Leave empty for all rows found.

## `onlyNew` (type: `boolean`):

Skip matches this same search already returned (new match, finished, or new live score). You are not charged for skipped matches. Max rows then counts new rows.

## `stateKey` (type: `string`):

Label to keep several independent monitors with identical filters. Leave empty to share state between runs of the same search.

## `resetSeen` (type: `boolean`):

Forget earlier runs of this search so everything counts as new again.

## Actor input object example

```json
{
  "playerNames": [
    "Novak Djokovic"
  ],
  "tournaments": [],
  "tournamentSeason": "",
  "eventIds": [],
  "liveOnly": false,
  "dateFrom": "",
  "dateTo": "",
  "gameWindow": "both",
  "tour": "any",
  "matchType": "any",
  "matchStatus": "any",
  "includeStatistics": false,
  "includeSetStatistics": false,
  "includePointByPoint": false,
  "includeH2h": false,
  "includeH2hMeetings": false,
  "includeOdds": false,
  "oddsMarkets": "main",
  "includeVotes": false,
  "includeRanking": false,
  "playerProfiles": [],
  "includePlayerResults": false,
  "playerResultsLimit": 30,
  "includeSeasonStatistics": false,
  "tournamentProfiles": [],
  "includeChampions": false,
  "championSeasons": 5,
  "searchQueries": [],
  "searchType": "all",
  "searchFullProfiles": false,
  "listTournaments": false,
  "maxItems": 5,
  "onlyNew": false,
  "stateKey": "",
  "resetSeen": false
}
```

# Actor output Schema

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

Key match fields: players, tournament, score, surface, date

## `players` (type: `string`):

Player profiles and ranking rows: ranking, live ranking, UTR, country, prize money, recent results

## `tournaments` (type: `string`):

Tournament profiles and lists: tour, surface, points, prize money, title holder, champions

## `search` (type: `string`):

Name search results: id, name, country or tour, link, relevance

## `fullData` (type: `string`):

Complete dataset with all 103 fields, including every requested extra

# 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 = {
    "playerNames": [
        "Novak Djokovic"
    ],
    "maxItems": 5
};

// Run the Actor and wait for it to finish
const run = await client.actor("recordsdata/tennis-match-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 = {
    "playerNames": ["Novak Djokovic"],
    "maxItems": 5,
}

# Run the Actor and wait for it to finish
run = client.actor("recordsdata/tennis-match-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 '{
  "playerNames": [
    "Novak Djokovic"
  ],
  "maxItems": 5
}' |
apify call recordsdata/tennis-match-stats-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,recordsdata/tennis-match-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/qG3wnUMNUXxfsQu8W/builds/5m3d9YhQehIvSr56E/openapi.json
