# Tennis Intelligence — Match Previews, Odds & Player Analytics (`varnie/tennis-intelligence`) Actor

Tennis betting & analytics data from SofaScore and Tennis Abstract: match previews that combine pre-match odds with player form, head-to-head and surface splits, plus live matches, schedules, player match history and rankings. No login or API key required.

- **URL**: https://apify.com/varnie/tennis-intelligence.md
- **Developed by:** [Sergei A.](https://apify.com/varnie) (community)
- **Categories:** AI, News
- **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/platform/actors/running/actors-in-store#pay-per-usage

## 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 Intelligence — Match Previews, Odds & Player Analytics

**Tennis betting and analytics data in one record: pre-match odds, player form, head-to-head and surface splits for each match — plus live matches, schedules and full player match history.** Start from match IDs, a tournament, a date or a player; no login or API key is required. Sources: [SofaScore](https://www.sofascore.com/tennis) (matches, live, odds) and [Tennis Abstract](https://www.tennisabstract.com/) (form, Elo, splits).

### What this Actor covers — and what it does not

- **Odds and match intelligence in a single record.** Every match preview combines the SofaScore pre-match odds feed (moneyline, set winner, totals, with fractional and decimal values and last movement) with Tennis Abstract analytics for both players: recent form, surface win rates and the head-to-head record. One match ID in, one decision-ready record out.
- **The only multi-signal tennis preview on the Store.** Incumbent tennis scrapers sell either SofaScore live/odds data or Tennis Abstract history alone. No current actor merges the two sources into a single per-match preview, which is the format betting and modeling teams actually consume.
- **Join-ready identifiers.** Every record carries the SofaScore match ID, tournament and unique-tournament IDs, player IDs and current rankings, plus each player's Tennis Abstract slug and Elo, so previews join cleanly with external odds and rankings feeds.
- **Four modes under one input.** `matchPreview` for per-match previews, `liveMatches` for the current live slate, `schedule` for upcoming matches on a date, and `playerHistory` for a player's recent results with dominance ratio, ace rate and double-fault rate per match.
- **Politeness and reliability built in.** Requests rotate browser headers, respect a sliding-window rate limit and a per-run delay, and retry with backoff; blocked pages are re-fetched through the Apify proxy pool with browser TLS impersonation, and missing pages are logged and skipped instead of killing the run.
- **Public pages only.** No account, cookies or API keys for either source. Unavailable values stay null rather than being invented.

### Who it's for

- **Sports betting analysts and odds engineers** — pull per-match previews that combine pre-match odds with form and head-to-head context for pricing and model features.
- **Tennis stat and modeling teams** — collect player form, surface splits, Elo and head-to-head records with stable SofaScore and Tennis Abstract identifiers.
- **LLM and dataset builders** — source structured tennis match and player-history pages for betting-knowledge and ranking datasets.

### Quick start

**One exact match preview**

```json
{
  "mode": "matchPreview",
  "matchIds": [
    16737476
  ],
  "maxMatches": 1
}
```

**Preview the next matches of a tournament**

```json
{
  "mode": "matchPreview",
  "tournamentId": 194944,
  "maxMatches": 10
}
```

**All live tennis matches right now**

```json
{
  "mode": "liveMatches",
  "maxMatches": 20
}
```

**Upcoming matches on a date**

```json
{
  "mode": "schedule",
  "date": "2026-08-16",
  "maxMatches": 50
}
```

**One player's full match history**

```json
{
  "mode": "playerHistory",
  "player": "NovakDjokovic",
  "maxMatches": 30
}
```

### Output

The dataset contains one `matchPreview` record per match ID (the core product: odds + form + head-to-head), `liveMatch` records for currently running matches, `scheduledMatch` records for upcoming matches on a date, and `playerHistory` records with a player's full match history. `recordType` identifies the shape.

| field | type | meaning |
|---|---|---|
| `recordType` | `string` | Record discriminator: `matchPreview`, `liveMatch`, `scheduledMatch` or `playerHistory`. |
| `scrapedAt` | `string` | ISO-8601 timestamp of when the record was collected. |
| `matchId` | `integer` | SofaScore event ID. |
| `startTime` | `string` | ISO-8601 start time of the match. |
| `status` | `string` | Match status: notstarted, inprogress, finished, etc. |
| `tournament` | `object` | Tournament metadata: name, category (ATP/WTA/Challenger/ITF), tournament and unique tournament IDs. |
| `surface` | `string` | Court surface, e.g. Hardcourt outdoor, Red clay, Grass. |
| `round` | `string` | Round name, e.g. Quarterfinal. |
| `homePlayer` | `object` | Player metadata: name, country, SofaScore ID, ranking, Tennis Abstract slug and Elo (when available). |
| `awayPlayer` | `object` | Away player metadata (same shape as homePlayer). |
| `score` | `object` | Set-by-set score for live/finished matches. |
| `odds` | `array` | Pre-match odds markets from SofaScore: moneyline, set winner, totals, with fractional and decimal values and last change direction. |
| `playerForm` | `array` | Aggregated recent form per player (from Tennis Abstract recent results): last N results, surface win rates, and head-to-head record between the two players. |
| `headToHead` | `object` | Aggregated head-to-head record between the two players (home/away wins). |
| `player` | `object` | Player history record: player identity and total rows. |
| `matches` | `array` | Match history rows for playerHistory mode. |

Live example from a bounded run:

```json
{
  "recordType": "matchPreview",
  "scrapedAt": "2026-08-16T10:39:43.037276+00:00",
  "matchId": 16737476,
  "startTime": "2026-08-16T15:00:00+00:00",
  "status": "notstarted",
  "tournament": {
    "name": "Cincinnati, USA",
    "category": "ATP",
    "tournamentId": 194944,
    "uniqueTournamentId": 2373
  },
  "surface": "Hardcourt outdoor",
  "round": "Round of 64",
  "homePlayer": {
    "name": "Marco Trungelliti",
    "country": "Argentina",
    "playerId": 38517,
    "ranking": 82
  },
  "awayPlayer": {
    "name": "Daniil Medvedev",
    "country": "Russia",
    "playerId": 163504,
    "ranking": 5
  },
  "score": {
    "homeSets": null,
    "awaySets": null,
    "sets": []
  },
  "odds": [
    {
      "market": "Full time",
      "group": "Home/Away",
      "period": "Match",
      "suspended": false,
      "choices": [
        {
          "name": "1",
          "fractional": "9/2",
          "decimal": 5.5,
          "change": -1
        },
        {
          "name": "2",
          "fractional": "7/50",
          "decimal": 1.14,
          "change": 1
        }
      ]
    },
    {
      "market": "First set winner",
      "group": "Home/Away",
      "period": "1st set",
      "suspended": false,
      "choices": [
        {
          "name": "1",
          "fractional": "3/1",
          "decimal": 4.0,
          "change": -1
        },
        {
          "name": "2",
          "fractional": "1/4",
          "decimal": 1.25,
          "change": 1
        }
      ]
    },
    {
      "market": "Total games won",
      "group": "Total sets/games",
      "period": "Extra time",
      "suspended": false,
      "choices": [
        {
          "name": "Over",
          "fractional": "10/11",
          "decimal": 1.91,
          "change": 1
        },
        {
          "name": "Under",
          "fractional": "4/5",
          "decimal": 1.8,
          "change": -1
        }
      ]
    }
  ],
  "playerForm": [
    {
      "player": "Marco Trungelliti",
      "playerId": 38517,
      "recent": [
        {
          "date": "13-Aug-2026",
          "tournament": "Cincinnati",
          "surface": "Hard",
          "result": "W",
          "opponent": "Hamad Medjedovic",
          "score": "7-5 7-6(2)"
        },
        {
          "date": "13-Aug-2026",
          "tournament": "Cincinnati",
          "surface": "Hard",
          "result": "W",
          "opponent": "Kei Nishikori",
          "score": "4-6 7-5 6-3"
        },
        {
          "date": "13-Aug-2026",
          "tournament": "Cincinnati",
          "surface": "Hard",
          "result": "W",
          "opponent": "Sebastian Ofner",
          "score": "4-6 6-4 7-6(4)"
        },
        {
          "date": "02-Aug-2026",
          "tournament": "Montreal",
          "surface": "Hard",
          "result": "L",
          "opponent": "Nicolas Mejia",
          "score": "7-6(5) 3-6 6-1"
        },
        {
          "date": "20-Jul-2026",
          "tournament": "Kitzbuhel",
          "surface": "Clay",
          "result": "L",
          "opponent": "Yannick Hanfmann",
          "score": "6-4 7-6(2)"
        },
        {
          "date": "20-Jul-2026",
          "tournament": "Kitzbuhel",
          "surface": "Clay",
          "result": "W",
          "opponent": "Juan Manuel Cerundolo",
          "score": "6-2 6-1"
        },
        {
          "date": "13-Jul-2026",
          "tournament": "Umag",
          "surface": "Clay",
          "result": "L",
          "opponent": "Alejandro Davidovich Fokina",
          "score": "6-4 7-5"
        },
        {
          "date": "13-Jul-2026",
          "tournament": "Umag",
          "surface": "Clay",
          "result": "W",
          "opponent": "Kyrian Jacquet",
          "score": "6-2 6-3"
        },
        {
          "date": "29-Jun-2026",
          "tournament": "Wimbledon",
          "surface": "Grass",
          "result": "L",
          "opponent": "Martin Damm",
          "score": "7-6(5) 6-7(5) 7-6(2) 7-6(5)"
        },
        {
          "date": "22-Jun-2026",
          "tournament": "Eastbourne",
          "surface": "Grass",
          "result": "L",
          "opponent": "Jack Pinnington Jones",
          "score": "5-7 6-3 7-5"
        }
      ],
      "surfaceSplits": {
        "hard": 0.25,
        "clay": 0.467,
        "grass": 0.0
      },
      "elo": 1636
    },
    {
      "player": "Daniil Medvedev",
      "playerId": 163504,
      "recent": [
        {
          "date": "02-Aug-2026",
          "tournament": "Montreal",
          "surface": "Hard",
          "result": "L",
          "opponent": "Botic Van De Zandschulp",
          "score": "6-3 7-6(5)"
        },
        {
          "date": "29-Jun-2026",
          "tournament": "Wimbledon",
          "surface": "Grass",
          "result": "L",
          "opponent": "Jan Lennard Struff",
          "score": "7-6(4) 7-6(5) 7-5"
        },
        {
          "date": "29-Jun-2026",
          "tournament": "Wimbledon",
          "surface": "Grass",
          "result": "W",
          "opponent": "Daniel Merida Aguilar",
          "score": "3-6 6-3 7-5 6-2"
        },
        {
          "date": "29-Jun-2026",
          "tournament": "Wimbledon",
          "surface": "Grass",
          "result": "W",
          "opponent": "Marin Cilic",
          "score": "6-1 6-2 6-4"
        },
        {
          "date": "15-Jun-2026",
          "tournament": "Halle",
          "surface": "Grass",
          "result": "L",
          "opponent": "Daniel Altmaier",
          "score": "6-4 6-7(6) 6-4"
        },
        {
          "date": "15-Jun-2026",
          "tournament": "Halle",
          "surface": "Grass",
          "result": "W",
          "opponent": "Terence Atmane",
          "score": "6-4 6-4"
        },
        {
          "date": "15-Jun-2026",
          "tournament": "Halle",
          "surface": "Grass",
          "result": "W",
          "opponent": "Tomas Martin Etcheverry",
          "score": "6-3 6-4"
        },
        {
          "date": "08-Jun-2026",
          "tournament": "s Hertogenbosch",
          "surface": "Grass",
          "result": "L",
          "opponent": "Kamil Majchrzak",
          "score": "7-6(4) 6-1"
        },
        {
          "date": "08-Jun-2026",
          "tournament": "s Hertogenbosch",
          "surface": "Grass",
          "result": "W",
          "opponent": "Marin Cilic",
          "score": "6-2 3-6 6-1"
        },
        {
          "date": "08-Jun-2026",
          "tournament": "s Hertogenbosch",
          "surface": "Grass",
          "result": "W",
          "opponent": "Thijs Boogaard",
          "score": "6-3 4-6 7-6(6)"
        }
      ],
      "surfaceSplits": {
        "hard": 0.735,
        "clay": 0.6,
        "grass": 0.667
      },
      "elo": 1971
    }
  ],
  "headToHead": {
    "home": 0,
    "away": 0,
    "matches": []
  }
}
```

### Pricing

Tennis Intelligence runs under Apify's **pay-per-event** model: you pay per delivered dataset record, plus the platform usage your run generates. No subscription, no data limits.

A typical run costs very little on the platform. Measured on a real run (20 `matchPreview` records, ~3 minutes): **$0.04 total platform usage**, of which ~$0.037 is compute units and ~$0.0006 is residential proxy traffic. That works out to roughly **$0.002 per record** of platform cost before the per-record event price.

You control spend directly with `maxMatches` (cap on delivered records, also clamped to your run's pay-per-event budget) and by trimming `includeOdds` / `includeHistory`. The per-record price is shown on this Actor's Pricing tab.

### Usage patterns

- **Start from a match ID** — Grab match IDs from SofaScore, or first run `schedule` / `liveMatches` and reuse the `matchId` values from the output dataset as `matchIds` input for a `matchPreview` run.
- **Bound the first run** — Use a small `maxMatches`; it caps the delivered records and is also clamped to the run's pay-per-event budget. `maxRequestsPerMinute` and `delaySeconds` control how fast the Actor hits the sources.
- **Trim the fat** — Set `includeOdds: false` to skip the odds feed (one request per match saved) or `includeHistory: false` to skip the Tennis Abstract form/splits blocks (two to three requests per match saved). The metadata-only record stays valid.
- **Handle rate limiting** — SofaScore blocks plain datacenter IPs, so on platform runs the Actor routes through the Apify proxy pool (auto group with an automatic RESIDENTIAL fallback) and impersonates a browser TLS fingerprint; on local runs it uses direct egress. Lower `maxRequestsPerMinute` or add `delaySeconds` for very large runs; the Actor retries 429s with backoff automatically.
- **Estimate the cost of a run** — A typical `matchPreview` run costs about $0.002 in platform usage per delivered record (compute units + residential proxy traffic), so a 100-record run lands near $0.20 before the per-record event price. `maxMatches` is your hard spend cap.

### Input reference

| field | type | default | what it does |
|---|---|---|---|
| `mode` | `matchPreview` / `liveMatches` / `schedule` / `playerHistory` | `"matchPreview"` | Which data operation to run. |
| `matchIds` | `array` |  | SofaScore match IDs for matchPreview mode (from schedule/live runs or the SofaScore site). Each produces one aggregated match preview record. Optional: if omitted, the Actor previews the next scheduled matches of the configured tournament. |
| `tournamentId` | `integer` |  | SofaScore tournament ID to pull upcoming matches from when matchIds is empty in matchPreview mode. Optional. |
| `date` | `string` |  | Calendar date for schedule mode (upcoming matches on that date). Defaults to today (UTC). |
| `player` | `string` |  | Tennis Abstract player slug for playerHistory mode, e.g. NovakDjokovic, IgaSwiatek, JannikSinner (name without spaces). |
| `maxMatches` | `integer` | `20` | Cap on the number of records produced per run (per mode: match previews, live matches, scheduled matches, or history rows). Default 20; set 0 for unlimited. |
| `maxFormMatches` | `integer` | `10` | How many recent matches per player to include in the form block of a match preview. Default 10. |
| `includeOdds` | `boolean` | `true` | Fetch the SofaScore odds feed (moneyline, set winner, totals) for each previewed or live match. One extra request per match. |
| `includeHistory` | `boolean` | `true` | Fetch Tennis Abstract recent-results for each player in a match preview (form + surface splits). One or two extra requests per match. |
| `maxRequestsPerMinute` | `integer` | `120` | Hard rate limit across all requests. Higher values speed up large runs but raise the risk of rate-limiting. Default 120 (each blocked SofaScore request is retried once via the proxy pool with browser TLS impersonation). |
| `delaySeconds` | `number` | `0.5` | Politeness delay between HTTP requests. |
| `proxyConfiguration` | `object` |  | Apify Proxy. On platform runs this defaults to on: SofaScore blocks plain datacenter egress, so the Actor routes through the Apify proxy pool (auto group, with a residential fallback) and uses browser TLS impersonation to deliver data. Local runs are direct unless enabled here. On accounts where the `auto` group only covers datacenter IPs, the Actor automatically retries through the RESIDENTIAL group. |

### FAQ

**Where do the odds come from?**

The SofaScore odds feed for each match (provider 1): typically full-time moneyline, first-set winner and total-games markets, with fractional and decimal values and the last change direction. SofaScore is a single book-implied source, not a multi-book comparison.

**What exactly is in a match preview?**

Match and tournament metadata, both players' names, countries, IDs and current rankings, the pre-match odds block, and per player: last N results (date, tournament, surface, result, opponent, score), surface win rates (hard/clay/grass) and Elo, plus the aggregated head-to-head record between the two players.

**Why is head-to-head sometimes empty?**

Tennis Abstract stores head-to-head aggregates per opponent; if the two players have never met (or the pair is absent from the table), `headToHead` stays null. Recent-results rows for upcoming matches (no score yet) are skipped, not reported as losses.

**Do I need a SofaScore or Tennis Abstract account or API key?**

No. Both sources are public pages/endpoints and the Actor reads them directly over HTTP. SofaScore and Tennis Abstract are unofficial, analytics-focused sources.

**Is this allowed? Terms of service and legality**

SofaScore and Tennis Abstract are unofficial sources and neither offers a licensed API for this data. The Actor reads public pages/endpoints at a polite rate; you are responsible for complying with the sources' terms and with any laws that apply to your use of the data, including any commercial redistribution. This is analytics/odds reference data, not a guarantee of outcomes.

**Why does a run sometimes deliver fewer records than I asked for?**

Records are only delivered for matches that exist and resolve. A match with no odds posted yet or no Tennis Abstract history keeps `odds`/`headToHead` null but is still delivered as a valid record; matches whose tournament feed fails or is blocked are skipped with a log warning. On very large runs the pay-per-event budget and `maxMatches` also cap the total.

**Why do some fields come back null or empty?**

A source may omit the field (e.g. odds not yet posted for a qualifier), or a request may be blocked or missing. Unavailable values stay null instead of being invented, and failed pages are logged and skipped rather than failing the run. On Apify platform runs the Actor routes through the proxy pool with browser TLS impersonation so SofaScore does not block it; on a local run your own IP applies.

### Integration

#### JavaScript

```javascript
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: 'YOUR_TOKEN' });
const run = await client.actor('varnie/tennis-intelligence').call({"mode": "matchPreview", "matchIds": [16737476], "maxMatches": 1});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

#### Python

```python
from apify_client import ApifyClient
client = ApifyClient('YOUR_TOKEN')
run = client.actor('varnie/tennis-intelligence').call(run_input={'mode': 'matchPreview', 'matchIds': [16737476], 'maxMatches': 1})
items = client.dataset(run['defaultDatasetId']).list_items().items
```

#### CLI

```bash
apify call varnie/tennis-intelligence --input '{"mode": "matchPreview", "matchIds": [16737476], "maxMatches": 1}'
```

#### REST

```bash
curl -X POST "https://api.apify.com/v2/acts/varnie~tennis-intelligence/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H 'Content-Type: application/json' -d '{"mode": "matchPreview", "matchIds": [16737476], "maxMatches": 1}'
```

### Support

support@hyperbach.com

*This page is generated from `readme.toml`, the Actor schemas, and a live sample. Edit the sources and run `python gen_readme.py`; do not hand-edit README.md.*

# Actor input Schema

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

Which data operation to run.

## `matchIds` (type: `array`):

SofaScore match IDs for matchPreview mode (from schedule/live runs or the SofaScore site). Each produces one aggregated match preview record. Optional: if omitted, the Actor previews the next scheduled matches of the configured tournament.

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

SofaScore tournament ID to pull upcoming matches from when matchIds is empty in matchPreview mode. Optional.

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

Calendar date for schedule mode (upcoming matches on that date). Defaults to today (UTC).

## `player` (type: `string`):

Tennis Abstract player slug for playerHistory mode, e.g. NovakDjokovic, IgaSwiatek, JannikSinner (name without spaces).

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

Cap on the number of records produced per run (per mode: match previews, live matches, scheduled matches, or history rows). Default 20; set 0 for unlimited.

## `maxFormMatches` (type: `integer`):

How many recent matches per player to include in the form block of a match preview. Default 10.

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

Fetch the SofaScore odds feed (moneyline, set winner, totals) for each previewed or live match. One extra request per match.

## `includeHistory` (type: `boolean`):

Fetch Tennis Abstract recent-results for each player in a match preview (form + surface splits). One or two extra requests per match.

## `maxRequestsPerMinute` (type: `integer`):

Hard rate limit across all requests. Higher values speed up large runs but raise the risk of rate-limiting. Default 120 (each blocked SofaScore request is retried once via the proxy pool with browser TLS impersonation).

## `delaySeconds` (type: `number`):

Politeness delay between HTTP requests.

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

Apify Proxy. On platform runs this defaults to on: SofaScore blocks plain datacenter egress, so the Actor routes through the Apify proxy pool (auto group, with a residential fallback) and uses browser TLS impersonation to deliver data. Local runs are direct unless enabled here. On accounts where the `auto` group only covers datacenter IPs, the Actor automatically retries through the RESIDENTIAL group.

## Actor input object example

```json
{
  "mode": "matchPreview",
  "matchIds": [],
  "player": "NovakDjokovic",
  "maxMatches": 20,
  "maxFormMatches": 10,
  "includeOdds": true,
  "includeHistory": true,
  "maxRequestsPerMinute": 120,
  "delaySeconds": 0.5,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

All scraped records in the default dataset. The Overview table view shows the key match fields.

# 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": "matchPreview",
    "matchIds": [],
    "player": "NovakDjokovic",
    "maxMatches": 20,
    "maxRequestsPerMinute": 120,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("varnie/tennis-intelligence").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": "matchPreview",
    "matchIds": [],
    "player": "NovakDjokovic",
    "maxMatches": 20,
    "maxRequestsPerMinute": 120,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("varnie/tennis-intelligence").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": "matchPreview",
  "matchIds": [],
  "player": "NovakDjokovic",
  "maxMatches": 20,
  "maxRequestsPerMinute": 120,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call varnie/tennis-intelligence --silent --output-dataset

```

## MCP server setup

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

```

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/DmxeqhRaqbuccvDa2/builds/k3LVOFbBL6sH9LZvK/openapi.json
