# Transfermarkt Player & Market Value Scraper (`incognito_mode/transfermarkt-player-scraper`) Actor

Scrape Transfermarkt football players by id, URL or name. Returns market value, full market-value history, every transfer with fees, contract expiry, agent, position, height, foot, citizenship and caps/goals. No API key, no login, no residential proxy — runs on the free plan. JSON, CSV or Excel.

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

## Pricing

from $1.20 / 1,000 player scrapeds

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

## Transfermarkt Player & Market Value Scraper

Scrape football players from [Transfermarkt](https://www.transfermarkt.com) —
the reference database for transfer market values — by player ID, by URL, or
just by name. Every run returns a flat, typed record per player plus the
player's **complete market-value history** and **complete transfer history**,
with fees as numbers rather than strings.

No API key, no login, no residential proxy, no browser. It runs on the Apify
free plan. Export to **JSON, CSV, Excel, XML** or pull it from the API.

***

### What you get

One row per player. This is a real, unedited run (`playerIds: ["418560"]`),
abbreviated only to fit the page:

| Field | Value |
| --- | --- |
| `playerId` | `418560` |
| `name` | `Erling Haaland` |
| `nameInHomeCountry` | `Erling Braut Håland` |
| `dateOfBirth` | `2000-07-21` |
| `age` | `26` |
| `placeOfBirth` | `Leeds` |
| `heightCm` | `195` |
| `citizenship` | `["Norway"]` |
| `positionGroup` / `position` | `Attack` / `Centre-Forward` |
| `foot` | `left` |
| `shirtNumber` | `9` |
| `playerAgent` | `Rafaela Pimenta` |
| `outfitter` | `Nike` |
| `clubId` / `clubName` | `281` / `Manchester City` |
| `competitionCode` / `competitionName` | `GB1` / `Premier League` |
| `joinedClub` | `2022-07-01` |
| `contractExpires` | `2034-06-30` |
| `contractExtensionDate` | `2025-01-17` |
| `marketValue` | `220000000` |
| `marketValueCurrency` | `€` |
| `marketValueLastUpdate` | `2026-07-22` |
| `highestMarketValue` | `220000000` |
| `totalTransferFees` | `88100000` |
| `nationalTeam` | `{"team": "Norway", "caps": 55, "goals": 62}` |
| `portraitUrl` | `https://img.a.transfermarkt.technology/portrait/header/418560-…png` |
| `profileUrl` | `https://www.transfermarkt.com/x/profil/spieler/418560` |

Plus two nested arrays:

**`marketValueHistory`** — every valuation Transfermarkt has ever recorded
(28 points for Haaland, back to €200k at Bryne FK in 2016):

```json
[
  { "date": "2016-12-18", "marketValue": 200000,    "marketValueFormatted": "€200k",    "clubName": "Bryne FK",       "age": 16 },
  { "date": "2019-11-07", "marketValue": 30000000,  "marketValueFormatted": "€30.00m",  "clubName": "Red Bull Salzburg", "age": 19 },
  { "date": "2026-07-22", "marketValue": 220000000, "marketValueFormatted": "€220.00m", "clubName": "Manchester City","age": 26 }
]
```

**`transferHistory`** — every career move, with the fee as a number:

```json
[
  {
    "season": "22/23", "date": "2022-07-01",
    "fromClubId": 16,  "fromClubName": "Dortmund",
    "toClubId": 281,   "toClubName": "Man City",
    "marketValueAtTransfer": 150000000,
    "fee": 60000000, "feeFormatted": "€60.00m", "feeType": "fee",
    "isUpcoming": false
  }
]
```

#### Money and dates are parsed, not passed through

Transfermarkt renders `€220.00m`, `€200k`, `€1.39bn` and dates as `dd/mm/yyyy`.
This Actor emits `220000000`, `200000`, `1390000000` and `2000-07-21`, so the
data sorts, sums and filters without a cleaning pass. The original strings are
kept beside the numbers (`marketValueFormatted`, `feeFormatted`) where they add
anything.

**A fee that is not a number is not turned into one.** `free transfer`,
`End of loan` and `?` (undisclosed) become `fee: null` with `feeType` set to
`free`, `end_of_loan` or `undisclosed`. Coercing those to `0` would be
indistinguishable from a real zero once it is in your dataset.

***

### Input

Give it IDs, URLs, names, or any mix of the three.

```json
{
  "playerIds": [
    "418560",
    "https://www.transfermarkt.com/jude-bellingham/profil/spieler/581678"
  ],
  "searchQueries": ["Lamine Yamal"],
  "maxPlayersPerQuery": 1,
  "includeMarketValueHistory": true,
  "includeTransferHistory": true,
  "maxItems": 100
}
```

| Input | What it does |
| --- | --- |
| `playerIds` | Numeric IDs **or any Transfermarkt player URL**. The profile, transfers, injuries and performance URLs all work — the name in the URL is ignored, so a copy-paste from the browser is enough. |
| `searchQueries` | Player names, when you don't have IDs. Each name is one extra request and yields up to `maxPlayersPerQuery` players. |
| `maxPlayersPerQuery` | How many players to take per name. Transfermarkt returns its own relevance order, so `1` is usually the player you meant. Default `5`. |
| `includeMarketValueHistory` | Nested valuation history. One extra request per player, **no extra charged rows**. Default on. |
| `includeTransferHistory` | Nested transfer history. One extra request per player, **no extra charged rows**. Default on. |
| `maxItems` | Hard cap on charged rows. Default `100`. |
| `proxyConfiguration` | Leave it off — see below. |

#### Cost lever

The one input that multiplies your bill is **`searchQueries` × `maxPlayersPerQuery`**:
three names at the default `maxPlayersPerQuery: 5` is up to **15 charged rows**,
not three. Set it to `1` when you want one player per name.

Neither history toggle affects the bill. They add a request each, not a row.

***

### Why this is cheap to run

Transfermarkt is fronted by AWS WAF, and the usual answer to that — a
residential proxy at $8/GB, on pages that are 124 KB each — is what makes most
scrapers of this site expensive. It turns out **Apify's own egress is not
challenged**, so this Actor calls Transfermarkt directly and pays nothing for
proxy. A residential IP is fetched only if a request is genuinely challenged,
and most runs never touch one.

Measured on the platform, a 20-player run with both histories: **7.1 s, 75 MB
peak, 0.00049 compute units, $0.000053 of platform usage** — about **$0.0027
per 1,000 players**.

That is also why the Actor start fee is **$0.00005** and the default memory is
**256 MB**. Apify charges the start fee once per gigabyte, so a 4 GB Actor with
a $0.10 start fee bills **$0.40 before returning a single row**. Several
Transfermarkt scrapers on the Store do exactly that. On a 10-player evaluation
run this one costs about **$0.012**, against **$0.10–$0.40 of start fee alone**
elsewhere.

**Do not turn on Apify's datacenter proxy.** Transfermarkt's WAF challenges
Apify's datacenter pool while leaving its direct egress alone — verified, same
URLs, same second — so switching it on takes a working run and breaks it. The
proxy input is there for people who need a specific configuration; the default
of "no proxy" is the right answer.

***

### What makes this different

- **The whole valuation history, not just today's number.** Both histories come
  from Transfermarkt's own JSON endpoints, so they are exact rather than scraped
  out of a chart, and adding them costs you nothing per row.
- **A missing player is reported, not invented.** Transfermarkt answers an
  unknown ID with **HTTP 200** and quietly redirects to its "most valuable
  players" page — a complete, valid page about something else. A scraper that
  trusts the status code returns a plausible-looking empty row. This one checks
  where the request actually landed and emits an unbilled `PLAYER_NOT_FOUND`.
- **You are never billed for a failure.** Rows carrying `error` are pushed for
  visibility and never charged. Bad input does not fail the run either — it
  returns an `INVALID_INPUT` row explaining what to fix.
- **Partial failures are named.** If some IDs could not be fetched, the run's
  status message lists them so you can retry those specific ones, instead of
  leaving you to diff a short dataset against your input.
- **A weekly canary** runs the deployed build against live Transfermarkt and
  opens a GitHub issue if field coverage drops — because the unit tests run on
  captured fixtures and would stay green through a redesign.

***

### What this Actor does NOT return

- **Transfer rumours.** Every rumour URL Transfermarkt's structure suggests
  (`/geruechtekueche/…`, `/geruechte/…`) returns **404**. There is no public
  rumour surface to scrape, so nothing is emitted rather than something guessed.
- **Match-by-match performance logs and injury history.** Both exist on
  Transfermarkt and neither is in this Actor's scope yet.
- **Anything requiring a Transfermarkt login**, including Transfermarkt PRO data.
- **A "current" market value that is fresher than Transfermarkt's own.** Values
  are revalued periodically; `marketValueLastUpdate` tells you exactly when the
  number you got was set.

***

### Example runs

**One player, everything:**

```json
{ "playerIds": ["418560"] }
```

**A squad's worth of IDs, no histories (fastest and smallest):**

```json
{
  "playerIds": ["418560", "581678", "937958"],
  "includeMarketValueHistory": false,
  "includeTransferHistory": false
}
```

**By name, one player each:**

```json
{ "searchQueries": ["Haaland", "Bellingham", "Yamal"], "maxPlayersPerQuery": 1 }
```

***

### Output formats

JSON, JSONL, CSV, Excel, XML and RSS, from the Console or the API:

```
https://api.apify.com/v2/datasets/<datasetId>/items?format=csv
```

The nested `marketValueHistory` and `transferHistory` arrays flatten into
indexed columns in CSV and Excel. Turn them off if you want a strictly flat
table.

***

### Related Actors

Part of a Transfermarkt suite:

- **Transfermarkt Player & Market Value Scraper** — this one.
- **Transfermarkt Club Squad Scraper** — a full squad per club, with values,
  contracts and joined dates.
- **Transfermarkt Competition Scraper** — league tables, participating clubs
  and top market values, for leagues and cups.
- **Transfermarkt Transfers Scraper** — transfer feeds by club, competition or
  the global latest-transfers stream.

The `playerId` this Actor returns is the input to the others, and vice versa.

# Actor input Schema

## `playerIds` (type: `array`):

Transfermarkt player IDs (e.g. 418560) or any Transfermarkt player URL — the profile, transfers or injuries page all work, and the name part of the URL is ignored. One charged row per player.

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

Player names to look up when you don't have IDs. Each name costs one extra request and yields up to `maxPlayersPerQuery` players, so a run's charged rows can exceed the number of names you typed.

## `maxPlayersPerQuery` (type: `integer`):

How many players to take from each name search. Transfermarkt returns its own relevance order, so 1 is usually the player you meant.

## `includeMarketValueHistory` (type: `boolean`):

Adds every recorded valuation for the player — date, value, club and age — as a nested array. One extra request per player; does not add charged rows.

## `includeTransferHistory` (type: `boolean`):

Adds every career transfer — date, both clubs, fee and market value at the time — as a nested array. One extra request per player; does not add charged rows.

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

Hard cap on charged rows for this run, counting both IDs and search hits.

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

Leave this off. Transfermarkt is reachable directly from Apify at no proxy cost; a residential IP is used automatically only if a request is actually challenged. Apify's datacenter proxy is blocked by Transfermarkt and will make a working run fail.

## Actor input object example

```json
{
  "playerIds": [
    "418560",
    "581678"
  ],
  "searchQueries": [
    "Lamine Yamal"
  ],
  "maxPlayersPerQuery": 5,
  "includeMarketValueHistory": true,
  "includeTransferHistory": true,
  "maxItems": 100,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

Dataset containing every scraped player.

# 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 = {
    "playerIds": [
        "418560",
        "https://www.transfermarkt.com/jude-bellingham/profil/spieler/581678"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("incognito_mode/transfermarkt-player-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 = { "playerIds": [
        "418560",
        "https://www.transfermarkt.com/jude-bellingham/profil/spieler/581678",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("incognito_mode/transfermarkt-player-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 '{
  "playerIds": [
    "418560",
    "https://www.transfermarkt.com/jude-bellingham/profil/spieler/581678"
  ]
}' |
apify call incognito_mode/transfermarkt-player-scraper --silent --output-dataset

```

## MCP server setup

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