# Transfermarkt Scraper: Players, Transfers & Club Squads (`s-r/transfermarkt-scraper`) Actor

Collect Transfermarkt football player profiles, current and historical market values, transfer fees and club squads by competition and season. Search by name, player ID or URL.

- **URL**: https://apify.com/s-r/transfermarkt-scraper.md
- **Developed by:** [SR](https://apify.com/s-r) (community)
- **Categories:** Automation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$1.00 / 1,000 results

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?

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

## Transfermarkt Scraper

Transfermarkt Scraper collects football player profiles, market values, transfer histories and club squads. Start with a player, club, competition or player name and export structured records for scouting, fantasy football research or editorial analysis.

### What Transfermarkt Scraper collects

Player records include the name, player identifier, date of birth, nationalities, field position, current team, contract end date and current market value when available. Additional fields describe height, preferred foot, agent, previous value, highest value and the dates associated with those valuations. Optional market value history appears as a list on the player record, with dates, values in euros and the club associated with each historical point.

Transfer records describe a player's move between teams. They include the transfer date, season, source and destination teams, transfer type, fee and whether the move is pending. Permanent moves, loans, returns from loan and internal promotions have distinct normalized types. The original transfer type and fee label remain available as additional fields, allowing you to inspect the source terminology when you need more detail.

Club squad records represent one player in one team's squad for one season. They include shirt number, field position, nationalities, contract information and market value where the source shows those fields. Historical squad pages can include the player's current team instead of a historical contract. Such records leave the historical contract date empty rather than copying a current contract into an earlier season.

### Input

You can provide numeric identifiers as strings in `player_ids` or `club_ids`. Use `player_urls` and `club_urls` when you already have Transfermarkt links. The `start_urls` field accepts a mixed list of player, club and competition URLs and identifies the requested entity from each URL path. Each list supports up to 500 entries. Repeated entities are deduplicated during collection.

For competition squads, set `competition` to a code such as `GB1` or provide a competition URL. Set `season` to the season's starting year: `2025` means the 2025/26 season. Omitting the season requests the current squad shown by the source. The season setting applies to squad membership; full player profiles describe the player as currently published.

To find players by name, use `search`. Search follows the source's ranking and collects additional player search pages as needed. A broad name can match several people, so retain the returned player identifiers when joining results with other datasets.

```json
{
  "player_ids": ["418560"],
  "include_market_value_history": true,
  "include_transfers": true,
  "max_results": 100
}
```

Market value history and transfer history are enabled by default. Club and competition requests return squad records by default. Turn on `include_player_details` to add full player profiles and their requested histories for squad members. This option is disabled for non-paying users. Set `max_results` to control the total number of rows, including all record types, with a maximum of 10,000.

### Output

Results appear in the default dataset. Every record has `row_type` and `_type`, whose values are `player`, `transfer` or `club_squad`. Every record also includes `source`, `sport`, `fetched_at`, `player_id` and a stable `_identity`. Player records use `name` for the person's name; transfer and squad records use `player`. Team fields use names such as `team`, `from_team` and `to_team` with corresponding identifiers.

Market values and transfer fees are numeric amounts. The `currency` field describes the main amount on that record. An explicitly free transfer has a fee of zero. An unknown fee remains null. A retired player without an active market valuation has a null current market value. Historical valuation points use `value_eur`, while the additional market value on a transfer record is expressed in euros independently of whether a fee is known.

The season on output records is the starting year as text. The extra `season_label` preserves the familiar season display. Player records precede that player's transfer records. Squad requests can produce many rows, so a single player's historical transfers may use several rows of your chosen result limit. The run output record contains warnings and collection status, including any unavailable requested enrichment.

### Use cases

- **Scouting:** build shortlists with current contract end dates, positions and Transfermarkt market value history per player.
- **Fantasy football:** track player values and squad changes by competition and season before each round.
- **Media and analysis:** pull transfer fees and club squads for stories, charts and season reviews.
- **Data pipelines:** use it as a Transfermarkt API alternative that returns typed JSON rows with stable identifiers, ready for a database or spreadsheet.

### How it compares

Other Transfermarkt actors on the Store mostly return a single record type or require a player name per run. This actor returns three linked record types (`player`, `transfer`, `club_squad`) in one dataset, accepts names, identifiers, URLs or a competition with a season, and keeps unknown values as null instead of guessing. Zero fees and unknown fees are kept apart, so your transfer analysis stays correct.

### Pricing and result limits

Billing uses the `result` event once per delivered dataset row. Player, transfer and club squad rows each count as one result. The current event price is displayed in the actor's Pricing tab. Requests that return no dataset records generate no result charges. Non-paying runs are capped at ten delivered rows across all record types; those delivered rows remain subject to the displayed event price.

The `max_results` setting is a total limit, including continuation pages. Collection continues automatically while there is work and room within that limit. Requested enrichment that cannot be retrieved is reported through warnings and partial status. It does not produce invented substitute records. Check the run output before treating a partially collected history as complete.

### FAQ

#### Can I retrieve a squad from a previous season?

Yes. Provide a club or competition together with the season's starting year. Historical squads are read for that season. Fields absent from the historical page remain null. Optional full player profiles still represent current published profiles, so use the squad fields when you need season-specific membership or valuations.

#### How are free transfers different from unknown fees?

A fee is zero only when the source explicitly labels the move as a free transfer. A missing or unknown fee is null. The extra fee label preserves the source wording, helping you distinguish a known free move from a transfer whose financial terms were not published.

#### Does a player name search return only one person?

No. A search can return several players with similar names. Results follow the source's order. Use player identifiers to select the person you need, then request that identifier directly for repeat collection.

#### What happens if some data is unavailable?

Available records are delivered with warnings describing missing requested information. Partial status signals incomplete collection. No values are generated to replace missing source data. For reliable comparisons, keep the collection timestamp and inspect warnings alongside the records.

#### Is this actor affiliated with Transfermarkt?

No. It is an independent data collection tool for publicly available football information. Transfermarkt owns its names and source content. Use the collected data in accordance with the source's terms and the rights applicable to your intended use.

# Actor input Schema

## `player_ids` (type: `array`):

Numeric Transfermarkt player IDs.

## `player_urls` (type: `array`):

Transfermarkt player profile URLs.

## `club_ids` (type: `array`):

Numeric Transfermarkt club IDs.

## `club_urls` (type: `array`):

Transfermarkt club URLs.

## `start_urls` (type: `array`):

Transfermarkt player, club or competition URLs; the path determines the record type.

## `competition` (type: `string`):

Competition code such as GB1 or a Transfermarkt competition URL.

## `season` (type: `integer`):

Season start year: 2025 means 2025/26. Omit for the current source season. Applies to clubs and competitions. Maximum: next calendar year.

## `search` (type: `string`):

Player name search. Results follow the source ranking; additional search pages are collected automatically.

## `max_results` (type: `integer`):

Total delivered rows across all record types and continuation pages, not a limit per club or player.

## `cursor` (type: `string`):

Opaque next_cursor returned by a previous request. Keep all other input fields unchanged.

## `include_market_value_history` (type: `boolean`):

Include market value history on player rows.

## `include_transfers` (type: `boolean`):

Include transfer rows after each player, including internal transfers and pending moves.

## `include_player_details` (type: `boolean`):

For club or competition inputs, also collect full player profiles and their requested histories. Disabled for free users.

## Actor input object example

```json
{
  "player_ids": [
    "418560"
  ],
  "max_results": 100,
  "include_market_value_history": true,
  "include_transfers": true,
  "include_player_details": false
}
```

# Actor output Schema

## `results` (type: `string`):

One row per player, transfer or squad membership, distinguished by row_type and \_type.

## `output` (type: `string`):

OUTPUT record with the run's counts and status flags.

## `errors` (type: `string`):

Failures with a code and a redacted message. Absent when the run had none.

# 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 = {
    "player_ids": [
        "418560"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("s-r/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 = { "player_ids": ["418560"] }

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

```

## MCP server setup

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