# Current prices, in batch

> The current prices of up to 50 cards in one request, for a list, a collection or a deck.

- **Endpoint**: `GET /v1/prices/current`
- **Cost**: 4 per 25 ids: a full batch of 50 costs 8
- **Minimum plan**: free
- **Cache-Control**: `private, max-age=300`

Source: https://pokemontcgapi.com/docs/api/prices/batch

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](https://pokemontcgapi.com/docs/quickstart) and the [pricing](https://pokemontcgapi.com/pricing).

One entry per card that has prices, in the order you asked, with `card_id` echoing the id you sent. `requested` and `found` tell you at a glance how many of your ids had no observations — those are simply absent from `data`, not returned as nulls.

Fifty and not the 100 of `/v1/cards/batch`, on purpose: a card carries a dozen series at the median and over a hundred at the worst case measured, so a hundred graded cards would be thousands of rows in one response.

## Query parameters

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `ids` **required** | string | — | Comma-separated card ids or alternate ids, 1 to 50. More than 50 is `LIMIT_EXCEEDED`; split the list instead. |
| `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**

```bash
curl -s -G "https://api.pokemontcgapi.com/v1/prices/current" \
  --data-urlencode "ids=bs-4,bs-2" \
  -H "X-Api-Key: $PTCG_API_KEY"
```

**TypeScript**

`request.ts`

```ts
const url = new URL("https://api.pokemontcgapi.com/v1/prices/current");
url.searchParams.set("ids", "bs-4,bs-2");

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();
```

**Python**

`request.py`

```python
import os, httpx

res = httpx.get(
    "https://api.pokemontcgapi.com/v1/prices/current",
    params={"ids": "bs-4,bs-2"},
    headers={"X-Api-Key": os.environ["PTCG_API_KEY"]},
)
res.raise_for_status()
payload = res.json()
```

## Example response

`200 OK` · `Cache-Control: private, max-age=300`

`response.json`

```json
{
  "data": [
    {
      "card_id": "bs-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": 368.67,
          "currency": "EUR",
          "locale": "en",
          "condition": "GOOD",
          "printing": null,
          "grading": null,
          "as_of": "2026-09-01",
          "sample_n": 14,
          "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"
        }
      ]
    },
    {
      "card_id": "bs-2",
      "index": {
        "eur": 181.35,
        "as_of": "2026-09-02",
        "sample_n": 16,
        "by_locale": [
          {
            "locale": "en",
            "printing": null,
            "eur": 181.35,
            "as_of": "2026-09-02",
            "sample_n": 16
          },
          {
            "locale": "fr",
            "printing": null,
            "eur": 350,
            "as_of": "2026-09-02",
            "sample_n": 1
          },
          {
            "locale": "de",
            "printing": null,
            "eur": 95.13,
            "as_of": "2026-09-02",
            "sample_n": 4
          },
          {
            "locale": "es",
            "printing": null,
            "eur": 240.64,
            "as_of": "2026-09-02",
            "sample_n": 5
          },
          {
            "locale": "it",
            "printing": null,
            "eur": 130.63,
            "as_of": "2026-09-02",
            "sample_n": 11
          }
        ]
      },
      "quotes": [
        {
          "source": "CARDMARKET",
          "variant": "LOW",
          "basis": "ASKING",
          "amount": 200,
          "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": 336.39,
          "currency": "EUR",
          "locale": "en",
          "condition": "NEAR_MINT",
          "printing": null,
          "grading": null,
          "as_of": "2026-09-01",
          "sample_n": 5,
          "provenance": "CardTrader"
        },
        {
          "source": "TCGPLAYER",
          "variant": "LOW",
          "basis": "GUIDE",
          "amount": 125,
          "currency": "USD",
          "locale": "en",
          "condition": null,
          "printing": "HOLOFOIL",
          "grading": null,
          "as_of": "2026-07-30",
          "sample_n": null,
          "provenance": "TCGplayer"
        }
      ]
    }
  ],
  "requested": 2,
  "found": 2
}
```

## 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. |
| 400 | `LIMIT_EXCEEDED` | `limit` is above 250, or a batch request carries more than 100 ids. |
| 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](https://pokemontcgapi.com/docs/errors) page.
