# Cardmarket Scraper (`unfenced-group/cardmarket-scraper`) Actor

Daily EU trading card market prices from Cardmarket for 20 games. Trend price, lowest listing, 1/7/30-day averages and foil variants for both single cards and sealed product, with real set names and price movement since your last run.

- **URL**: https://apify.com/unfenced-group/cardmarket-scraper.md
- **Developed by:** [Unfenced Group](https://apify.com/unfenced-group) (community)
- **Categories:** E-commerce
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.15 / 1,000 cards

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## Cardmarket Scraper

![Cardmarket Scraper](https://api.apify.com/v2/key-value-stores/0lXEtF7aT95jMSnRF/records/banner.png)

Extract daily European market prices from Cardmarket for 20 trading card games, covering both single cards and sealed product, with trend price, lowest listing, 1/7/30-day averages, foil and holo variants, real set names, and price movement since your last run. No API key required.

***

### Why this scraper?

#### 🎮 20 games in one actor

Pokémon, Magic: The Gathering, Yu-Gi-Oh!, One Piece, Disney Lorcana, Digimon, Flesh and Blood, Star Wars: Unlimited, Dragon Ball Super, Riftbound and eleven more. Pick the game in the input rather than hunting for a separate actor per game.

#### 📦 Sealed product, not just singles

Booster boxes, displays, elite trainer boxes, tins, theme decks, blisters and coins are priced alongside single cards. Set `productType` to `sealed` or `all`. Sealed is where the largest price movements happen and it is the part of the market most tools leave out.

#### 🏷️ Real set names

Cardmarket's catalogue identifies sets by numeric ID only. This scraper resolves those IDs to readable expansion names, and to official three-letter set codes for Magic. You get `"Obsidian Flames"`, not `1234`.

#### 📈 Full price history per card

Trend price, current lowest listing, and 1-day, 7-day and 30-day sale averages, for the regular printing and the foil or holo printing separately. Momentum percentages against the 7-day and 30-day averages are calculated for you.

#### 🔁 Price movement between runs

Every record carries `changeStatus` (`NEW`, `PRICE_UP`, `PRICE_DOWN`, `UNCHANGED`), the previous trend price, and the percentage change. Schedule it daily and the output reads as a movement feed rather than a flat snapshot.

#### 🇺🇸 EU versus US spread

For Magic, switch on `includeUsPrices` to add the US market price per card, converted to euros, and the percentage spread against the Cardmarket trend price. That is the cross-Atlantic arbitrage gap on a per-card basis. Printings with no US counterpart, such as Summer Magic and the 30th Anniversary set, are caught before they reach the output: the spread comes back `null` with a reason in `usMatchConfidence` rather than a false gap.

#### 🔒 No seller data

This scraper collects no seller usernames, no seller locations, no ratings, and no personal data of any kind. It reads market-level price data only.

***

### Input parameters

| Parameter | Type | Default | Description |
|---|---|---|---|
| `game` | string | `"pokemon"` | Which trading card game to price. One of `pokemon`, `magic`, `yugioh`, `onepiece`, `lorcana`, `digimon`, `fleshandblood`, `starwarsunlimited`, `dragonballsuper`, `riftbound`, `battlespiritssaga`, `finalfantasy`, `forceofwill`, `cardfightvanguard`, `weissschwarz`, `starwarsdestiny`, `worldofwarcraft`, `mylittlepony`, `dragoborne`, `thespoils`. |
| `searchQuery` | string | (empty) | Case-insensitive filter on the product name. Leave empty for the whole game catalogue. |
| `productType` | string | `"singles"` | `singles`, `sealed`, or `all`. |
| `expansionNames` | array | `[]` | Keep only these expansions. Accepts the resolved set name, or the three-letter set code for Magic. Empty means all expansions. |
| `priceMinEur` | integer | (empty) | Keep products whose trend price is at least this many euros. |
| `priceMaxEur` | integer | (empty) | Keep products whose trend price is at most this many euros. |
| `onlyPriced` | boolean | `true` | Skip products with no trend price in today's price guide. |
| `includeUsPrices` | boolean | `false` | Add the US market price and EU/US spread. Magic only. Adds roughly a minute to the run. |
| `sortBy` | string | `"trendDesc"` | `trendDesc`, `trendAsc`, `momentum` (biggest 7-day movers first), or `name`. |
| `maxResults` | integer | `1000` | Maximum number of products to return. |

***

### Output schema

Every field below is present on every record. Fields Cardmarket does not publish for a given product are returned as `null` rather than omitted.

#### Always present

| Field | Type | Description |
|---|---|---|
| `idProduct` | integer | Cardmarket's stable product ID. Use it as the primary key across runs. |
| `name` | string | Product name as Cardmarket publishes it. |
| `game` | string | Display name of the trading card game, for example `Pokémon`. |
| `gameSlug` | string | Machine-readable game key matching the `game` input value. |
| `productType` | string | `single` for a card, `sealed` for boxes, displays, tins and similar. |
| `categoryId` | integer | Cardmarket's product category ID. |
| `categoryName` | string | Category label, for example `Pokémon Display` or `Magic Single`. |
| `idExpansion` | integer | Cardmarket's expansion (set) ID. |
| `expansionName` | string | null | Resolved set name. Null for a small number of promo and one-off expansions Cardmarket does not name in its published files. |
| `expansionCode` | string | null | Official three-letter set code. Populated for Magic only; null for other games. |
| `expansionSource` | string | null | How the set name was resolved: `atlas` or `catalogue`. |
| `dateAdded` | string | null | ISO 8601 date the product entered Cardmarket's catalogue. |
| `currency` | string | Always `EUR`. Cardmarket is a euro marketplace. |
| `priceGuideDate` | string | Date of the price guide the figures come from, `YYYY-MM-DD`. |
| `trendEur` | number | null | Cardmarket's trend price, the headline market value. |
| `lowEur` | number | null | Lowest current listing price. |
| `avgEur` | number | null | All-time average sale price. |
| `avg1DayEur` | number | null | Average sale price over the last day (null when there were no sales). |
| `avg7DayEur` | number | null | Average sale price over the last 7 days. |
| `avg30DayEur` | number | null | Average sale price over the last 30 days. |
| `variantType` | string | null | `foil` or `holo`, naming which premium printing the variant fields describe. |
| `variantTrendEur` | number | null | Trend price of the foil or holo printing. |
| `variantLowEur` | number | null | Lowest listing for the foil or holo printing. |
| `variantAvgEur` | number | null | All-time average for the foil or holo printing. |
| `variantAvg1DayEur` | number | null | 1-day average for the foil or holo printing. |
| `variantAvg7DayEur` | number | null | 7-day average for the foil or holo printing. |
| `variantAvg30DayEur` | number | null | 30-day average for the foil or holo printing. |
| `momentum7Pct` | number | null | Trend price against the 7-day average, as a percentage. Positive means rising. |
| `momentum30Pct` | number | null | Trend price against the 30-day average, as a percentage. |
| `trendVsLowPct` | number | null | How far the cheapest listing sits below the trend price, as a percentage. |
| `variantPremiumPct` | number | null | Premium of the foil or holo printing over the regular one, as a percentage. |
| `searchUrl` | string | Cardmarket search link that resolves to this product. |
| `source` | string | Always `cardmarket.com`. |
| `scrapedAt` | string | ISO 8601 timestamp of the run. |
| `contentHash` | string | Hash of the price fields, used to detect change between runs. |
| `changeStatus` | string | `NEW`, `PRICE_UP`, `PRICE_DOWN`, `UPDATED` or `UNCHANGED` versus your previous run. |
| `previousTrendEur` | number | null | Trend price recorded on your previous run. Null on first sight. |
| `trendChangePct` | number | null | Percentage change in trend price since your previous run. |

#### With `includeUsPrices: true` (Magic only)

| Field | Type | Description |
|---|---|---|
| `usMarketUsd` | number | null | US market price for the regular printing, in dollars. |
| `usMarketFoilUsd` | number | null | US market price for the foil printing, in dollars. |
| `usMarketEur` | number | null | US market price converted to euros at the day's European Central Bank rate. |
| `euUsSpreadPct` | number | null | US price against the Cardmarket trend price, as a percentage. Positive means the card is cheaper in Europe. Returned as `null` when the US match is not reliable, so a bad match never reads as an arbitrage gap. |
| `usMatchConfidence` | string | null | `ok` when the two prices are comparable, `no_price` when no US price was found, `price_ratio_implausible` when one market sits below a tenth of the other and the match was rejected. Null on runs without `includeUsPrices`. |

#### Example record

```json
{
  "idProduct": 5395,
  "name": "Lightning Bolt",
  "game": "Magic: The Gathering",
  "gameSlug": "magic",
  "productType": "single",
  "categoryId": 1,
  "categoryName": "Magic Single",
  "idExpansion": 1,
  "expansionName": "Limited Edition Alpha",
  "expansionCode": "LEA",
  "expansionSource": "atlas",
  "dateAdded": "2007-01-01T00:00:00Z",
  "currency": "EUR",
  "priceGuideDate": "2026-09-04",
  "trendEur": 409.41,
  "lowEur": 379.00,
  "avgEur": 421.86,
  "avg1DayEur": 399.00,
  "avg7DayEur": 415.20,
  "avg30DayEur": 418.75,
  "variantType": "foil",
  "variantTrendEur": null,
  "variantLowEur": null,
  "variantAvgEur": null,
  "variantAvg1DayEur": null,
  "variantAvg7DayEur": null,
  "variantAvg30DayEur": null,
  "momentum7Pct": -1.4,
  "momentum30Pct": -2.2,
  "trendVsLowPct": -7.4,
  "variantPremiumPct": null,
  "usMarketUsd": 626.67,
  "usMarketFoilUsd": null,
  "usMarketEur": 539.20,
  "euUsSpreadPct": 31.7,
  "usMatchConfidence": "ok",
  "searchUrl": "https://www.cardmarket.com/en/Magic/Products/Search?searchString=Lightning%20Bolt",
  "source": "cardmarket.com",
  "scrapedAt": "2026-09-04T19:32:15.672Z",
  "contentHash": "ffd387447a0bbf27",
  "changeStatus": "PRICE_DOWN",
  "previousTrendEur": 415.00,
  "trendChangePct": -1.3
}
```

***

### Examples

**Every priced Pokémon card, most valuable first:**

```json
{
  "game": "pokemon",
  "productType": "singles",
  "sortBy": "trendDesc",
  "maxResults": 5000
}
```

**Sealed Pokémon product above 100 euros, for reseller sourcing:**

```json
{
  "game": "pokemon",
  "productType": "sealed",
  "priceMinEur": 100,
  "sortBy": "trendDesc",
  "maxResults": 1000
}
```

**Magic cards with the EU/US spread, for cross-market arbitrage:**

```json
{
  "game": "magic",
  "productType": "singles",
  "priceMinEur": 20,
  "includeUsPrices": true,
  "sortBy": "trendDesc",
  "maxResults": 2000
}
```

**Daily movement feed for one Lorcana set:**

```json
{
  "game": "lorcana",
  "expansionNames": ["The First Chapter"],
  "productType": "all",
  "sortBy": "momentum",
  "maxResults": 1000
}
```

***

### 💰 Pricing

**$0.19 per 1,000 results** — you only pay for products actually returned. Failed retries are never charged.

| Results | Cost |
|---|---|
| 100 | ~$0.02 |
| 1,000 | ~$0.19 |
| 10,000 | ~$1.90 |
| 100,000 | ~$19.00 |

> Flat-rate alternatives typically charge $29–$49/month regardless of usage.

Subscription tiers lower the rate further, down to $0.15 per 1,000 at the Gold tier. Use the **Max results** cap in the input to control your spend exactly.

***

### Performance

| Run size | Typical time |
|---|---|
| 1,000 results | under 1 minute |
| 10,000 results | 1–2 minutes |
| 100,000 results | 3–5 minutes |

Adding `includeUsPrices` costs roughly one extra minute per run, regardless of result count.

***

### Known limitations

- A small share of expansions carry no resolvable set name, mostly promotional and one-off sets. Coverage runs from about 86% to 98% of cards depending on the game. Where the name cannot be resolved, `expansionName` is `null` and `idExpansion` is still returned rather than a guess being made.
- `expansionCode` is populated for Magic only. Other games have no official short code in the published data.
- Cardmarket publishes no product-URL mapping in its catalogue files, so `searchUrl` is a search link rather than a direct product link. It resolves to the product reliably.
- Some games publish only a trend figure for the foil printing and no foil averages. The variant average fields are `null` in those cases.
- Prices refresh once per day, in the early hours European time. Running more often than daily returns the same figures.
- The US market comparison covers Magic only, and reaches about 70% of priced Magic singles. Coverage is thinner at the top of the market: roughly 75% of cards between 20 and 100 euros carry a US price, falling to about 26% above 1,000 euros, because US market data is sparse for the rarest cards. Cards without a US price return `null` in those fields rather than an estimate. Where a US price exists but the two markets are more than a factor of ten apart, the match is treated as unreliable: `euUsSpreadPct` is `null` and `usMatchConfidence` says why. This affects under 1% of priced Magic singles above 20 euros.

***

### Technical details

- **Source:** cardmarket.com — European trading card market prices
- **Coverage:** 20 games, single cards and sealed product, all expansions
- **Refresh:** daily price guide, dated in every record via `priceGuideDate`
- **Retry:** automatic retry on network errors with exponential backoff

***

### Rate this actor

If this scraper does its job, a short review on the **Reviews** tab helps other users find it. Something not working? Open an issue on the **Issues** tab instead — issues get fixed.

***

### Need a custom scraper?

**[Unfenced Group](https://www.unfencedgroup.nl)** builds Apify actors for any website — for free.

If the site you need isn't in our portfolio yet, just ask. We scope, build, and publish it at no cost to you. You only pay for results — we absorb the compute and proxy costs ourselves. Same pay-per-result pricing, same quality, same standards as every actor in this portfolio.

**Get in touch:** [www.unfencedgroup.nl](https://www.unfencedgroup.nl)

# Actor input Schema

## `game` (type: `string`):

Which trading card game to price. One run covers one game. Example: pokemon.

## `searchQuery` (type: `string`):

Case-insensitive filter on the product name. Leave empty to return the whole game catalogue. Example: charizard.

## `productType` (type: `string`):

Single cards, sealed product (booster boxes, displays, elite trainer boxes, tins, theme decks), or both. Example: singles.

## `expansionNames` (type: `array`):

Keep only these expansions. Accepts the resolved set name or, for Magic, the three-letter set code. Leave empty for all expansions. Example: \["Base Set", "Jungle"].

## `priceMinEur` (type: `integer`):

Keep products whose trend price is at least this many euros. Leave empty for no lower bound. Example: 5.

## `priceMaxEur` (type: `integer`):

Keep products whose trend price is at most this many euros. Leave empty for no upper bound. Example: 500.

## `onlyPriced` (type: `boolean`):

Skip products that have no trend price in today's price guide. Turn off to include the full catalogue including unpriced products. Example: true.

## `includeUsPrices` (type: `boolean`):

Adds the US market price per card and the EU/US spread percentage, for arbitrage between Cardmarket and the US market. Available for Magic: The Gathering only and adds about a minute to the run. Example: false.

## `sortBy` (type: `string`):

Order of the returned rows. Use momentum to surface the biggest short-term movers first. Example: trendDesc.

## `maxResults` (type: `integer`):

Maximum number of products to return. Controls your spend exactly. Example: 1000.

## Actor input object example

```json
{
  "game": "pokemon",
  "searchQuery": "charizard",
  "productType": "singles",
  "expansionNames": [
    "Base Set"
  ],
  "onlyPriced": true,
  "includeUsPrices": false,
  "sortBy": "trendDesc",
  "maxResults": 1000
}
```

# Actor output Schema

## `results` (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 = {
    "game": "pokemon",
    "searchQuery": "charizard",
    "productType": "singles",
    "expansionNames": [
        "Base Set"
    ],
    "sortBy": "trendDesc"
};

// Run the Actor and wait for it to finish
const run = await client.actor("unfenced-group/cardmarket-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 = {
    "game": "pokemon",
    "searchQuery": "charizard",
    "productType": "singles",
    "expansionNames": ["Base Set"],
    "sortBy": "trendDesc",
}

# Run the Actor and wait for it to finish
run = client.actor("unfenced-group/cardmarket-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 '{
  "game": "pokemon",
  "searchQuery": "charizard",
  "productType": "singles",
  "expansionNames": [
    "Base Set"
  ],
  "sortBy": "trendDesc"
}' |
apify call unfenced-group/cardmarket-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,unfenced-group/cardmarket-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/XA7SYbATIMTqbTILM/builds/jyefaiI2beyG90bbp/openapi.json
