Tennis Elo Ratings Scraper - ATP & WTA Surface Elo, Win Odds
Pricing
from $10.95 / 1,000 player or matchup returneds
Tennis Elo Ratings Scraper - ATP & WTA Surface Elo, Win Odds
For tennis bettors and model builders: Tennis Abstract Elo for every ATP and WTA player - overall, hard, clay and grass Elo with ranks, peak Elo, official rank - plus matchup win probability. 1,083 players in the 14 Sep 2026 update. Price matches on surface Elo without your own model.
Pricing
from $10.95 / 1,000 player or matchup returneds
Rating
0.0
(0)
Developer
NeverEmpty
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
a day ago
Last modified
Categories
Share
For tennis bettors and model builders: Elo ratings for every ATP and WTA player from Tennis Abstract - overall, hard-court, clay-court and grass-court Elo with their ranks, peak Elo and the month it was reached, official ranking - plus the win probability and fair odds for any matchup on the surface you choose. The 14 Sep 2026 update lists 1,083 players (553 ATP, 530 WTA), and both lists come from two page loads. Price a match on surface Elo in seconds instead of scraping tables by hand or maintaining your own rating model.
Export as JSON, CSV or Excel.
The ratings come from Tennis Abstract's public Elo reports (/reports/atp_elo_ratings.html and /reports/wta_elo_ratings.html), which Tennis Abstract updates about once a week. This Actor is unofficial and not affiliated with Tennis Abstract.
Why this Actor
- Surface Elo, not just one number. Every player row carries overall Elo plus hard, clay and grass Elo and the rank on each surface, the peak Elo and its month, the official ATP or WTA rank, and Tennis Abstract's "log diff" (how far the Elo rank is from the official rank).
- Win probability built in. Enter
Jannik Sinner vs Carlos Alcaraz | clayand get both players' clay Elo, the win probability for each, the fair decimal odds (1 / probability) and, for comparison, the probability on overall Elo. - Find players the way you type them. Full name, a unique last name, the Tennis Abstract id (
JannikSinner) or the player page URL. Accents and case do not matter (iga świątekfindsIga Swiatek). A name that matches several players is not guessed: you get a free row that lists the candidates. - Optional player profiles. Tick
includeProfileto add current and best official rank, the dates the best rank was first and last held, date of birth, height, playing hand, backhand, country and an active flag, read from each player's Tennis Abstract page. The name on the page is checked against the name in the Elo table before the profile is attached. - Honest failures. A player not in the lists, an unreadable page, a matchup across ATP and WTA, and a run that hits your spending limit each come back as a separate free row that says what happened. An unreadable table is never reported as "0 players".
How the win probability is calculated
The standard Elo expected-score formula:
P(A beats B) = 1 / (1 + 10^((EloB - EloA) / 400))
EloA and EloB are the two players' Tennis Abstract ratings on the chosen surface (overall uses the overall Elo, hard/clay/grass use the surface Elo). Fair decimal odds are 1 / P, with no bookmaker margin. player1WinProbabilityOverallElo gives the same formula on overall Elo, because some modellers blend overall and surface Elo; the blend is up to you. Probabilities are rounded to 4 decimals and odds to 3.
Example (14 Sep 2026 update): Jannik Sinner clay Elo 2211.8, Carlos Alcaraz clay Elo 2083.8, difference 128 → Sinner 0.6763, Alcaraz 0.3237, fair odds 1.479 / 3.089. On overall Elo (2321.9 vs 2141.2) Sinner's probability is 0.7389.
This is a rating-based estimate for a single match. It does not know about injuries, fatigue, best-of-three versus best-of-five, or anything that happened after the last Tennis Abstract update.
Input
| Field | Type | Default | What it does |
|---|---|---|---|
tours | list | both (prefilled: ATP, WTA) | atp and/or wta. Empty = both. |
maxPlayersPerTour | integer | 50 | Players from the top of each Elo list. 0 = every player. Ignored when players is filled. |
players | list | (none) | Only these players: full name, unique last name, Tennis Abstract id or player page URL. |
includeProfile | boolean | false | Add the player profile from each player's page. Slower (see below). |
matchups | list | (none) | Player A vs Player B, optionally followed by a bar and a surface (clay, hard, grass or overall). With matchups, only matchup rows are returned unless alsoReturnRankings is on. |
surface | select | overall | Surface for matchup lines that do not name one. |
alsoReturnRankings | boolean | false | With matchups, also return the ranking rows chosen above. |
Examples:
{ "tours": ["atp", "wta"], "maxPlayersPerTour": 0 }
{ "players": ["Jannik Sinner", "Carlos Alcaraz", "Iga Swiatek"], "includeProfile": true }
{ "matchups": ["Jannik Sinner vs Carlos Alcaraz | clay", "Aryna Sabalenka vs Coco Gauff | hard"] }
Output
Player row (ATP, 14 Sep 2026 update, with includeProfile):
{"source": "tennisabstract.com","status": "ok","rowType": "player","tour": "ATP","eloRank": 1,"playerName": "Jannik Sinner","playerId": "JannikSinner","playerUrl": "https://www.tennisabstract.com/cgi-bin/player.cgi?p=JannikSinner","age": 24.8,"elo": 2321.9,"hardEloRank": 1,"hardElo": 2259.3,"clayEloRank": 1,"clayElo": 2211.8,"grassEloRank": 1,"grassElo": 2125.5,"peakElo": 2339.8,"peakEloMonth": "2026-05","officialRank": 1,"rankLogDiff": 0,"ratingsDate": "2026-09-14","currentOfficialRank": 1,"bestOfficialRank": 1,"bestRankFirstDate": "2024-06-10","bestRankLastDate": "2026-09-14","dateOfBirth": "2001-08-16","heightCm": 191,"plays": "right","backhand": "two-handed","country": "ITA","active": true,"profileStatus": "ok"}
Matchup row:
{"source": "tennisabstract.com","status": "ok","rowType": "matchup","matchup": "Jannik Sinner vs Carlos Alcaraz | clay","tour": "ATP","surface": "clay","player1Name": "Jannik Sinner","player1Id": "JannikSinner","player1Elo": 2211.8,"player1EloRank": 1,"player2Name": "Carlos Alcaraz","player2Id": "CarlosAlcaraz","player2Elo": 2083.8,"player2EloRank": 2,"eloDifference": 128,"player1WinProbability": 0.6763,"player2WinProbability": 0.3237,"player1FairDecimalOdds": 1.479,"player2FairDecimalOdds": 3.089,"player1WinProbabilityOverallElo": 0.7389,"formula": "P(A beats B) = 1 / (1 + 10^((EloB - EloA) / 400))","ratingsDate": "2026-09-14"}
Notes on the columns:
ratingsDateis Tennis Abstract's "Last update" date for the list, not the time of the run.officialRankis the ATP or WTA rank shown in the Elo table (as ofratingsDate);currentOfficialRankcomes from the player page and can be newer. Either can benull(5 of 553 ATP players had no official rank in the 14 Sep 2026 update).ageis in years with one decimal, as Tennis Abstract shows it (nullfor 3 ATP players and 1 WTA player).profileStatus:ok;not-found(the player page has no profile);name-mismatch(the page names a different player, so no profile is attached);unreadable(the page could not be read after 3 tries; the Elo columns are still filled).- Values are never filled with 0 or guessed: a missing value is
null.
Not included: match-by-match results and histories. Tennis Abstract's robots.txt asks crawlers not to read the files that hold them (/jsfrags/, /jsmatches/, /jsplayers/), and this Actor respects that. Only the Elo reports and the player pages are read.
Free note rows
These rows have rowType: "note", a status and a plain-English note, and are never charged:
status | Meaning |
|---|---|
player-not-found | The name is not in the Elo lists (they list players with recent tour-level matches). |
ambiguous-player | The name matches several players; the row lists them. Use the full name. |
duplicate | The same player, or the same two players on the same surface (in either order), was asked for twice; it is returned once. |
different-tours | A matchup across the ATP and WTA lists. The two Elo scales are separate and cannot be compared. |
invalid-matchup | A matchup line that is not "Player A vs Player B", names an unknown surface, or names the same player twice. |
no-surface-rating | One player has no rating on the chosen surface. |
unreadable | A Tennis Abstract page could not be read (after waiting and retrying). Nothing from it is guessed. |
budget-reached | The run reached the maximum total charge you set; the row says how many rows were not returned. |
invalid-input | The input could not be used (for example an unknown tour); nothing was requested. |
Speed and politeness
Tennis Abstract is an independent site, so this Actor reads one page at a time, waits at least 3 seconds between pages, follows the site's Retry-After when it answers HTTP 429 (too many requests) and then waits one second longer between pages (up to 8 seconds). It identifies itself with its own User-Agent and connects through Apify Proxy (datacenter), because plain cloud addresses get a browser check page.
- Rankings and matchups need only the two Elo pages: a run takes seconds, whatever the number of players.
- With
includeProfile, each player needs one more page: about 5 seconds per player (60 ATP players took 4 min 45 s on 21 Sep 2026), so 100 players take about 8 minutes and all 1,083 players about 1.5 hours or more. The default run timeout is 3 hours; if a run hits its timeout, the rows returned so far are kept and only they are charged, but no note row is written, so raise the timeout for very long runs. - If player pages stop answering (3 failures in a row), the run stops and a free row says how many players were left out.
Pricing
Pay per result: each player row and each matchup row is one result. Note rows are free. With includeProfile, a player row whose profile could not be attached (profileStatus other than ok) is still one result, because all its Elo columns are filled; after 3 unreadable player pages in a row the run stops. If a run reaches the maximum total charge you set, it stops and tells you in a free row how many rows were not returned.
FAQ
How often do the ratings change? Tennis Abstract updates the Elo lists about once a week (the ratingsDate column shows the update). Running more often than that returns the same numbers.
Why is a well-known player missing? The Elo lists contain players with recent tour-level results. Retired or long-inactive players drop off the list.
Can I compare an ATP and a WTA player? No. The two lists are separate rating pools, so the Actor returns a free different-tours row instead of a meaningless probability.
Is this official? No. It reads Tennis Abstract's public pages; it is not affiliated with or endorsed by Tennis Abstract, the ATP or the WTA.