Tennis Abstract Player Elo and Surface Statistics avatar

Tennis Abstract Player Elo and Surface Statistics

Pricing

from $3.53 / 1,000 item extracteds

Go to Apify Store
Tennis Abstract Player Elo and Surface Statistics

Tennis Abstract Player Elo and Surface Statistics

Export Tennis Abstract player Elo ratings, current rankings and aggregate surface serving and returning statistics for scouting, including Alcaraz and Sinner profiles.

Pricing

from $3.53 / 1,000 item extracteds

Rating

0.0

(0)

Developer

Automation Lab

Automation Lab

Maintained by Community

Actor stats

0

Bookmarked

3

Total users

2

Monthly active users

20 days ago

Last modified

Share

Refresh Tennis Abstract player Elo ratings, current tour rankings and aggregate serving/returning statistics for scouting and analytics. An Alcaraz Tennis Abstract lookup can return Carlos Alcaraz's player snapshot and career or last-52-week surface splits without exporting individual match histories. Jannik Sinner profiles work through the same route.

What does this Actor export?

One default-dataset record per unique player, containing:

  • the source's player name, slug and country;
  • current official tour rank when available;
  • current all-surface Elo rank and rounded profile rating when available;
  • career and/or rolling last-52-week tour-level surface aggregates;
  • serving, returning and win/loss metrics with source provenance;
  • an extraction timestamp and a verified SofaScore ID when in the curated crosswalk.

It does not return match histories, live scores, odds, individual point sequences, charting tables or a complete Elo leaderboard.

Who is it for?

Scouts can compare player performance on hard, clay and grass courts. Analysts can refresh player-level features for models without first collecting thousands of match rows. Editors can check player rankings and aggregate records against the linked source.

Why use this Actor?

The output separates player snapshots from surface aggregates. All routes use the same filters and global player limit. Missing data remains null or absent rather than being invented. The Actor reads Tennis Abstract's own aggregate tables instead of pretending that a small recent-match sample represents an entire career.

Getting started

  1. Enter exact Tennis Abstract slugs, for example CarlosAlcaraz and JannikSinner.
  2. Select career, last52, or both periods.
  3. Select the surfaces you need.
  4. Set a maximum number of unique player profiles.
  5. Run the Actor and open the Player statistics dataset.
  6. Export JSON for nested aggregates, or flatten the array downstream for tabular analysis.
{
"playerSlugs": ["CarlosAlcaraz", "JannikSinner"],
"periods": ["career", "last52"],
"surfaces": ["Hard", "Clay", "Grass"],
"maxItems": 2
}

Alcaraz Tennis Abstract and Sinner inputs

Use exact source identifiers, not fuzzy player names. Carlos Alcaraz uses CarlosAlcaraz; Jannik Sinner uses JannikSinner. Other valid source slugs, such as NovakDjokovic, can be supplied, but their available statistics depend on Tennis Abstract's current page.

Profile URLs also work:

{
"playerUrls": ["https://www.tennisabstract.com/cgi-bin/player.cgi?p=CarlosAlcaraz"],
"periods": ["last52"],
"surfaces": ["Hard"]
}

Optional SofaScore mapping

sofaScoreIds uses a deliberately bounded, verified crosswalk. It currently supports only 206570 → JannikSinner. This mapping was checked against SofaScore's public player response. The Actor does not call SofaScore during ordinary runs and does not return SofaScore statistics.

Unknown IDs fail clearly; they are never fuzzily assigned to another player. Use a Tennis Abstract slug or URL for Alcaraz and all players outside this crosswalk. This is not a universal SofaScore-to-Tennis-Abstract resolver.

{ "sofaScoreIds": [206570], "surfaces": ["Hard"], "periods": ["career"] }

Input parameters

ParameterDefaultBehavior
playerSlugsNoneExact, case-sensitive source identifiers; at most 100 entries
playerUrlsNoneSource profile URLs on tennisabstract.com or www.tennisabstract.com only; at most 100 entries
sofaScoreIdsNoneSupported verified integer IDs only; currently 206570
periodsBothNonempty array containing career and/or last52
surfacesAll fourNonempty array containing exact Hard, Clay, Grass, Carpet labels
maxItems10Global unique-player profile limit from 1 to 100; not a surface-row limit
proxyConfigurationApify datacenterSet useApifyProxy false for direct HTTP or supply custom proxies; Apify RESIDENTIAL groups rejected

At least one input route is required. Slugs, URLs and IDs are combined in that order, then deduplicated before limiting. Identical filters apply to every route. Zero and unlimited limits are unsupported. Run and billing limits can stop before the requested profile count.

Output fields

FieldMeaning
playerSlug, playerNameSource identity, validated against the profile
countryPublished country code or null
currentRankOfficial tour rank published on the profile, or null
eloRank, eloRatingCurrent all-surface profile Elo; source rating is rounded
sofaScoreIdVerified curated mapping when available; otherwise null
surfaceStatsSelected source aggregate rows, not individual matches
sourceUrlCanonical player profile URL
statisticsSourceUrlExact public fragment containing aggregate tables
scrapedAtUTC extraction timestamp, not source-update time

Each aggregate has period, surface, matches, wins, losses, win percentage, matches with statistics, hold/break percentages, ace/double-fault percentages, first-serve-in percentage, first/second-serve points won percentages, service/return/total points won percentages and dominance ratio.

Percentage values use 0–100 percentage points: 67.5 means 67.5%, not 0.675. Dominance ratio is not a percentage. The number of matches with statistics can be smaller than the total matches played.

Example output

This shortened example reflects the locally observed Alcaraz record. The complete result includes all selected surface rows and metrics.

{
"playerSlug": "CarlosAlcaraz",
"playerName": "Carlos Alcaraz",
"country": "ESP",
"currentRank": 3,
"eloRank": 2,
"eloRating": 2149,
"sofaScoreId": null,
"surfaceStats": [{
"period": "career", "surface": "Hard",
"matches": 212, "wins": 166, "losses": 46,
"winPercent": 78.3, "statsMatches": 209,
"servicePointsWonPercent": 67.5,
"returnPointsWonPercent": 41.0,
"dominanceRatio": 1.26
}],
"sourceUrl": "https://www.tennisabstract.com/cgi-bin/player.cgi?p=CarlosAlcaraz",
"statisticsSourceUrl": "https://www.tennisabstract.com/jsfrags/CarlosAlcaraz.js",
"scrapedAt": "2026-10-05T06:00:00.000Z"
}

Values are snapshots and will change. Never treat this sample as a current ranking feed.

How much does it cost to export Tennis Abstract player statistics?

Pricing is pay-per-event: one start event per valid run and one item event per exported player profile. Nested surface aggregates have no separate event charge. Filtered, duplicate and failed player records are not charged as items.

The start event is $0.005 per run. Profile prices follow your qualifying aggregate monthly Apify Store spend tier, not a private per-Actor volume schedule:

TierPer player profile
FREE$0.0067712
BRONZE$0.005888
SILVER$0.0045926
GOLD$0.0035328
PLATINUM$0.0035328
DIAMOND$0.0035328

At BRONZE, one profile is approximately $0.010888, five $0.03444, ten $0.06388, and 100 $0.5938, including one start event. At FREE, two profiles are approximately $0.0185424. These are Actor-event estimates, not guaranteed invoices; check the active Store pricing panel before running.

Freshness and missing data

The Actor reports what the source publishes when fetched. The profile Elo is rounded and may differ from the more precise weekly leaderboard rating. Rankings and statistics can have different source update cadences; extraction time is not their effective ranking date.

Career and last52 refer to source-published tour-level aggregates. No custom date filtering or historical snapshot selection is offered. A requested surface with no published row is absent. A valid profile can therefore have an empty surfaceStats array. Missing ranking or metric values are null rather than zero.

Limits and failure behavior

The Actor processes players sequentially and requests only the profile and statistics fragment. Datacenter proxy sessions and request pacing limit shared-IP rate limiting. Transient network errors, rate limits and selected server errors have bounded retries. A permanent upstream block or missing/mismatched profile fails the run instead of silently returning a successful empty result.

If a later player fails, earlier exported profiles may remain in the dataset. Such a run is a failed partial result, not proof that all requested players were processed. Check run status before treating an export as complete.

Integrations

Schedule recurring snapshots and write the default dataset to a warehouse or spreadsheet. Flatten surfaceStats into one row per player/period/surface for BI charts. Join snapshots on playerSlug and compare extraction timestamps in your own system.

The Actor does not store cross-run history, calculate alerts or perform change detection. Those functions belong in your downstream automation. Apify exports and integrations can connect datasets to Google Sheets, Make, Zapier or your own API consumer.

API usage

Start the Actor with your Apify token stored outside source:

curl -X POST \
'https://api.apify.com/v2/acts/automation-lab~tennisabstract-player-statistics/runs' \
-H "Authorization: Bearer $APIFY_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"playerSlugs":["CarlosAlcaraz"],"maxItems":1}'

JavaScript with the Apify client:

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('automation-lab/tennisabstract-player-statistics')
.call({ playerSlugs: ['CarlosAlcaraz'], maxItems: 1 });
const { items } = await client.dataset(run.defaultDatasetId).listItems({ limit: 10 });
console.log(items);

Python:

import os
from apify_client import ApifyClient
client = ApifyClient(os.environ['APIFY_TOKEN'])
run = client.actor('automation-lab/tennisabstract-player-statistics').call(
run_input={'playerSlugs': ['JannikSinner'], 'maxItems': 1})
items = client.dataset(run['defaultDatasetId']).list_items(limit=10).items

Check terminal status and paginate dataset reads with explicit limits. A run ID is not the player data.

MCP use

Scope discovery to this Actor:

claude mcp add --transport http apify \
'https://mcp.apify.com?tools=automation-lab/tennisabstract-player-statistics'

Claude Desktop, Cursor, and VS Code HTTP MCP configuration (use the equivalent server entry for your client):

{"mcpServers":{"apify":{"url":"https://mcp.apify.com?tools=automation-lab/tennisabstract-player-statistics"}}}

Discover actual argument schemas with scoped tools/list. This endpoint exposes automation-lab--tennisabstract-player-statistics and run/dataset/KV/abort helpers, not call-actor. Discovery does not authorize execution or aborting a run.

For an authorized invocation, start once with the exposed Actor tool:

{"playerSlugs":["CarlosAlcaraz","JannikSinner"],"maxItems":2,"waitSecs":30}

Its waitSecs is 0–45 seconds (default 30), not a completion guarantee. Retain the returned runId, status and storages.datasets.default.id (the run API calls this defaultDatasetId); these are metadata, not player rows. If nonterminal, use get-actor-run for that same run:

{"runId":"<returned runId>","waitSecs":0}

Use bounded host-side backoff of 2, 4, 8 seconds, capped at 10, with a total 120-second deadline from invocation. Set the client's request timeout above the selected server wait plus transport margin, within that deadline. After a client timeout, check the known run rather than starting another; if the run ID is unknown, report uncertainty without blindly rerunning. At the deadline, report pending with the run ID and stop polling. Report FAILED, ABORTED or TIMED-OUT honestly; available rows are failed partial output, never a successful full export.

After SUCCEEDED, call get-dataset-items with the returned default dataset ID:

{
"datasetId":"<storages.datasets.default.id>",
"limit":20,
"offset":0,
"fields":"playerSlug,playerName,eloRank,eloRating,surfaceStats,sourceUrl"
}

Use untransformed JSON and pages of 20 source rows from offset 0. Continue at source offsets 20, 40, and so on, using the returned offset/limit/totalItemCount metadata to detect exhaustion; never advance by a count of flattened surface rows or model-visible summaries. Keep the same dataset ID.

Admit at most 100 source rows or 64 KiB of serialized UTF-8 result content across all pages to model context, whichever comes first. Process pages host-side before model injection and enforce the byte ceiling even for one oversized row/page: omit or summarize it with explicit disclosure. A client without host-side interception cannot guarantee that byte ceiling and must not request unbounded results. Stop at exhaustion or budget and report returned rows, the next source continuation offset, and partial/truncated status when incomplete. Full exports belong in storage or downstream processing outside model context. This consumer context budget does not reduce Actor collection limits or fields.

Example prompt: “Export Alcaraz's career and last-52-week Hard statistics, then show the ranking, Elo and return-points-won percentages.”

Legality and diagnostics

This independent tool is not affiliated with Tennis Abstract or SofaScore. Use public data responsibly, respect source terms and applicable laws, and do not imply that scraped ratings guarantee sporting or betting outcomes.

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.

No AI is used at runtime or to generate output statistics. Apify hosts the run, logs and dataset and routes configured datacenter proxy traffic; those operating costs belong to the Actor operator, not an undisclosed third-party subscription. User-supplied custom proxies are the user's own dependency. Public player identifiers are sent to the source, and processed output contains public sporting names and statistics, not user-private accounts.

Run storage follows your Apify account's retention settings (31 days on our current plan); users can delete datasets and runs through Apify. The Actor keeps no persistent business cache or cross-run player history. Contact us through the Actor's Apify Store Issues tab for support. Pricing and export examples are estimates, not guaranteed invoices or payouts; adjustments, refunds, fraud, disputes, taxes, corrections and contractual clawbacks may affect final amounts.

FAQ and troubleshooting

Does it export individual matches? No. Use the related match-history Actor below for that output.

Why is a surface absent? Tennis Abstract did not publish that aggregate row for the selected period/player. Absence does not mean zero wins or zero matches.

Why is Elo null? The source profile may not have an eligible/current Elo rating. No rating is inferred from match history.

Can I pass any SofaScore ID? No. Only explicitly verified crosswalk IDs work; currently Jannik Sinner 206570. Supply the source slug or profile URL for other players.

Why did a valid-looking name fail? Slugs are exact and case-sensitive. Check the p value in the source profile URL. Full display names are not automatically searched.

Is there a Tennis Abstract API or GitHub dataset behind this? The Actor parses public profile/fragment pages. It does not claim an official Tennis Abstract API and does not import a GitHub historical dataset.

What if the source blocks my network? Try a configured datacenter or custom proxy if appropriate. Apify RESIDENTIAL groups are rejected; there is no automatic paid fallback. Failed upstream pages are not charged as exported profiles.