Cardmarket price data
Cardmarket price data for Pokémon cards, over a REST API
European asking prices in euro, one series per printing language, with a per-language index on top. The same card in Italian, German and English is three rows, because it is three prices.
- Currency
- EUR, as published
- Basis
- ASKING: what sellers list, not what buyers paid
- Granularity
- Per card, per printing language
- Refresh
- Western cards daily; Japanese and Chinese about every 48 hours
If your users are in Europe, a price in dollars from a US guide is the wrong number: it is a different market with different supply. The rows with source: "CARDMARKET" are European asking prices in euro, one series per printing language, because an Italian and an English copy of the same card do not sell for the same money.
The index follows the same split. index.by_locale carries one euro figure per printing language with its own sample_n, so a French-language shop can price on French copies and a marketplace that mixes languages can read the overall figure. Next to the Cardmarket rows you get CardTrader, also in EUR, and the composite index, so a card that one source does not list still has a number.
One call, and what comes back
The Cardmarket row of one card as the trial reads it, English only, and above it the per-language index: one euro figure per printing language, each with its sample size. The request filters with source=CARDMARKET; drop the parameter and the other sources come back.
curl -s -G "https://api.pokemontcgapi.com/v1/cards/base1-4/prices" \
--data-urlencode "source=CARDMARKET" \
-H "X-Api-Key: $PTCG_API_KEY"const url = new URL("https://api.pokemontcgapi.com/v1/cards/base1-4/prices");
url.searchParams.set("source", "CARDMARKET");
const res = await fetch(url, {
headers: { "X-Api-Key": process.env.PTCG_API_KEY ?? "" },
});
if (!res.ok) {
const { error } = await res.json();
throw new Error(`${error.code}: ${error.message} (${error.request_id})`);
}
const { data, meta } = await res.json();import os, httpx
res = httpx.get(
"https://api.pokemontcgapi.com/v1/cards/base1-4/prices",
params={"source": "CARDMARKET"},
headers={"X-Api-Key": os.environ["PTCG_API_KEY"]},
)
res.raise_for_status()
payload = res.json(){
"data": {
"card_id": "base1-4",
"index": {
"eur": 556.23,
"as_of": "2026-09-02",
"sample_n": 16,
"by_locale": [
{
"locale": "en",
"printing": null,
"eur": 556.23,
"as_of": "2026-09-02",
"sample_n": 16
},
{
"locale": "fr",
"printing": null,
"eur": 900.58,
"as_of": "2026-09-02",
"sample_n": 3
},
{
"locale": "de",
"printing": null,
"eur": 399.95,
"as_of": "2026-09-02",
"sample_n": 5
},
{
"locale": "es",
"printing": null,
"eur": 1313.14,
"as_of": "2026-09-02",
"sample_n": 8
},
{
"locale": "it",
"printing": null,
"eur": 400.64,
"as_of": "2026-09-02",
"sample_n": 11
}
]
},
"quotes": [
{
"source": "CARDMARKET",
"variant": "LOW",
"basis": "ASKING",
"amount": 599.9,
"currency": "EUR",
"locale": "en",
"condition": "NEAR_MINT",
"printing": null,
"grading": null,
"as_of": "2026-09-01",
"sample_n": null,
"provenance": "Cardmarket"
}
]
},
"meta": {
"quotes": 5,
"delayed_hours": 0
}
}Which languages each plan reads
English-language rows are on every key, the trial included, which is enough to prove the integration. The rows of the other printing languages (German, French, Italian, Spanish and the rest) come with Developer (€29/month) and above; below that, meta.withheld says non_english_locales so your code knows the rows exist and can offer the upgrade instead of showing an empty state.
A euro series per language, back in time
GET /v1/cards/{id}/prices/history takes source=CARDMARKET with locale and variant, and a bucket of day, week or month, so a chart of the German copy alone is one request. The window is the plan's: 7 days on the trial, 30 on Developer, everything we hold from Growth; meta.plan_window_days says which applied.
Joining your own product table
Every card carries cardmarket_id where the pairing is known, so once you hold the card you can join it to a database keyed on Cardmarket product ids. The lookup itself goes by card id, set code and number, and GET /v1/cards/batch resolves up to 100 ids a call, naming the ones it could not find. There is no search by marketplace id.
What it is not
Read this before you build on it. Every line here is a support ticket somebody else already opened.
- This is not the Cardmarket API
- You cannot list, buy, sell or read your Cardmarket account here. This API serves price data read from the market, with attribution, for applications that display or analyse card values.
- Asking prices, re-read on a cadence
- A row is the lowest asking price or a rolling average of asking prices, never a completed sale. Western cards are re-read daily, Japanese and Chinese ones about every 48 hours, and a point is served no sooner than 24 hours after capture.
- Attribution travels with the row
- Each row carries a
provenancestring. Show it next to the number: it is what your users will ask for the first time a price looks wrong.
Questions people ask before signing up
- Is this the official Cardmarket API?
- No. pokemontcgapi.com is an independent service with no affiliation to Cardmarket, which is named here only as the source of a price row, the way a newspaper names the exchange a quote came from. Listing, buying or selling on Cardmarket needs their own tools.
- Do I need a Cardmarket seller account to use it?
- No. A pokemontcgapi.com key is all it takes, and the trial key is free. Nothing here reads or writes a Cardmarket account. Free trial key
- Are prices split by card language?
- Yes, twice over: each Cardmarket row carries the printing language in locale, and index.by_locale gives one euro figure per language. English rows are on every plan; the other languages come with Developer and above. Pricing
- Can I get Cardmarket price history?
- Yes, from GET /v1/cards/{id}/prices/history with source=CARDMARKET, optionally one locale and one variant. The window depends on the plan, from 7 days on the trial to the full series on Growth.