# WTA Tennis Rankings Scraper (`praise-most-high/wta-tennis-rankings-scraper`) Actor

WTA Tennis Rankings Scraper reads the WTA's own published ranking tables and returns one row per player: rank, week-on-week movement, points, country, age and tournaments played. Singles, doubles and both Race tables. onlyChangedSinceLastRun defaults ON, so a daily run bills only movers.

- **URL**: https://apify.com/praise-most-high/wta-tennis-rankings-scraper.md
- **Developed by:** [angel nguyen](https://apify.com/praise-most-high) (community)
- **Categories:** News, Business, Other
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$4.00 / 1,000 ranked player returneds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

WTA Tennis Rankings Scraper extracts the Women's Tennis Association's own published ranking tables and returns one flat, typed row per ranked player — rank, week-on-week movement, points, country, age, tournaments played, and the WTA's own player ID. You pick the tables; you get back a spreadsheet of the rankings.

<img src="https://apify-image-uploads-prod.s3.us-east-1.amazonaws.com/0ezaunoiVKF5LFUup-actor-wNcTtg7Rrnz29w4as-rj4spGYmyh-wta-rankings.png" alt="WTA Tennis Rankings Scraper" width="96" height="96">

The source is `wtatennis.com/rankings`, the WTA's own website. It is public, it needs no API key and no login, and it is the same page a fan reads. The WTA publishes four ranking tables — **singles**, **doubles**, and the two **Race to the Finals** tables — and this Actor reads them exactly as published.

### Key features

| Feature | Detail |
|---|---|
| Input | any of the four WTA ranking tables, in one run |
| Fields | rank, movement, points, country, age, tournaments played, player ID, profile URL |
| Movement | parsed as a signed number, with the WTA's own text kept alongside it |
| Ties | preserved, not flattened — the doubles table really does publish two players at the same rank |
| Incremental runs | `onlyChangedSinceLastRun` is **on by default** — a repeat run bills only for players who moved |
| Pre-charge filters | rank and country filters run *before* billing, so filtering lowers your cost |
| Honest failures | a table name the WTA does not publish is `unavailable`; a table published but empty is `empty`, and neither is charged |
| Output | one row per player, flat and typed — JSON, CSV, Excel, XML |

### WTA tennis rankings scraper: how it works

1. **Resolve the table.** `singles`, `doubles`, `race-singles`, `race-doubles`, plus the obvious aliases. Anything else is reported as unavailable, naming the four tables that do exist, and is not charged.
2. **Read the published table.** One request per table. The rankings are rendered into the page itself, so there is no hidden API and nothing is reconstructed or inferred.
3. **Parse each row.** Rank, movement, player ID and name, country, age, tournaments played and points come out of the row's own cells.
4. **Filter, then charge, then deliver.** Filters run first so you are never billed for rows a filter discards. Charges are applied before records are handed over and delivery is truncated to whatever your budget allowed, so the rows you receive equal the rows you paid for.

### Tutorial

1. Open the Actor and choose your tables in **WTA ranking tables**. The prefilled example reads `singles` and `doubles`.
2. Leave **Only players whose ranking changed since your last run** switched on. It is on by default and it is what makes a daily schedule cheap.
3. Optionally set **Only players ranked this high or better** to `10` for just the top ten, or list **Only these countries** as three-letter codes such as `USA`, `POL`, `BLR`.
4. Click **Start**. Two tables finish in a few seconds.
5. Open the **Storage** tab and export to CSV or Excel, or read the same rows from the dataset API.
6. To follow the rankings over time, open **Schedules** and run it daily. WTA rankings update weekly, so with the incremental setting on most days will cost you very little and the rows you do get are exactly the week's movers.

### Input

The minimum input is a list of tables. Everything else has a working default.

```json
{
  "rankingTypes": ["singles", "doubles"],
  "maxPlayersPerTable": 50,
  "onlyChangedSinceLastRun": true,
  "maxRank": 50,
  "countryCodes": []
}
```

| Field | Type | Default | Meaning |
|---|---|---|---|
| `rankingTypes` | array of string | — | Any of `singles`, `doubles`, `race-singles`, `race-doubles`. Required. |
| `maxPlayersPerTable` | integer | `50` | Cap per table. 50 is also the ceiling — see the limits section. |
| `onlyChangedSinceLastRun` | boolean | **`true`** | Return only players whose rank or points moved since your last run. **On by default.** |
| `maxRank` | integer | `50` | Keep only players at this rank or better. Filters before charging. |
| `countryCodes` | array of string | *(empty)* | Three-letter country codes as the WTA publishes them. Filters before charging. |

### Output

One row per ranked player. This is a real row from a real run against the live singles table, not an illustration.

```json
{
  "rankingType": "singles",
  "rankingTable": "WTA singles rankings",
  "rank": 1,
  "movement": 0,
  "movementText": "-",
  "playerId": "320760",
  "playerName": "Aryna Sabalenka",
  "countryCode": "BLR",
  "age": 28,
  "tournamentsPlayed": 18,
  "points": 8575,
  "profileUrl": "https://www.wtatennis.com/players/320760",
  "sourceUrl": "https://www.wtatennis.com/rankings/singles",
  "scrapedAt": "2026-09-02T17:36:07.641Z"
}
```

Two details worth knowing, because they are the ones that quietly corrupt a rankings dataset:

- **A dash in the movement column means "did not move", which is `0` — not missing.** The Actor returns `movement: 0` and keeps the WTA's own `"-"` in `movementText`. Reading the dash as null would throw away the only field that tells you a ranking is stable.
- **Ties are real and are preserved.** The Actor returns the rank the WTA prints and never renumbers, so when the source ties two players it emits the same rank twice and the following rank skips. Any code you write against this dataset should expect ranks to be non-decreasing rather than a clean 1..N sequence.

### Pricing

This Actor is priced per event, so you pay for what it delivers rather than for how long it runs.

| Event | Price | Charged when |
|---|---|---|
| `player-returned` | $0.004 | Once per player row written to the dataset |

There is no per-run start fee and no monthly minimum. A run that returns nothing costs nothing at all — including a Race table that the WTA publishes but has not populated.

#### What a repeat run actually costs, worked through

This is why `onlyChangedSinceLastRun` defaults to on. Take both the singles and doubles tables — 100 players — polled daily for a month. WTA rankings update once a week, so on most days nothing moves at all, and on an update day perhaps 30 of the 100 players shift:

| Setting | Players billed per day | Monthly cost |
|---|---|---|
| `onlyChangedSinceLastRun: true` *(the default)* | 100 on day one, then ~30 on each of ~4 update days | **$0.40 + $0.48 = $0.88** |
| `onlyChangedSinceLastRun: false` | 100 every day | **$12.00** |

A **13x** difference, and all of it would have been spent re-buying rankings you already had. Turn it off when you deliberately want the full table again — to rebuild a lost dataset, or for a one-off snapshot — and not by accident.

The other filters work the same way. `maxRank: 10` bills for ten players, not fifty. `countryCodes: ["USA"]` bills only for the American players. Both filter before charging, so narrowing your question always lowers your bill.

### Frequently asked questions

**Where does the data come from?**
`wtatennis.com/rankings`, the WTA's own published ranking tables. Nothing is inferred, computed or sourced from a third party.

**How deep do the rankings go?**
Fifty players per table. The WTA's page publishes its top 50 and its "Load More" control is client-side — no deeper page is served to an ordinary request. `maxPlayersPerTable` is therefore capped at 50 rather than accepting a larger number and quietly returning 50 anyway.

**What are the Race tables?**
The Race to the Finals standings — points accumulated in the current season, as opposed to the rolling 52-week ranking. The WTA leaves them empty outside the season, and an empty table is reported as `empty` and charged nothing.

**What happens if I ask for a table that does not exist?**
You get `unavailable` with a message naming the four tables that do, and **no charge**. An unresolvable input must never bill.

**Does it cover the ATP or men's tennis?**
No. This Actor reads the WTA's tables only, and the listing does not claim otherwise.

**Does it return match results or head-to-head data?**
No. It returns rankings. Match-level data is a different product and is not implied here.

**How often should I run it?**
Daily is sensible and cheap with the incremental default on — you will get rows on the days the WTA actually republishes and near-nothing in between. Weekly also works if you only want the update day.

**What if my budget cap is reached mid-run?**
Delivery is truncated to the number of records actually charged and the run says so in the log. You are never billed for rows you did not receive.

### Limits and troubleshooting

- **Fifty per table is the source's limit, not a setting.** If you need players ranked 51 and below, this Actor cannot get them, because the WTA does not serve them to an ordinary page request.
- **An empty Race table is not a bug.** Out of season the WTA publishes the page with no players in it. The Actor reports `empty`, distinguishes it from a page it failed to read, and charges nothing.
- **Rankings are weekly.** A daily run on a non-update day is expected to return nothing with the incremental default on. That is the setting working, not a failure.
- **Site markup changes.** If the WTA restructures the page, the Actor reports the table as unavailable with "the page loaded but carried no rankings table" rather than returning zero players, because zero players would be a claim about tennis rather than about the page.

### Integrations and API

The Actor is callable from anywhere the Apify API reaches.

```bash
curl -X POST "https://api.apify.com/v2/acts/praise-most-high~wta-tennis-rankings-scraper/runs?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"rankingTypes":["singles"],"maxRank":10}'
```

- **Scheduling** — run it daily from the Apify scheduler; with the incremental default on, each run is a movers feed.
- **Webhooks** — fire on `ACTOR.RUN.SUCCEEDED` to push new rows into your own store.
- **Python and JavaScript SDKs** — both call it the same way; see the [Apify client docs](https://docs.apify.com/api/client/js/).
- **MCP** — reachable as a tool from any MCP-speaking agent through Apify's own MCP server.

### Related Actors

Other Actors on this account that pair with this one:

- [X (Twitter) Tweet Scraper - Bulk by URL](https://apify.com/praise-most-high/x-twitter-bulk-tweet-extractor) - player news and match reaction alongside the ranking movement
- [YouTube Channel Upload Monitor](https://apify.com/praise-most-high/youtube-channel-upload-monitor) - match highlight channels publishing against the same calendar
- [Telegram Channel Message Scraper](https://apify.com/praise-most-high/telegram-channel-message-scraper) - sports and betting signal channels discussing these tables
- [Apple Podcasts New-Episode Monitor](https://apify.com/praise-most-high/podcast-directory-monitor) - tennis podcasts covering the ranking weeks this Actor returns

### Legal and data handling

> **Unofficial.** This Actor is not affiliated with, endorsed by, or sponsored by the Women's Tennis Association. "WTA", "Women's Tennis Association" and "Race to the Finals" are trademarks of their respective owner and are used here only to identify the source being read.

This Actor reads a public, unauthenticated page that the WTA publishes for general readership, and returns the professional competitive record it contains: rank, points, movement, nationality, age and tournaments played. These are published sporting statistics about professional athletes in their professional capacity. The Actor collects no contact details, no private information and nothing behind an authentication boundary.

The rankings themselves are the WTA's own compilation. This Actor retrieves them; it does not license them to you. Whether your intended use of that output is permitted is a question about your jurisdiction, your purpose and the WTA's own terms, and it is yours to answer — this Actor makes no representation about it.

### Support and feedback

Open an issue on the Actor's Issues tab. Bug reports that include the run ID and the input that produced the problem are answered fastest. Feature requests are read and are the main source of what gets built next.

# Actor input Schema

## `rankingTypes` (type: `array`):

Which of the WTA's four published ranking tables to read: singles, doubles, race-singles, race-doubles. Each table publishes its top 50 players. The two Race tables are empty outside the season — an empty table is reported as empty and is never charged.

## `maxPlayersPerTable` (type: `integer`):

Cap on players returned per ranking table. The WTA publishes 50 per table and no more, so 50 is both the default and the ceiling — a higher number returns 50, it does not reach deeper into the rankings.

## `onlyChangedSinceLastRun` (type: `boolean`):

ON BY DEFAULT, and leaving it on is what keeps a scheduled run cheap. A player is returned only when their rank or their points have moved since you last received them. WTA rankings update once a week, so on a daily schedule this is the difference between paying for 50 rows a day and paying for the handful that actually moved. Turn it off only when you deliberately want the full table again.

## `maxRank` (type: `integer`):

Keep only players whose rank is this number or lower — 10 gives you the top ten. Leave empty for the whole published table. The filter runs BEFORE charging, so narrowing it lowers your bill.

## `countryCodes` (type: `array`):

Three-letter country codes as the WTA publishes them, for example USA, POL, BLR. Leave empty for every country. This filter also runs before charging, so it lowers your bill rather than raising it.

## Actor input object example

```json
{
  "rankingTypes": [
    "singles",
    "doubles"
  ],
  "maxPlayersPerTable": 50,
  "onlyChangedSinceLastRun": true,
  "maxRank": 50,
  "countryCodes": []
}
```

# Actor output Schema

## `players` (type: `string`):

No description

# 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 = {
    "rankingTypes": [
        "singles",
        "doubles"
    ],
    "maxPlayersPerTable": 50,
    "onlyChangedSinceLastRun": true,
    "maxRank": 50,
    "countryCodes": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("praise-most-high/wta-tennis-rankings-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 = {
    "rankingTypes": [
        "singles",
        "doubles",
    ],
    "maxPlayersPerTable": 50,
    "onlyChangedSinceLastRun": True,
    "maxRank": 50,
    "countryCodes": [],
}

# Run the Actor and wait for it to finish
run = client.actor("praise-most-high/wta-tennis-rankings-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 '{
  "rankingTypes": [
    "singles",
    "doubles"
  ],
  "maxPlayersPerTable": 50,
  "onlyChangedSinceLastRun": true,
  "maxRank": 50,
  "countryCodes": []
}' |
apify call praise-most-high/wta-tennis-rankings-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,praise-most-high/wta-tennis-rankings-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/wNcTtg7Rrnz29w4as/builds/aLrtqcuk8Kkr0VFld/openapi.json
