# Sofascore Rankings Scraper (`incognito_mode/sofascore-rankings-scraper`) Actor

Scrape every global ranking Sofascore publishes: FIFA world rankings, UEFA club and league coefficients, ATP and WTA singles and live, UTR, World Rugby, rugby league, Premier Padel and all twelve UFC divisions. Plus name-to-ID search. No API key, no login.

- **URL**: https://apify.com/incognito\_mode/sofascore-rankings-scraper.md
- **Developed by:** [Elena Vance](https://apify.com/incognito_mode) (community)
- **Categories:** News, Developer tools, Automation
- **Stats:** 2 total users, 1 monthly users, 80.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.50 / 1,000 ranking rows

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

## Sofascore Rankings Scraper

Every global ranking [Sofascore](https://www.sofascore.com) publishes, in one
run — and a name-to-ID search for the rest of its data.

**25 ranking tables** across five sports: FIFA world rankings, UEFA club and
league coefficients, ATP and WTA singles and live, UTR, World Rugby, rugby
league, Premier Padel, and all twelve UFC divisions.

No API key, no login, no browser. Calls Sofascore's own JSON API directly and
only reaches for a proxy if a request is actually blocked.

### The point of this Actor

Sofascore's ranking endpoint returns rows and **nothing that names them**. Ask
it for type 11 and you get sixteen fighters with no indication that this is the
UFC flyweight division; ask for type 1 expecting the FIFA table and you get
UEFA's league coefficients instead. There is no index, no documentation, and
the numbering has gaps.

So the catalogue below *is* the product. It was built by sweeping every type
from 1 to 110 against the live API and identifying each table from its
contents — 25 live, the rest 404.

| Football | Tennis | Combat & other |
| --- | --- | --- |
| FIFA Men's World Ranking (211 nations) | ATP singles (500) | UFC — all 12 divisions |
| UEFA club coefficients (411 clubs) | WTA singles (500) | World Rugby (113 nations) |
| UEFA league coefficients (55 leagues) | ATP live, WTA live | Rugby league world (35) |
| | UTR men, UTR women | Premier Padel men & women |

### What you get

Two row types in one dataset, told apart by `recordType`.

**`ranking`** — one row per ranked entity: rank, points, previous rank, places
moved, the entity's Sofascore ID and a URL you can open. Tennis rows add career
best and tournaments played; MMA rows add the fight record; UEFA coefficient
rows add the year.

**`searchResult`** — one row per hit when you search a name: the entity type,
its Sofascore ID, its sport and club, and how many users follow it (which is
how you tell the Erling Haaland you want from the four others).

### What makes this different

- **The catalogue is the product.** Sofascore's ranking endpoint returns rows
  with nothing that names them, no index, and gaps in the numbering. This
  Actor's 25-table catalogue was built by sweeping every type from 1 to 110
  against the live API and identifying each table from its contents — see [The
  point of this Actor](#the-point-of-this-actor).
- **Five sports in one Actor.** Sellers elsewhere carry a single slice each:
  tennis only, or football only. This one covers football, tennis, rugby, MMA
  and padel.
- **Name-to-ID search feeds the whole suite.** `searchQueries` resolves a name
  into the team, player and tournament IDs that the other five Actors take as
  input, and tells duplicates apart by follower count.
- **UEFA coefficient history no bulk table exposes** — ask for a club by ID
  and get its coefficient and position for every year from 2018 to 2027.
- **UFC rank 0 is the champion**, carried with an explicit `isChampion` flag
  so code that treats 0 as a null does not misplace the title-holder.

### Input

| Field | Type | Default | What it does |
| --- | --- | --- | --- |
| `rankings` | array | — | Table names from the catalogue (`atp-singles`, `fifa-world-ranking`, `ufc-heavyweight`…), raw Sofascore type numbers, or `["all"]` for every table. |
| `topN` | integer | whole table | Keep only the top N of each. The tennis and padel tables are 500 rows; the UFC divisions are 16. |
| `entities` | array | — | Team IDs or Sofascore URLs. Returns every ranking that entity is in. |
| `searchQueries` | array | — | Names to resolve to Sofascore IDs. 2–100 characters each. |
| `searchType` | string | `all` | Narrow to teams, players, competitions, managers or referees. |
| `maxSearchPages` | integer | `1` | 20 results per page; paging stops early on a short page. |
| `maxItems` | integer | `10000` | Stop after this many rows. |
| `proxyConfiguration` | object | off | Only needed if your IP is blocked. |

A run needs at least one of `rankings`, `entities` or `searchQueries`.

```json
{ "rankings": ["fifa-world-ranking", "atp-singles"], "topN": 50 }
```

### Three things worth knowing

**In the UFC tables, rank 0 is the champion.** Not a missing value. Every
division runs 0–15, and code that treats 0 as null puts the title-holder in the
wrong place — so every row also carries an explicit `isChampion` flag.

**Sofascore models individual athletes as teams.** A tennis player, an MMA
fighter, a padel player and a motorsport driver all arrive as `team` entities;
only team-sport athletes are `player`. That is why `entities` takes team IDs,
and why the URL for a "team" depends on the sport — `/tennis/player/…`,
`/mma/fighter/…`, `/football/team/…`. The Actor emits the right one.

**A club look-up returns history the bulk table cannot.** `/rankings` for the
UEFA club coefficients carries only the current year. Ask for Manchester City
by ID and you get its coefficient and position for **every year from 2018 to
2027** — ten rows, one per season. This is the only route to that series.

The flip side: footballers cannot be looked up at all. Sofascore only ranks
entities it models as teams, so a `player` ID returns nothing, and the run says
so rather than failing.

### Notes

- The whole catalogue is 25 requests, roughly 5,700 rows. A complete sweep is
  a cheap run.
- There is no way to ask for a historical snapshot of a ranking — every dated
  variant of the endpoint 404s. The UEFA club coefficient series above is the
  only history Sofascore exposes.
- A type number outside the catalogue is still fetched. It comes back labelled
  `Ranking type N` rather than rejected, so a table Sofascore adds tomorrow
  works today.
- Bad input is recorded as an unbilled `INVALID_INPUT` row rather than crashing
  the run.

### The Sofascore Actor suite

Six Actors from the same publisher, built on one shared HTTP layer and one ID
space — so the output of one is the input of the next.

| Actor | What it returns |
| --- | --- |
| [Live Scores & Fixtures](https://apify.com/incognito_mode/sofascore-live-scores-scraper) | Fixtures and live results by date across 19 sports. Emits `eventId`. |
| [Match Stats, xG & Lineups](https://apify.com/incognito_mode/sofascore-match-analytics-scraper) | Per-match shotmaps with pitch coordinates, lineups, ratings, momentum. Takes `eventId`. |
| [League Standings & Tables](https://apify.com/incognito_mode/sofascore-standings-scraper) | League tables, group stages, conferences and divisions. Takes `uniqueTournamentId`. |
| [Team & Player Data](https://apify.com/incognito_mode/sofascore-team-player-scraper) | Squads, bios, market values, transfers, match logs. Takes team and player IDs. |
| [Season Stats](https://apify.com/incognito_mode/sofascore-season-stats-scraper) | Season leaderboards and complete team statistics. Takes `uniqueTournamentId`. |
| **Rankings & Search** | *This Actor.* |

**How they chain.** Live Scores emits the `eventId` that Match Stats takes.
Rankings' search turns a name into the team, player and tournament IDs the
other five accept. Standings and Season Stats share `uniqueTournamentId`, and
Team & Player emits that same ID as `primaryTournamentId`.

All six need no API key, no login and no headless browser; none requires a
residential-proxy add-on, and all run on the Apify free plan.

### Development

```bash
pip install -r requirements-dev.txt
pytest                       # 161 tests, no network
npx apify-cli@1 validate-schema
apify run                    # needs storage/key_value_stores/default/INPUT.json
```

`docs/architecture.md` covers the catalogue and how the pieces fit together;
`docs/ci-cd.md` the GitHub Actions pipeline; `docs/publishing.md` the Apify
Store listing and pricing.

# Actor input Schema

## `rankings` (type: `array`):

Which global ranking tables to scrape. Sofascore's API returns these as bare numbers with no name attached, so the list below is this Actor's own catalogue — 25 live tables across football, tennis, rugby, MMA and padel. A raw Sofascore ranking type number is also accepted, in case one is added that this list does not name yet.

## `topN` (type: `integer`):

Keep only the top N of each table. Useful because the tennis and padel tables are 500 rows each while the UFC divisions are 16. Leave empty for the whole table.

## `entities` (type: `array`):

Sofascore team IDs or URLs. Returns every ranking that entity appears in — and for a club, its UEFA coefficient for every year on record, which the bulk tables do not carry. Only works for entities Sofascore models as teams: clubs, national teams, tennis players, MMA fighters and motorsport drivers. Footballers and other team-sport athletes are not ranked individually and return nothing.

## `searchQueries` (type: `array`):

Find the Sofascore ID for a team, player, competition, manager or referee by name — the IDs the other Sofascore Actors take as input. Each query must be 2 to 100 characters.

## `searchType` (type: `string`):

Narrow the search to one kind of entity, or leave it on everything.

## `maxSearchPages` (type: `integer`):

Sofascore returns 20 results per page. Paging stops early on a short page.

## `maxItems` (type: `integer`):

Stop after this many rows across all record types.

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

Optional. This Actor calls Sofascore directly and only reaches for a proxy if a request is actually blocked, trying datacenter IPs (included in every Apify plan) before residential ones. Set this only to pin a specific proxy or country from the start.

## Actor input object example

```json
{
  "rankings": [
    "fifa-world-ranking"
  ],
  "searchType": "all",
  "maxSearchPages": 1,
  "maxItems": 10000,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `dataset` (type: `string`):

Dataset containing every ranking and search row.

# 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 = {
    "rankings": [
        "fifa-world-ranking"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("incognito_mode/sofascore-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 = { "rankings": ["fifa-world-ranking"] }

# Run the Actor and wait for it to finish
run = client.actor("incognito_mode/sofascore-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 '{
  "rankings": [
    "fifa-world-ranking"
  ]
}' |
apify call incognito_mode/sofascore-rankings-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,incognito_mode/sofascore-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/k8fLcQRCYsCm40yMY/builds/TRejV1hPBsNoFl4pf/openapi.json
