Current prices
Every live price series for one card — one row per source, variant, printing and grade — plus the composite index in EUR.
/v1/cards/{id}/prices2 creditsfree planCache-Control: private, max-age=300
No key yet? One call gets you one: POST /v1/accounts/free with an Idempotency-Key header and your email address returns a working key in the response. Keep the original Idempotency-Key and body for response replay for up to 24 hours, under the conditions in the quickstart. The trial is 800 credits over 30 days once the address is confirmed, no card; paid plans start at 29 € a month. See the quickstart and the pricing.
data.index is our composite: the median across sources for each locale and printing, converted to EUR, with sample_n saying how many sources agreed. data.quotes are the observations behind it, each in the currency it was seen in, each with its own as_of and a printable provenance string.
What you see depends on the plan. The trial returns English-locale, ungraded rows; paid plans return every locale and the graded rows (MEDIAN_GRADED with grading.score and, when the source names one, grading.company), which is where a PSA 10 and a raw copy stop being the same number. meta.plan_notes says what was withheld, so a missing row is never a mystery.
index is computed before the filters, on everything the plan allows: narrowing to one source changes the rows you see, never the composite value of the card. A card with no observations is a 200 with index: null and an empty quotes — thousands of cards are in that state, and it is not an error. The same rows are also available on the card itself through GET /v1/cards/{id}?include=prices, which costs the same two credits, applies the same plan filter, and is the right call when you are rendering the card anyway.
This sample is trimmed
The full response for this card carries 109 rows; 5 are shown, one per source.
Path parameters
| Name | Type | Default | Description |
|---|---|---|---|
id required | string | — | Card id (bs-4) or alternate id (base1-4). Both resolve. |
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
source | string | — | Keep only rows from one source, as listed by GET /v1/prices/sources. A card can carry over a hundred rows once graded medians are included, so this is usually the difference between a response you parse and one you filter. An unknown value is INVALID_PARAMETER with the valid list in details, never a silently unfiltered page. |
variant | string | — | Keep only one variant: LOW, MARKET, TREND, AVG_1D, AVG_7D, AVG_30D, MEDIAN_GRADED, INDEX. |
locale | string | — | Keep only rows for one printing language. Rows that do not state a language are excluded rather than assumed to match. |
Headers
| Name | Type | Default | Description |
|---|---|---|---|
X-Api-Key required | string | — | Your API key. Authorization: Bearer <key> is accepted as an alias. |
If-None-Match | string | — | Send back the ETag you stored. The API returns a strong ETag on every catalogue response, and a 304 is a header exchange: no body, no database row read. |
Example request
curl -s "https://api.pokemontcgapi.com/v1/cards/base1-4/prices" \
-H "X-Api-Key: $PTCG_API_KEY"Example response
200 OK · Cache-Control: private, max-age=300
{
"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"
},
{
"source": "CARDTRADER",
"variant": "LOW",
"basis": "ASKING",
"amount": 1250.64,
"currency": "EUR",
"locale": "en",
"condition": "NEAR_MINT",
"printing": null,
"grading": null,
"as_of": "2026-09-01",
"sample_n": 4,
"provenance": "CardTrader"
},
{
"source": "TCGPLAYER",
"variant": "LOW",
"basis": "GUIDE",
"amount": 510,
"currency": "USD",
"locale": "en",
"condition": null,
"printing": "HOLOFOIL",
"grading": null,
"as_of": "2026-07-30",
"sample_n": null,
"provenance": "TCGplayer"
},
{
"source": "EBAY",
"variant": "MEDIAN_GRADED",
"basis": "ASKING",
"amount": 400,
"currency": "USD",
"locale": "en",
"condition": null,
"printing": null,
"grading": {
"company": "PSA",
"score": "1"
},
"as_of": "2026-08-26",
"sample_n": 7,
"provenance": "eBay"
},
{
"source": "PRICECHARTING",
"variant": "MEDIAN_GRADED",
"basis": "GUIDE",
"amount": 20061.4,
"currency": "USD",
"locale": "en",
"condition": null,
"printing": null,
"grading": {
"company": "PSA",
"score": "10"
},
"as_of": "2026-08-24",
"sample_n": null,
"provenance": "PriceCharting"
}
]
},
"meta": {
"quotes": 5,
"delayed_hours": 0
}
}Errors
Every error body carries error.code, error.message and error.request_id. Switch on the code, never on the message.
| Status | Code | When |
|---|---|---|
| 401 | MISSING_API_KEY | No X-Api-Key header and no bearer token on a route that requires one. |
| 401 | INVALID_API_KEY | The key does not match any account. |
| 400 | INVALID_PARAMETER | A query parameter has the wrong type or an unsupported value — a non-integer limit, an unknown lang, an unknown region, a sort key that is not sortable. |
| 429 | QUOTA_EXCEEDED | Trial credits, monthly credits or the daily cap are exhausted, and details says which: daily_cap with retry_after_s is the trial daily cap, which resets at midnight UTC; renews: true with resets_at is the monthly quota of a paid plan; renews: false is the trial allowance, which never comes back. details.next_step carries the plan, prices and owner action, or email verification when it unlocks the full trial. |
| 403 | PLAN_REQUIRED | The feature is outside the plan (movers require Growth; photo recognition has 5 calls after Free email confirmation, then requires Growth), or the account subscription is canceled or suspended. Centering requires Growth or higher; Developer is excluded. After email confirmation the trial includes 5 measure and 5 confirm calls independently; grade and standards use trial credits without separate call counters. Before email confirmation, the message starts with “Confirm your email…”. Follow details.next_step when present. |
| 403 | TRIAL_EXPIRED | The trial has ended: the account is on the trial and its 30 days from signup are over, and this route costs credits. details.trial_expired_at is the date, details.trial_days the length, and details.next_step the owner handoff. Zero-cost routes — /v1/me, /v1/me/usage, the key routes and /v1/billing/checkout — still answer. |
The full taxonomy, with what to do about each code, is on the errors page.