Tennis Abstract Player Elo and Surface Statistics
Pricing
from $3.53 / 1,000 item extracteds
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
Maintained by CommunityActor stats
0
Bookmarked
3
Total users
2
Monthly active users
20 days ago
Last modified
Categories
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
- Enter exact Tennis Abstract slugs, for example
CarlosAlcarazandJannikSinner. - Select
career,last52, or both periods. - Select the surfaces you need.
- Set a maximum number of unique player profiles.
- Run the Actor and open the Player statistics dataset.
- 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
| Parameter | Default | Behavior |
|---|---|---|
playerSlugs | None | Exact, case-sensitive source identifiers; at most 100 entries |
playerUrls | None | Source profile URLs on tennisabstract.com or www.tennisabstract.com only; at most 100 entries |
sofaScoreIds | None | Supported verified integer IDs only; currently 206570 |
periods | Both | Nonempty array containing career and/or last52 |
surfaces | All four | Nonempty array containing exact Hard, Clay, Grass, Carpet labels |
maxItems | 10 | Global unique-player profile limit from 1 to 100; not a surface-row limit |
proxyConfiguration | Apify datacenter | Set 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
| Field | Meaning |
|---|---|
playerSlug, playerName | Source identity, validated against the profile |
country | Published country code or null |
currentRank | Official tour rank published on the profile, or null |
eloRank, eloRating | Current all-surface profile Elo; source rating is rounded |
sofaScoreId | Verified curated mapping when available; otherwise null |
surfaceStats | Selected source aggregate rows, not individual matches |
sourceUrl | Canonical player profile URL |
statisticsSourceUrl | Exact public fragment containing aggregate tables |
scrapedAt | UTC 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:
| Tier | Per 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 osfrom apify_client import ApifyClientclient = 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.
Related automation
- Tennis Abstract Player Match History Scraper: dated match rows, opponents and scores rather than player aggregates.
- WTA Tennis Rankings Scraper: official WTA ranking snapshots rather than Tennis Abstract profile Elo and serving/returning aggregates.