# Transfermarkt Scraper (`toned_jade/transfermarkt-scraper`) Actor

FastAPI service scraping Transfermarkt, served as an HTTP API via Apify Standby (scale-to-zero: the container stops when idle and cold-starts on the next request). The OpenAPI schema drives Apify's automatic MCP tool generation.

- **URL**: https://apify.com/toned\_jade/transfermarkt-scraper.md
- **Developed by:** [Bob The Builder](https://apify.com/toned_jade) (community)
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 successful data requests

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

Extract Transfermarkt football data: player profiles, market values, transfer fees, club squads, national-team call-ups, match line-ups, league standings and top scorers. Query it as a REST API or call it directly from an AI agent as 33 named tools. No login, no API key on the target site, JSON out.

### What Transfermarkt API can do

|  | |
|---|---|
| ⚽ **Players** | Profiles, market-value history, full transfer record, career, achievements, injuries, shirt numbers, per-season stats |
| 🏟 **Clubs** | Squads, profiles, transfers in and out, season fixtures across every competition |
| 🏆 **Competitions** | Standings, member clubs, top scorers, assists, goal contributions, clean sheets, attendance, past champions |
| 📋 **Matches** | Starting XI, substitutes and manager for both sides of any match |
| 💶 **Market** | Most valuable players, latest transfers, all-time transfer records, market-value movers, free agents, expiring contracts |
| 🌍 **Rankings** | FIFA world ranking, UEFA club coefficients, available coaches |
| 🤖 **AI agents** | Every endpoint is published as an individually named MCP tool, so an agent can call `get_player_transfers` directly |

### What data you get

Every response carries an `updatedAt` timestamp and the absolute Transfermarkt `url` it came from, so any number can be traced back to the page it was read off.

Player search:

```json
{
  "updatedAt": "2026-08-12T10:43:59Z",
  "query": "haaland",
  "url": "https://www.transfermarkt.com/schnellsuche/ergebnis/schnellsuche?query=haaland",
  "pageNumber": 1,
  "lastPageNumber": 2,
  "results": [
    {
      "id": "418560",
      "name": "Erling Haaland",
      "position": "CF",
      "club": { "id": "281", "name": "Manchester City" },
      "age": 26,
      "nationalities": ["Norway"],
      "marketValue": 220000000
    }
  ]
}
```

A player inside a match line-up, carrying the club he was registered with **on that date**:

```json
{
  "id": "565093",
  "name": "Bart Verbruggen",
  "shirtNumber": "1",
  "position": "Goalkeeper",
  "nationality": ["Netherlands"],
  "marketValue": 40000000,
  "clubId": "1237",
  "clubName": "Brighton & Hove Albion"
}
```

### How to use it

The actor runs in Standby mode, so it behaves like a normal HTTP API. There is no run to start and no dataset to poll.

```bash
BASE=https://toned-jade--transfermarkt-scraper.apify.actor

curl -H "Authorization: Bearer $APIFY_TOKEN" "$BASE/players/search/haaland"
curl -H "Authorization: Bearer $APIFY_TOKEN" "$BASE/players/418560/transfers"
curl -H "Authorization: Bearer $APIFY_TOKEN" "$BASE/competitions/GB1/standings"
```

Browsable docs at `/docs`, machine-readable schema at `/openapi.json`.

For AI agents, point an MCP client at the actor and the 33 endpoints arrive as named tools with typed arguments. No glue code.

Use `https://toned-jade--transfermarkt-scraper.apify.actor/mcp/` as the MCP URL
with your Apify token in the `Authorization: Bearer ...` header.

### Pricing and request limits

Each successful data request costs **$0.005 ($5 per 1,000 requests)**, whether made
through REST or an MCP tool. Cached answers and statistics endpoints have the same
price. Failed requests, documentation and tool discovery have no data-event charge.
Apify charges Standby compute and storage separately. Live data requires a paid
Apify plan; free-plan accounts can browse documentation.

A run processes one data request at a time, with a maximum of 1,000 attempts and
$5 in data events, or the lower spending limit you set on Apify. Five consecutive
failed requests stop further work in that run. HTTP 429 means retry after the
indicated delay or start a new run if its allowance is exhausted; HTTP 402 means
the run's spending limit is exhausted. Each page requested counts separately.

Successful answers are saved in the run's default key-value store before charging.
The REST response header `X-Apify-Result-Key` identifies the saved answer. If billing
is temporarily unavailable, HTTP 503 includes an `operationId`; check the record
`RESULT-<operationId>` in that run's store before repeating the data request.
Reading an existing record through Apify's storage API incurs no new data event.
Storage retention follows your Apify account settings. A new data call, including
a repeated call for the same resource, is a new billable operation.

Large or repeatedly failing upstream responses may return HTTP 502. Results reflect
the source page available at collection time; missing source fields remain missing.

### Two things that will bite you if you skip them

**`fee` and `marketValue` are not the same number.** `fee` is what a transfer actually cost. `marketValue` is Transfermarkt's own estimate. They disagree routinely, and `fee` is legitimately `0` or missing for a free transfer, a loan, or an undisclosed sum. Substituting one for the other quietly corrupts anything you calculate downstream.

**National-team line-ups resolve each player to his club on the match date, not today's club.** Ask a squad endpoint who played for Norway in a March qualifier and you get their *current* clubs, which is wrong for anything retrospective. Chain `/clubs/{id}/fixtures` into `/matches/{id}/lineups` and every player carries `clubId` and `clubName` as they stood on the day. Qualifying campaigns run across several seasons, so enumerate seasons rather than assuming one.

### Notes and exceptions

- Club endpoints detect a national team and return the call-up list with caps, goals and debut, rather than a club squad.
- A player who has retired returns `club: "Retired"` and no market value.
- Upstream 404s come back as 404. Transient upstream failures are retried before surfacing as an error.
- Season parameters are 4-digit start years: `2025` means 2025/26. Omit for the current season.
- Responses are cached briefly, so repeat calls for the same resource return without re-fetching.

### FAQ

**Do I need a Transfermarkt account?** No. It reads public pages.

**How current is the data?** Live at request time, with a short cache. `updatedAt` tells you exactly when.

**Can I get historical transfers?** Yes, `/players/{id}/transfers` returns the full career record with fees, dates and both clubs.

**What identifies a player or club?** The Transfermarkt numeric ID as a string. Get it from the search endpoints, then reuse it everywhere.

**Is it rate limited?** Each run accepts one data request at a time. Honor `Retry-After` and spread bulk work out rather than firing it in parallel.

**Is scraping Transfermarkt legal?** It reads publicly available pages. You are responsible for how you use and redistribute the data, and for complying with Transfermarkt's terms. Check them before building on this commercially.

# Actor input Schema

## Actor input object example

```json
{}
```

# Actor output Schema

## `savedAnswers` (type: `string`):

List this run's stored records. RESULT- records contain operationId, status, contentType and the JSON answer in body. Access requires your Apify credentials.

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("toned_jade/transfermarkt-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 = {}

# Run the Actor and wait for it to finish
run = client.actor("toned_jade/transfermarkt-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 '{}' |
apify call toned_jade/transfermarkt-scraper --silent --output-dataset

```

## MCP server setup

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