# Sofascore Tennis Tournaments Scraper (`automation-lab/sofascore-tennis-tournaments`) Actor

Export SofaScore tennis tournament metadata, current title holders, most-title records, season IDs, and chronological season matches for recurring tournament analysis.

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

## Pricing

from $3.04 / 1,000 tournament bundle exporteds

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

## Sofascore Tennis Tournaments Scraper

Export **sofascore tennis tournaments** as tournament-season bundles for recurring tournament analysis. Provide SofaScore unique tournament IDs to retrieve current metadata, title holders, most-title records, available season identifiers and chronological season matches.

Each result contains one tournament and its selected season's matches. This Actor does **not** export historical champion archives, bracket topology, point-by-point statistics, odds or player career histories.

### Who is it for?

- Tennis researchers comparing season schedules and results.
- Sports editors maintaining tournament information and source-linked match summaries.
- Data engineers loading repeatable tournament-season snapshots into a warehouse.
- Analysts who already know their tournament IDs and need a season-oriented export rather than today's live-event feed.

### Why use this Actor?

Tournament and season IDs make repeated exports easy to join. Match IDs provide deduplication keys. The Actor combines finished and upcoming pages, removes duplicate matches and orders them by UTC start time, then applies your match limit. It reports how many distinct matches were available and whether the returned list was truncated.

Current tournament metadata is clearly separated from the selected season. Selecting an older season does not turn the current title holder into that season's champion.

### What data can I extract?

| Field | Meaning |
| --- | --- |
| `tournamentId`, `tournamentName` | Unique tournament identity and source name |
| `sourceUrl`, `scrapedAt` | Attribution and UTC extraction time |
| `category`, `gender`, `surface`, `numberOfSets` | Current source tournament context, nullable |
| `currentTitleHolder`, `currentTitleHolderTitles` | Current holder and their source title count |
| `mostTitles`, `mostTitleHolders` | Current most-title record and participants |
| `seasons` | Available season IDs, names and year labels |
| `selectedSeason` | Season used for matches, or null in metadata-only mode |
| `matchesIncluded` | Whether matches were requested |
| `totalMatchesAvailable` | Distinct matches before the match limit; null without match fetching |
| `matchesTruncated` | Whether later chronological matches were omitted |
| `matches` | Ordered match IDs, source links, UTC starts, rounds, status, participants, winners and set scores |

Unavailable source fields are null or empty arrays. Doubles participants can be combined team names; individual doubles rosters are not promised. Set scores are game counts; tie-break values appear only when published.

### Getting started

1. Open the Actor's Input tab.
2. Keep tournament ID `2363` for Australian Open men, or enter your own tennis unique tournament IDs.
3. Leave `seasonId` absent to select the first season in SofaScore's index.
4. Set `maxMatches` to the number of chronological matches you want per tournament.
5. Run the Actor and open the Results dataset.
6. Export JSON or load the dataset through the Apify API.

```json
{
  "tournamentIds": [2363],
  "includeMatches": true,
  "maxMatches": 200,
  "maxItems": 1
}
```

The source can change between runs. The tested 2026 Australian Open season exposed 244 matches including qualifying matches; the default retained the earliest 200 and reported truncation. This is a tested observation, not a guarantee of future source volume.

### Inputs and limits

| Input | Default | Behavior |
| --- | --- | --- |
| `tournamentIds` | `[2363]` | 1–100 positive unique tournament IDs; duplicates removed in input order |
| `seasonId` | omitted | Exact season ID; only accepted with one unique tournament ID and matches enabled |
| `includeMatches` | `true` | False returns metadata and season index without match requests |
| `maxMatches` | `200` | 1–1000 nested matches per bundle, applied after sorting all fetched matches |
| `maxItems` | `1` | 1–100 tournament bundles globally, applied to deduplicated input IDs before fetching |

IDs beyond `maxItems` are not processed. Zero and unlimited values are unsupported. `maxMatches` is ignored with `includeMatches=false`. An unknown ID, non-tennis tournament or season not listed under the selected tournament fails instead of silently widening the scope.

For a known older season:

```json
{
  "tournamentIds": [2363],
  "seasonId": 69039,
  "maxMatches": 1000,
  "maxItems": 1
}
```

For metadata only:

```json
{
  "tournamentIds": [2363],
  "includeMatches": false,
  "maxItems": 1
}
```

Do not combine the metadata-only mode with `seasonId`.

### Output example

This shortened extract reflects the tested source response. The complete result includes seasons, match arrays and source attribution:

```json
{
  "tournamentId": 2363,
  "tournamentName": "Australian Open, Men",
  "category": "ATP",
  "surface": "Hardcourt outdoor",
  "numberOfSets": 5,
  "currentTitleHolder": { "id": 275923, "name": "Carlos Alcaraz", "country": "Spain" },
  "currentTitleHolderTitles": 1,
  "mostTitles": 10,
  "selectedSeason": { "id": 80012, "name": "Australian Open Men Singles 2026", "year": "2026" },
  "matchesIncluded": true,
  "totalMatchesAvailable": 244,
  "matchesTruncated": true
}
```

The overview table summarizes tournament fields. Use the complete JSON export for nested matches. CSV and Excel are available through Apify; flatten or unwind `matches` downstream for one match per spreadsheet row. That transformation does not change Actor billing.

### How much does it cost to export SofaScore tennis tournaments?

Pay-per-event pricing charges one **$0.005 start event** per run and one **item event per saved tournament bundle**. The BRONZE item price is **$0.0050712**. Nested seasons and matches have no separate event charge; metadata-only bundles use the same item event.

| Saved tournament bundles | BRONZE example total |
| --- | --- |
| 1 | $0.0100712 |
| 5 | $0.030356 |
| 10 | $0.055712 |
| 100 | $0.51212 |

The examples include one start event. Subscription tiers have different item prices; check the active Pricing tab. Failed or duplicate results do not generate item events. The start charge can remain when extraction fails after validation. No result is manufactured to make a failed source appear successful.

### Coverage and reliability

The Actor reads public source JSON, attempting SofaScore directly and using an alternate public translated API surface when the direct endpoint is challenged. No user login, API key, proxy configuration, CAPTCHA-solving service or cached match fixture is used.

Transient transport, 429 and server errors are retried at most three times per request. Both finished and upcoming match directions are paginated. A missing first match page can mean that direction has no matches; a missing advertised later page fails. Each direction has a 40-page safety ceiling. Reaching it fails the tournament export rather than claiming completeness.

Match availability depends on SofaScore's coverage. Qualifying, cancellations, walkovers and doubles appear only if present in the selected tournament season. The Actor does not reconstruct missing rounds or infer winners from incomplete scores.

### Integrations

- Schedule recurring Apify runs and compare `tournamentId`, `selectedSeason.id` and `matches[].matchId` in your database.
- Load full JSON into a warehouse, with a tournament snapshot table and a downstream flattened match table.
- Send completed run notifications through Apify webhooks, then fetch the dataset.
- Export CSV or Excel for editorial summaries, flattening nested arrays where required.

Change detection and notification logic belong in your integration; this Actor produces snapshots and does not maintain a cross-run champion archive.

### API usage

Use your own Apify token through an environment variable, not a hardcoded credential.

#### cURL

```bash
curl -X POST \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"tournamentIds":[2363],"maxMatches":200,"maxItems":1}' \
  "https://api.apify.com/v2/acts/automation-lab~sofascore-tennis-tournaments/run-sync-get-dataset-items"
```

#### JavaScript

```javascript
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('automation-lab/sofascore-tennis-tournaments').call({
  tournamentIds: [2363], maxMatches: 200, maxItems: 1
});
const { items } = await client.dataset(run.defaultDatasetId).listItems({ limit: 1 });
console.log(items);
```

#### Python

```python
import os
from apify_client import ApifyClient
client = ApifyClient(os.environ['APIFY_TOKEN'])
run = client.actor('automation-lab/sofascore-tennis-tournaments').call(
    run_input={'tournamentIds': [2363], 'maxMatches': 200, 'maxItems': 1})
items = client.dataset(run['defaultDatasetId']).list_items(limit=1).items
print(items)
```

For longer exports, start asynchronously and retain the run ID. A client timeout is not permission to start a duplicate run.

### MCP for AI workflows

Use the scoped endpoint:

`https://mcp.apify.com?tools=automation-lab/sofascore-tennis-tournaments`

#### Claude Code

```bash
claude mcp add --transport http apify \
  "https://mcp.apify.com?tools=automation-lab/sofascore-tennis-tournaments"
```

#### Claude Desktop, Cursor, and VS Code

Configure an HTTP MCP server using your client's authentication flow:

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com?tools=automation-lab/sofascore-tennis-tournaments"
    }
  }
}
```

Example prompts:

- “Export Australian Open men's 2026 season using tournament 2363 and season 80012; show the earliest five match IDs and whether the result was truncated.”
- “Get current title-holder metadata and available season IDs for tournament 2363 without fetching matches.”

Discover actual tool names and schemas with `tools/list`. Actor selection also supplies run and storage helper tools; it does not expose exactly one tool. Execute only an authorized invocation. Start once, retain the run and storage IDs, and inspect the same run if not terminal. `call-actor` waits up to 45 seconds, so use bounded status backoff (2, 4, 8, then at most 10 seconds) and a total deadline such as 120 seconds; at deadline report pending with the run ID rather than restarting.

After success, fetch bounded dataset pages with explicit limit, offset and fields. A bundle can be large: project summary fields for model context and keep full exports host-side. An example consumer budget is at most 100 top-level rows and 64 KiB serialized UTF-8 across admitted pages; enforce the byte ceiling host-side even for a single oversized row. Report partial results and continuation offsets. If your client cannot intercept oversized content, do not claim a hard byte guarantee. These are consumer context limits, not Actor collection limits. No universal token-savings guarantee is made.

### Legality, responsible use and diagnostics

Use public data responsibly, respect source terms and applicable laws, and avoid using incomplete sporting data as a guarantee of future outcomes. This independent Actor is not affiliated with or endorsed by SofaScore. Tournament and participant names are source attribution, not branding endorsements.

Failed operations send sanitized diagnostic input, exceptions and Actor/build/run IDs to our private GlitchTip service for repair. Secret fields and URL queries are removed; reports are retained for 30 days. Do not submit credentials through this Actor's public input.

### Storage and support

Results and logs follow your Apify account's retention settings. Delete run storage through Apify when no longer needed. The Actor creates no global match cache or persistent sessions. Only public source paths are sent to the alternate Google Translate surface; private credentials are not accepted. No AI inference is used during a run.

For extraction problems, open an issue on the Actor's Apify page with the tournament IDs, season ID and run link. Do not post credentials or private tokens. Public athlete names and country fields are intentional output; operational logs contain technical IDs and counts instead of full player records.

### FAQ and troubleshooting

**Why is the historical champion different from the current title holder?**

The Actor returns current metadata and separately selected season matches. It does not supply historical champion archives. Do not interpret `currentTitleHolder` as the winner of an older season.

**Why are only 200 matches present?**

That is the default per-tournament match limit. Check `totalMatchesAvailable` and `matchesTruncated`; increase `maxMatches` up to 1000 for more chronological matches.

**Why did my tournament ID fail?**

Use a tennis unique tournament ID from a SofaScore tournament URL, not a match ID or tournament-group ID. A season must be listed under that tournament. Unsupported and inaccessible targets fail visibly.

**Does a successful empty match list mean the source is broken?**

Not necessarily: a season can have no published events in either direction. The tournament metadata and season index still identify the requested source. Unexpected response shapes and failures are not converted into successful empty exports.

**Can I export many tournaments?**

Provide up to 100 IDs and raise `maxItems`. Each accepted bundle is a billable item; IDs beyond the global limit are not queried. Large requests may exceed the run timeout, so use sensible batches.

### Related Actors

- [SofaScore Live Events & Statistics Scraper](https://apify.com/automation-lab/sofascore-live-events-statistics-scraper) for date/live/exact-event snapshots rather than tournament-season bundles.
- [Tennis Abstract Player Match History Scraper](https://apify.com/automation-lab/tennis-abstract-player-match-history-scraper) for player-oriented history from a different source.

# Changelog

This Actor's version history is a separate document: https://apify.com/automation-lab/sofascore-tennis-tournaments/changelog.md

# Actor input Schema

## `tournamentIds` (type: `array`):

SofaScore tennis unique tournament IDs, not individual match or tournament-group IDs. Duplicates are removed in input order. Defaults to Australian Open men (2363). Up to 100 supplied IDs; maxItems limits exported bundles. Non-tennis or unknown IDs fail the run.

## `seasonId` (type: `integer`):

Exact season ID listed for the tournament. Requires exactly one unique tournament ID and includeMatches=true; incompatible combinations fail. If omitted, the first season in SofaScore's season index is selected. This does not change the current title holder into a historical champion.

## `includeMatches` (type: `boolean`):

Fetch finished and upcoming match pages for the selected season, deduplicate by match ID and sort by UTC start time then match ID. False exports only tournament metadata and season IDs, with no selectedSeason or match fetches; seasonId is then rejected. No bracket topology or champion archive is returned.

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

Maximum nested match objects per tournament bundle, default 200, range 1–1000 (zero is invalid). All available pages are fetched before the earliest chronological matches are retained; totalMatchesAvailable and matchesTruncated disclose truncation. Ignored when includeMatches=false. A source safety ceiling of 40 pages per direction fails rather than claiming a complete export.

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

Global maximum top-level tournament results for this run, default 1, range 1–100; not a match count. Applied to the deduplicated IDs in input order before any requests. IDs beyond this limit are not processed. Zero/unlimited is not supported.

## Actor input object example

```json
{
  "tournamentIds": [
    2363
  ],
  "includeMatches": true,
  "maxMatches": 200,
  "maxItems": 1
}
```

# Actor output Schema

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

Tournament-season dataset overview; use the full JSON export for nested season and match arrays.

# 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 = {
    "tournamentIds": [
        2363
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("automation-lab/sofascore-tennis-tournaments").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 = { "tournamentIds": [2363] }

# Run the Actor and wait for it to finish
run = client.actor("automation-lab/sofascore-tennis-tournaments").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 '{
  "tournamentIds": [
    2363
  ]
}' |
apify call automation-lab/sofascore-tennis-tournaments --silent --output-dataset

```

## MCP server setup

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

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/nOicZnTSPsPLdFdvh/builds/XpxO2cTJENgeqeIzK/openapi.json
