Sealed product price API
Booster box and sealed Pokémon product prices over an API
Booster boxes, cases, Elite Trainer Boxes, tins, blisters and collection boxes as first-class objects, each tied to its set, with names in several languages, an image and current prices.
- Kinds
- Booster box, case, ETB, tin, blister, collection and more
- Search
q,set,kind,lang, sorted by release, name or value- Prices
GET /v1/sealed/{id}/prices, same row format as cards- List cost
- 1 credit per 50 products requested, with or without
include=index
Sealed product is where a lot of the money in this hobby sits, and it is usually an afterthought in card APIs: a string in a notes field, if anything. Here a sealed product has an id, a kind, the set it belongs to, a pack count where that makes sense, an image, and its own price route.
GET /v1/sealed?set=evs&kind=BOOSTER_BOX answers "what does an Evolving Skies booster box cost" in one call. With include=index every row includes index_eur and last_price_at. The EUR figure is there where a catalogue price is known, and last_price_at says when a price was last recorded.
One call, and what comes back
Searching sealed products. The response is a real one from production: note kind, set_code and pack_count next to the price fields.
curl -s -G "https://api.pokemontcgapi.com/v1/sealed" \
--data-urlencode "q=evolving skies" \
--data-urlencode "kind=BOOSTER_BOX" \
--data-urlencode "limit=2" \
-H "X-Api-Key: $PTCG_API_KEY"const url = new URL("https://api.pokemontcgapi.com/v1/sealed");
url.searchParams.set("q", "evolving skies");
url.searchParams.set("kind", "BOOSTER_BOX");
url.searchParams.set("limit", "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();import os, httpx
res = httpx.get(
"https://api.pokemontcgapi.com/v1/sealed",
params={"q": "evolving skies", "kind": "BOOSTER_BOX", "limit": "2"},
headers={"X-Api-Key": os.environ["PTCG_API_KEY"]},
)
res.raise_for_status()
payload = res.json(){
"data": [
{
"id": "evolving-skies-6-booster-box-case",
"sku": "evolving-skies-6-booster-box-case",
"slug": "evolving-skies-6-booster-box-case",
"name": "Evolving Skies 6 Booster Box Case",
"kind": "BOOSTER_BOX",
"set_code": "evs",
"set_name": "Evolving Skies",
"image_url": "https://img.pokemontcgapi.com/media/boxes/evolving-skies-6-booster-box-case-normal.webp?e=1791763200&s=Zp2fL8qKw3xVn1tRc7bYdM",
"release_date": null,
"pack_count": 216,
"languages": [],
"index_eur": 15495,
"last_price_at": "2026-09-02T00:38:55.286Z",
"created_at": "2026-05-23T12:43:56.128Z",
"updated_at": "2026-09-02T15:56:54.473Z"
},
{
"id": "evolving-skies-booster-box",
"sku": "evolving-skies-booster-box",
"slug": "evolving-skies-booster-box",
"name": "Evolving Skies Booster Box",
"kind": "BOOSTER_BOX",
"set_code": "evs",
"set_name": "Evolving Skies",
"image_url": "https://img.pokemontcgapi.com/media/boxes/evolving-skies-booster-box-normal.webp?e=1791763200&s=Hq6dS1nXt9wBk4yPm2vLcJ",
"release_date": null,
"pack_count": 36,
"languages": [],
"index_eur": 1750,
"last_price_at": "2026-09-02T00:36:37.317Z",
"created_at": "2026-05-23T12:43:56.069Z",
"updated_at": "2026-09-02T15:56:54.473Z"
}
],
"meta": {
"limit": 2,
"count": 2,
"total_count": 3,
"has_more": true
},
"links": {
"next": "https://api.pokemontcgapi.com/v1/sealed?cursor=eyJrIjpbbnVsbF0sImlkIjoiZXZvbHZpbmctc2tpZXMtYm9vc3Rlci1ib3giLCJzIjoicmVsZWFzZV9kYXRlOmRlc2Msc2t1OmFzYyJ9&kind=BOOSTER_BOX&limit=2&q=evolving+skies"
}
}The product and its quotes
A product's current prices come from GET /v1/sealed/{id}/prices for 2 credits, in the same row format as cards: source, variant, basis, currency, date. A list costs 1 credit per 50 products requested, rounded up, with or without include=index. A single product includes the index fields, which can be null when no catalogue price is known.
Cards and sealed in one portfolio
Because a sealed product points at its set by set_code, the same set page in your app can show the cards, the products that contain them and what each is worth. The /v1/changes feed reports sealed products too, so a mirror stays current without re-reading the list.
What it is not
Read this before you build on it. Every line here is a support ticket somebody else already opened.
- Pokémon only
- The sealed catalogue is filtered to Pokémon TCG products. Other games are not served.
- Release dates are partial
release_dateis null on many products. Sort bynameor by value when the date matters to your UI, or fall back to the release date of the set.- Fewer sources than cards
- Not every price source covers sealed product. Read
GET /v1/sealed/{id}/pricesfor what exists rather than assuming the card sources apply.
Questions people ask before signing up
- Is there an API for Pokémon booster box prices?
- Yes. GET /v1/sealed lists sealed products, filterable by set and kind, and GET /v1/sealed/{id}/prices returns the current price rows for one product, each with its source, basis and date.
- Which sealed product types are covered?
- Booster boxes and cases, Elite Trainer Boxes, tins, blisters, collection boxes and similar retail products, each with a kind field you can filter on. The live count is on GET /v1/status. Coverage, measured live
- Can I track the value of a sealed collection?
- Yes. Ask for the list with include=index and store index_eur daily for each product that has a catalogue price; for the others it is null. last_price_at says when a price was last recorded. The call costs 1 credit per 50 products requested (the limit), rounded up, the same as the plain list. Use case: price tracker
- Is sealed product available on the trial?
- Yes, the catalogue and the current prices both. The trial key reads sealed lists and single products the same way a paid key does; what grows with the plan is the price history behind them. Free trial key