# Tennis Abstract Player Match Scraper (`mikolabs/tennis-abstract-player-match-scraper`) Actor

Extract comprehensive Tennis Abstract player profiles, historical match logs, surface splits (Clay/Hard/Grass), Elo ratings, and match charting stats alongside real-time live tennis scores, odds, and tournament draw history.

- **URL**: https://apify.com/mikolabs/tennis-abstract-player-match-scraper.md
- **Developed by:** [Mikolabs](https://apify.com/mikolabs) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $15.00 / 1,000 results

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?

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

### What does Tennis Abstract Player Match Scraper do?

**Tennis Abstract Player Match Scraper** is a high-performance data extraction tool designed to collect comprehensive, real-time, and historical professional tennis data from [Tennis Abstract](https://www.tennisabstract.com) and [SofaScore](https://www.sofascore.com). It automatically resolves player identity mappings and extracts historical player match logs, Elo ratings, surface performance splits, Match Charting Project (MCP) metrics, head-to-head records, live match scores, date-level betting odds, and full tournament season draws.

Running on the Apify platform, Tennis Abstract Player Match Scraper provides:

- **Automated Scheduling**: Run scraper jobs periodically (e.g., daily ranking updates or real-time live score updates) using Apify Schedules.
- **REST API & Webhooks**: Seamlessly integrate Tennis Abstract player profiles, Elo statistics, and pro match datasets into your analytical database, sports application, or betting algorithm.
- **Anti-Bot & Proxy Rotation**: Pre-configured with Chrome TLS fingerprint impersonation and Apify residential proxy support to bypass Cloudflare anti-scraping challenges.
- **Multi-Format Data Exports**: Export datasets instantly in JSON, CSV, Excel, XML, or HTML.

> **Unofficial Scraper**: This independent Actor is not affiliated with or endorsed by Tennis Abstract or SofaScore.

***

### Why use Tennis Abstract Player Match Scraper?

Extracting data from Tennis Abstract and live sports feeds manually or through fragile custom scrapers is challenging due to complex JavaScript fragments (`jsfrags`), varied naming conventions, and strict anti-bot firewalls. **Tennis Abstract Player Match Scraper** bridges this gap by automatically indexing player registries, parsing JS data fragments, and delivering clean, normalized, and structured records on demand.

#### Key Use Cases

- **Predictive Sports Analytics & AI Models**: Train machine learning models with historical player match logs, career and 52-week surface splits (Hard, Clay, Grass), dominance ratios, and Elo ratings.
- **Match Charting Project (MCP) Insights**: Access detailed tactical charting metrics including serve speed distributions, winners vs. unforced errors, rally length breakdowns, and net frequency points.
- **Betting Intelligence & Odds Monitoring**: Combine historical player form with live set, game, and point scores alongside decimal and fractional odds to detect value betting opportunities.
- **Player Scouting & Rivalry Tracking**: Analyze head-to-head records, tiebreak win percentages, break point conversion efficiency, and year-end ranking trajectories across ATP, WTA, Challenger, and ITF levels.

***

### How to use Tennis Abstract Player Match Scraper

Getting started takes less than two minutes:

1. **Sign up or log in** to your free [Apify Console](https://console.apify.com/).
2. Navigate to the **Tennis Abstract Player Match Scraper** Actor page and click **Try for free**.
3. Select your desired **Scraper mode** under the **Input** tab (e.g., *Player details*, *Live matches*, *Tournaments*, *Point by point*, *Tournament seasons*, or *Tournament details*).
4. Fill in the required or optional parameters (e.g., `playerId: 275923` for Carlos Alcaraz or `206570` for Jannik Sinner).
5. Click **Start** to run the Actor.
6. When the run finishes, navigate to the **Dataset** tab to view, filter, or download your structured records.

***

### Input

Tennis Abstract Player Match Scraper accepts a clean JSON configuration with mode-specific fields. You can configure it directly via the visual form in Apify Console or via API request payload.

#### Scraper Modes Overview

| Mode | Title | Description | Key Parameters |
|---|---|---|---|
| `playerDetails` | **Player profile & career analytics** | Historical match logs, Elo ratings, surface splits, charting stats & H2Hs | `playerId` (positive integer) |
| `liveMatches` | **Live matches & odds** | Instantaneous snapshots of pro tour matches in progress with live scores & odds | `maxMatches` (optional cap) |
| `tournaments` | **Scheduled tournaments by date** | Discover all tournaments scheduled on a calendar date across ATP/WTA/ITF | `date` (`YYYY-MM-DD`) |
| `pointByPoint` | **Point-by-point match progression** | Full set, game, and point progression with server & period statistics | `matchId` (positive integer) |
| `tournamentSeasons` | **Available tournament seasons** | Retrieve all past and active season IDs for any unique tournament | `tournamentId` (positive integer) |
| `tournamentDetails` | **Tournament details & match draw** | Tournament metadata, defending champions, and full match draw list | `tournamentId`, `seasonId` (optional) |

#### JSON Input Example: Player Analytics (`playerDetails`)

```json
{
  "mode": "playerDetails",
  "playerId": 275923
}
```

#### JSON Input Example: Live Matches (`liveMatches`)

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

#### Input Fields Specification

- **`mode`** (*string*, default: `"playerDetails"`): Selects one of the six data extraction operations.
- **`playerId`** (*integer*, required for `playerDetails`): Positive SofaScore player ID (e.g., `275923` for Carlos Alcaraz, `206570` for Jannik Sinner, `14882` for Novak Djokovic). The Actor automatically resolves the corresponding Tennis Abstract profile.
- **`maxMatches`** (*integer*, optional for `liveMatches`): Maximum number of live matches to extract. Leave empty or set to `0` for no limit.
- **`date`** (*string*, required for `tournaments`): Calendar date in `YYYY-MM-DD` format (e.g., `"2024-09-11"`).
- **`matchId`** (*integer*, required for `pointByPoint`): Positive SofaScore match ID.
- **`tournamentId`** (*integer*, required for `tournamentSeasons` and `tournamentDetails`): Positive tournament ID (e.g., `2361` for Wimbledon).
- **`seasonId`** (*integer*, optional for `tournamentDetails`): Positive season ID. Omit to automatically resolve the newest season.
- **`proxyConfiguration`** (*object*, optional): Apify Proxy configuration settings.

***

### Output

Every run produces structured dataset records pushed directly to the default Apify Dataset. You can download the dataset in various formats such as **JSON, HTML, CSV, Excel, or XML**.

#### Sample Output: Player Match Record (`playerDetails`)

```json
{
  "id": 275923,
  "fullName": "Carlos Alcaraz",
  "country": "ESP",
  "currentRank": 3,
  "recordType": "recentResult",
  "date": "31-Aug-2026",
  "tournament": "US Open",
  "surface": "Hard",
  "round": "QF",
  "rank": 3,
  "opponentRank": 9,
  "result": "L",
  "opponentName": "Ben Shelton",
  "opponentCountry": "USA",
  "score": "6-7(5) 6-1 6-3 1-6 7-6(7)",
  "dominanceRatio": 1.12,
  "acePercentage": 8.4,
  "doubleFaultPercentage": 2.8,
  "firstServeInPercentage": 67.2,
  "firstServeWonPercentage": 74.5,
  "secondServeWonPercentage": 56.1,
  "breakPointsSaved": "5/7",
  "time": "4:28"
}
```

#### Sample Output: Player Profile Summary (`playerDetails`)

```json
{
  "id": 275923,
  "url": "https://www.tennisabstract.com/cgi-bin/player.cgi?p=CarlosAlcaraz",
  "fullName": "Carlos Alcaraz",
  "country": "ESP",
  "dateOfBirth": "2003-05-05",
  "heightCm": 183,
  "handedness": "Right-handed",
  "backhand": "Two-handed",
  "currentRank": 3,
  "peakRank": 1,
  "eloRank": 2,
  "eloRating": 2147,
  "recordType": "playerProfile"
}
```

#### Sample Output: Live Match & Odds (`liveMatches`)

```json
{
  "id": 16979794,
  "slug": "barrientos-kestelboim-behar-romboli",
  "url": "https://www.sofascore.com/barrientos-kestelboim-behar-romboli/16979794",
  "matchType": "doubles",
  "surface": "Clay",
  "status": "2nd set",
  "tournamentName": "Seville, Spain, Doubles",
  "firstToServe": "home",
  "score": {
    "home": 1,
    "away": 0,
    "homePoint": "40",
    "awayPoint": "30",
    "sets": [
      { "set": 1, "home": 6, "away": 4 },
      { "set": 2, "home": 3, "away": 2 }
    ]
  },
  "odds": {
    "home": { "fractional": "4/6", "decimal": 1.67 },
    "away": { "fractional": "6/5", "decimal": 2.2 }
  }
}
```

***

### Data Table

The table below outlines the primary data fields extracted across Tennis Abstract Player Match Scraper modes:

| Field Name | Category | Type | Description |
|---|---|---|---|
| `id` | Identifier | `integer` | Unique ID of the player, match, or tournament |
| `fullName` | Profile | `string` | Player full legal name |
| `country` | Profile | `string` | Player nationality / country code |
| `currentRank` / `peakRank` | Ranking | `integer` | Current official pro ranking and career-high ranking |
| `eloRank` / `eloRating` | Rating | `integer` | Tennis Abstract Elo rating and global Elo ranking |
| `recordType` | Metadata | `string` | Record type (`"playerProfile"`, `"recentResult"`, `"tourLevelSeason"`, `"careerTourLevelSplit"`, `"matchChartingServe"`, etc.) |
| `tournament` | Schedule | `string` | Tournament name |
| `surface` | Environment | `string` | Court surface (`"Hard"`, `"Clay"`, `"Grass"`, `"Indoors"`) |
| `round` | Schedule | `string` | Round stage (`"F"`, `"SF"`, `"QF"`, `"R16"`, `"R32"`, `"R64"`, `"R128"`) |
| `opponentName` | Match | `string` | Opponent's full name |
| `opponentRank` | Match | `integer` | Opponent's pro ranking at match date |
| `result` | Match | `string` | Match outcome (`"W"` for win, `"L"` for loss) |
| `score` | Match | `string`/`object` | Final match scoreline or live set/game score |
| `dominanceRatio` | Analytics | `number` | Ratio of return points won % to service points lost % |
| `firstServeWonPercentage` | Analytics | `number` | First serve points won percentage |
| `breakPointsSaved` | Analytics | `string` | Break points saved ratio (e.g. `"5/7"`) |
| `winnersAndUnforcedErrors` | MCP | `object` | Total winners, unforced errors, rally ratios, and forehand/backhand winners |
| `serveSpeed` | Tracking | `object` | Average 1st/2nd serve speeds, max speed, and T/Wide placement distributions |
| `odds` | Betting | `object` | Decimal and fractional odds for home and away sides |

***

### Pricing / Cost estimation

Tennis Abstract Player Match Scraper executes lightweight, asynchronous HTTP requests using direct JSON and fragment parsing rather than spawning resource-heavy browser sessions, making it fast and economical.

- **Average Run Time**: 1 to 4 seconds per request.
- **Memory Allocation**: 512 MB to 1024 MB is optimal for all modes.
- **Compute Unit (CU) Consumption**: Approximately 0.0002 to 0.001 CU per execution.
- **Free Tier Coverage**: With Apify's monthly free tier ($5 credit), you can execute **thousands of player and match queries every month** completely free of charge.

***

### Tips or Advanced options

1. **Chain Player & Match Discovery**: Use `tournaments` or `tournamentDetails` to discover active `playerId` and `matchId` parameters, then pass those into `playerDetails` or `pointByPoint` for automated data enrichment pipelines.
2. **Automate with Schedules**: Under the Actor's **Schedules** tab, configure recurring runs (e.g., `0 6 * * *` for daily morning ranking updates or `*/10 * * * *` during Grand Slam matches).
3. **Apify Residential Proxies**: While Tennis Abstract requests run smoothly without proxies, high-volume SofaScore requests benefit from Apify Residential Proxies (`{ "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"] }`).
4. **API & Webhook Integrations**: Stream output records directly to your web server using Apify Webhooks triggered upon run completion.

***

### FAQ, disclaimers, and support

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

Yes. Tennis Abstract Player Match Scraper collects publicly accessible pro sports records and tournament statistics. It does not access private user accounts or confidential data. Please ensure your data consumption respects local legal frameworks and source terms of use.

#### How does Tennis Abstract mapping work?

When you supply a positive SofaScore `playerId`, the Actor matches the player's name and demographic profile against Tennis Abstract's indexed registry (`mwplayerlist.js`), extracting complete historical splits, Elo rankings, and charting metrics.

#### What if a query returns zero rows?

If `liveMatches` is executed during off-peak hours with no active tournament sessions, or `tournaments` is checked on a date with no scheduled events, the Actor completes cleanly with an empty dataset.

#### Support & Custom Development

Need assistance, additional analytical splits, or customized tennis scraping features? Feel free to open a ticket on the Actor's **Discussion** tab in Apify Console.

# Actor input Schema

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

Select the data extraction operation to perform. Depending on the mode selected, fill in the corresponding parameters below.

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

*(Used only in `liveMatches` mode)* Limit the number of live matches returned. Leave empty or set to `0` to extract all currently active pro matches.

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

*(Used only in `tournaments` mode)* Discover all tournaments scheduled on this calendar date across ATP, WTA, Grand Slams, Challengers, and ITF circuits.

## `matchId` (type: `integer`):

*(Used only in `pointByPoint` mode)* Positive match ID to inspect point-by-point progressions and period statistics. You can find match IDs in results from `liveMatches` or `tournamentDetails`.

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

*(Used in `tournamentSeasons` and `tournamentDetails` modes)* Positive unique tournament identifier. Example: `2361` for Wimbledon, `2287` for Davis Cup.

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

*(Used only in `tournamentDetails` mode)* Optional specific season ID (e.g., `63966` for Wimbledon 2024). Leave empty to automatically retrieve the latest/current season.

## `playerId` (type: `integer`):

*(Used only in `playerDetails` mode)* Positive SofaScore player ID. The scraper automatically maps the player to Tennis Abstract and parses rankings, career splits, match charting, and head-to-heads. Example: `275923` (Carlos Alcaraz), `206570` (Jannik Sinner).

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

Select proxies to be used by the scraper. Using Apify Residential or Datacenter proxies is recommended to avoid rate limits and Cloudflare blocking on SofaScore.

## Actor input object example

```json
{
  "mode": "liveMatches",
  "maxMatches": 10,
  "date": "2024-09-11",
  "matchId": 16979809,
  "tournamentId": 2361,
  "seasonId": 63966,
  "playerId": 275923,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

The direct rows produced by the selected mode. Collection successes may produce zero or many rows, singleton successes produce one row, and failed scrape operations produce one error row when it can be saved.

# 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": "liveMatches",
    "maxMatches": 10,
    "date": "2024-09-11",
    "matchId": 16979809,
    "tournamentId": 2361,
    "seasonId": 63966,
    "playerId": 275923,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("mikolabs/tennis-abstract-player-match-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 = {
    "mode": "liveMatches",
    "maxMatches": 10,
    "date": "2024-09-11",
    "matchId": 16979809,
    "tournamentId": 2361,
    "seasonId": 63966,
    "playerId": 275923,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("mikolabs/tennis-abstract-player-match-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 '{
  "mode": "liveMatches",
  "maxMatches": 10,
  "date": "2024-09-11",
  "matchId": 16979809,
  "tournamentId": 2361,
  "seasonId": 63966,
  "playerId": 275923,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call mikolabs/tennis-abstract-player-match-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,mikolabs/tennis-abstract-player-match-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/XeT2igqmzyzyomOdI/builds/rCuARoBpKvNs86lXG/openapi.json
