# Sofascore Live Tennis Scraper (`automation-lab/sofascore-live-tennis-scraper`) Actor

Export current Sofascore live tennis match snapshots with player and tournament IDs, match status, set scores, point scores, and observation timestamps. Use bounded runs for score dashboards and recurring analysis; no odds, point-by-point history, or continuous stream.

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

## Pricing

from $1.92 / 1,000 item processeds

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 Live Tennis Scraper

Export **sofascore live tennis** match snapshots for score dashboards, recurring analysis, and match discovery. Each run reads the current live tennis feed once and saves bounded, normalized match records with participants, tournament context, status, set scores, game points, and observation time.

This is a snapshot exporter, not a continuous stream. It does not collect odds, historical results, point-by-point rallies, detailed match statistics, or player biographies.

### What does it do?

- Finds tennis events currently marked in progress by SofaScore.
- Exports singles and doubles when present in the source feed.
- Preserves match, participant, tournament and unique-tournament IDs.
- Keeps set scores and current game points separate from sets won.
- Filters by gender, singles/doubles and exact tournament IDs.
- Deduplicates match IDs within a run and applies a global accepted-record limit.

### Who is it for?

Sports analysts can append snapshots to a warehouse and compare changes by match ID. Dashboard developers can refresh a current-score table. Researchers can use the IDs to join live observations to their own tournament datasets.

It is not suitable for latency-sensitive trading, guaranteed-complete event coverage, or reconstructing every rally between snapshots.

### Why use this Actor?

The output is one structured row per accepted match, rather than a browser page or a nested raw feed. Source-update time and collection time are separate, so consumers can reason about upstream delay. Bounded runs avoid leaving a streaming connection open. No source account, API key or user-supplied proxy is required.

### Getting started

1. Keep the default input to collect up to 100 current matches.
2. Optionally choose women, men, singles or doubles.
3. Run the Actor and open the default dataset.
4. Export JSON or CSV, or connect the dataset to your downstream workflow.
5. To compare observations, schedule separate runs and store their datasets externally.

```json
{
  "maxItems": 100,
  "gender": "all",
  "matchType": "all",
  "tournamentIds": []
}
```

### Input parameters

| Parameter | Default | Meaning |
|---|---|---|
| `maxItems` | `100` | Global accepted-match limit, 1–500, applied after filters and deduplication. No unlimited or zero mode. |
| `gender` | `all` | Exact source event tag: `F`, `M`, or all. Mixed/untagged records remain eligible only with all. |
| `matchType` | `all` | Exact source category tag: `singles`, `doubles`, or all. A specific choice excludes untagged records. |
| `tournamentIds` | `[]` | ANY-of exact numeric `tournament.id` values; empty means all. At most 100 positive integers. |

Filters combine using AND. Tournament IDs are not `uniqueTournamentId` or season IDs. Obtain them from an unfiltered snapshot first; copying a unique-tournament ID into this filter may return nothing. Unsupported fields and malformed values fail validation rather than being silently ignored.

### Output data

| Fields | Meaning |
|---|---|
| `matchId`, `matchUrl` | Stable SofaScore event identity and public match link. |
| `homeId`, `homeName`, `awayId`, `awayName` | Participants as labeled by the source; home/away does not imply home-court advantage. |
| `tournamentId`, `tournamentName`, `uniqueTournamentId`, `category` | Tournament and competition context. |
| `gender`, `matchType` | Source filter tags, or null when absent. |
| `status`, `statusCode` | Human-readable live status and source status code. |
| `homeSets`, `awaySets` | Sets won, not current-set games. |
| `homePoint`, `awayPoint` | Current game points as strings, including advantage when supplied. |
| `setScores` | Up to five available sets, including optional tiebreak values. |
| `startTime` | Scheduled/source start time in UTC when available. |
| `sourceUpdatedAt` | Last change timestamp supplied by SofaScore; not a freshness guarantee. |
| `observedAt` | UTC collection time for this snapshot. |

Optional values are null when absent; missing scores are not invented as zero. Doubles participants may be source-provided pair names. The Actor does not fetch individual doubles biographies.

### Output example

This shortened example reflects the shape observed in a live-feed test; values will change with the match schedule.

```json
{
  "matchId": 17275017,
  "homeName": "Ann Li",
  "awayName": "Janice Tjen",
  "tournamentName": "Wuhan, China",
  "status": "2nd set",
  "homeSets": 1,
  "awaySets": 0,
  "homePoint": "0",
  "awayPoint": "0",
  "setScores": [
    {"set": 1, "home": 6, "away": 1, "homeTiebreak": null, "awayTiebreak": null},
    {"set": 2, "home": 1, "away": 2, "homeTiebreak": null, "awayTiebreak": null}
  ],
  "observedAt": "2026-10-15T14:03:33.000Z"
}
```

### How much does it cost to export Sofascore live tennis snapshots?

The Actor uses pay-per-event pricing: one `start` event per valid run, plus one `item` event per saved match snapshot. Invalid input is rejected before the start charge. Empty valid snapshots have only the start fee. Retry attempts, rejected matches and duplicate IDs have no separate item charge.

BRONZE examples use a $0.0005 start fee and $0.0032 per match snapshot:

| Saved matches | Estimated Actor charge |
|---:|---:|
| 1 | 0.0037 USD total (start + 1 match) |
| 10 | 0.0325 USD total (start + 10 matches) |
| 100 | 0.3205 USD total (start + 100 matches) |

The applicable six-tier event prices appear in the pricing panel. Spend tiers follow qualifying aggregate monthly Apify Store spend, not a private per-Actor volume discount. Repeated scheduled runs each incur their own start and output events. Apify plan/platform costs may apply separately. These are usage estimates, not guaranteed invoices; the configured charge limit can truncate saved results.

### Limits and source behavior

A run takes one feed snapshot, not all matches on a date. There is no pagination or historical backfill. Fewer than the requested maximum is normal when fewer events match. A legitimate empty feed succeeds with zero rows. Unsupported response shapes, persistent source errors and challenge pages fail explicitly instead of producing a false empty success.

The Actor tries the public SofaScore JSON feed and, when necessary, an alternate public JSON surface delivered through Google's translation infrastructure. Transient/network failures are retried at most twice per surface with per-request deadlines. No residential proxy, login, browser session or paid external API is enabled. Both surfaces may become unavailable; no uptime or real-time latency guarantee is made.

Source updates can lag or pause. `observedAt` means collection time, not when a point happened. Missing observations between runs cannot be reconstructed. Live event coverage, tagging and optional fields depend on SofaScore.

### Integrations

- **Google Sheets:** append snapshots with `matchId` and `observedAt` as a composite observation key, or upsert the latest row per match.
- **Warehouse:** retain raw snapshots externally and compute score changes without interpreting missing rows as a finished result.
- **Dashboard:** display the observation time and source-update time alongside the score.
- **Webhooks:** use Apify's run-completed webhook to fetch the dataset after success. The Actor itself does not send score-change alerts.

### API usage

Use your own Apify token through the authorization header. Never put tokens in shared examples or public datasets.

```bash
curl -X POST \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"maxItems":10,"gender":"F"}' \
  'https://api.apify.com/v2/acts/automation-lab~sofascore-live-tennis-scraper/run-sync-get-dataset-items'
```

JavaScript with the Apify client:

```javascript
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('automation-lab/sofascore-live-tennis-scraper').call({ maxItems: 10 });
const { items } = await client.dataset(run.defaultDatasetId).listItems({ limit: 10 });
console.log(items);
```

Python:

```python
import os
from apify_client import ApifyClient
client = ApifyClient(os.environ['APIFY_TOKEN'])
run = client.actor('automation-lab/sofascore-live-tennis-scraper').call(run_input={'maxItems': 10})
print(client.dataset(run['defaultDatasetId']).list_items(limit=10).items)
```

### MCP integration

Select a stable Actor-specific tool subset:

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

Claude Desktop, Cursor, and VS Code configuration (for clients supporting HTTP MCP):

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

Configure authorized Apify authentication in your client. Discover actual names and schemas with scoped `tools/list`; Actor selection also adds run and storage tools, not exactly one tool. Example prompt: “Collect up to ten current women's singles tennis snapshots and show participants, score and observation time.”

For an authorized invocation, start once and retain the run ID, status and storage IDs. They are metadata, not results. A server wait is limited to 0–45 seconds; a nonterminal run should be checked using `get-actor-run` for that same run with bounded backoff (2, 4, 8, then at most 10 seconds) and a 120-second total deadline. Client request timeout should exceed the server wait plus transport margin. Recover the known run after a client timeout; never restart merely to poll. If the ID is unknown, report uncertainty. At the deadline, report pending and stop. Terminal failures are failures even if partial rows exist.

After success, read bounded pages using `get-dataset-items`: for example 20 source rows per page, at most 100 rows and 64 KiB serialized UTF-8 admitted to model context, whichever comes first. Enforce the byte ceiling host-side before model injection, track source offsets, and report returned count, continuation offset and truncation honestly. Keep full exports outside model context. Clients unable to intercept oversized responses cannot promise a hard byte guarantee. Discovery transport size and client caching do not establish universal token savings.

### Legality and data handling

This Actor processes public sports-event information, including publicly listed professional participant names and IDs. No AI model is used at runtime or to generate dataset values. No credentials, private account data, cookies or source payload dumps are included in output or ordinary logs. The source and alternate delivery infrastructure receive anonymous public feed requests, not user credentials or filter values.

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. Apify stores run datasets and logs according to your account retention settings; delete runs/storage through Apify when no longer needed. The Actor creates no persistent cross-run cache or external copy of successful datasets. Your downstream exports have their own retention policy.

Use the data lawfully and respect source rights and applicable terms. This independent Actor is not affiliated with, endorsed by or sponsored by SofaScore. Standard Apify terms apply; no custom end-user agreement is added.

### FAQ and troubleshooting

**Why are there no rows?** There may be no live events, or your exact-tag/tournament filter may exclude them. Try the default input and confirm `tournamentId`, not `uniqueTournamentId`.

**Can I export a past date or a specific finished match?** No. This product is limited to events in the current live feed.

**Does scheduling create point-by-point history?** No. You can keep separate snapshots, but any changes between observations may be missed.

**Why does the source update time differ from observation time?** One is supplied by SofaScore; the other is when the feed was collected. Neither promises instant delivery.

**What happens if the source blocks requests?** The bounded alternate-surface attempt is automatic. If both surfaces fail, the run fails clearly. Retry later rather than starting many parallel copies.

For support, open an issue on this Actor's Apify page with the run link and a minimal non-secret input. Do not post tokens or private credentials.

### Related Actors

- [Sofascore Tennis Tournaments Scraper](https://apify.com/automation-lab/sofascore-tennis-tournaments): tournament season IDs, metadata and chronological season matches, rather than current live-feed discovery.
- [WTA Tennis Rankings Scraper](https://apify.com/automation-lab/wta-rankings): ranking observations for a separate player-ranking workflow.

# Changelog

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

# Actor input Schema

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

Global limit after filtering and match-ID deduplication. Default 100, range 1–500; zero and unlimited are not supported. One current live feed is fetched, so fewer records may be available.

## `gender` (type: `string`):

Exact match against SofaScore event gender tags. all includes mixed or untagged events; F or M excludes events without that exact tag.

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

Exact match against SofaScore singles/doubles event category tags. all includes untagged events; a specific choice excludes events without that tag.

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

Optional ANY-of exact numeric tournament.id filter, not uniqueTournament.id or season IDs. Empty means all tournaments. Combined with gender and match type using AND. At most 100 positive integer IDs.

## Actor input object example

```json
{
  "maxItems": 10,
  "gender": "all",
  "matchType": "all",
  "tournamentIds": []
}
```

# Actor output Schema

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

Open the default dataset containing normalized live tennis match snapshots.

# 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 = {
    "maxItems": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("automation-lab/sofascore-live-tennis-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 = { "maxItems": 10 }

# Run the Actor and wait for it to finish
run = client.actor("automation-lab/sofascore-live-tennis-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 '{
  "maxItems": 10
}' |
apify call automation-lab/sofascore-live-tennis-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,automation-lab/sofascore-live-tennis-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/HVVKNk4cBQ7gRjHuN/builds/v9SWtZvTNDuWt1Af4/openapi.json
