Resolve TCGPlayer IDs to Card Prices in Bulk (2026)
Turn a CSV of TCGPlayer product IDs into card data and prices in one request — single lookup, batch resolve up to 1,000 IDs, and name matching for rows where the ID is missing.
If you have ever imported a collection export, a store inventory, or a sales report, you know the shape of the problem: a spreadsheet with thousands of rows, each carrying a TCGPlayer product ID, and no prices attached.
The usual fix is a mapping table — your own copy of every product ID matched to a card, maintained forever, drifting out of date every time a new set drops. You do not need one.
The single lookup
If you have one TCGPlayer product ID and want the card plus its current prices:
curl "https://api.tcgapi.dev/v1/cards/tcgplayer/187172" \ -H "X-API-Key: YOUR_KEY"The response carries the card and a prices array with one row per printing:
{ "data": { "id": 12345, "name": "Full Power Broly, Resonant Evolution", "tcgplayer_id": 187172, "game_name": "Dragon Ball Super: Masters", "set_name": "Galactic Battle", "prices": [ { "printing": "Normal", "market_price": 0.25, "low_price": 0.15 } ] }}Note that prices is an array, not an object keyed by printing. A card with Normal and
Foil versions returns two rows, and you pick the one you want:
const res = await fetch('https://api.tcgapi.dev/v1/cards/tcgplayer/187172', { headers: { 'X-API-Key': process.env.TCGAPI_KEY }});const { data } = await res.json();
const normal = data.prices.find(p => p.printing === 'Normal');console.log(`${data.name}: $${normal?.market_price?.toFixed(2) ?? 'n/a'}`);The bulk lookup
One request per row does not scale past a few hundred cards. Batch them instead:
curl -X POST "https://api.tcgapi.dev/v1/bulk/resolve/tcgplayer" \ -H "X-API-Key: YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{"ids": [187172, 187171, 999999999]}'{ "data": { "resolved": [ { "tcgplayer_id": 187172, "card": { "id": 12345, "name": "Full Power Broly, Resonant Evolution" }, "prices": [{ "printing": "Normal", "market_price": 0.25 }] } ], "not_found": [999999999] }, "meta": { "total_requested": 3, "total_resolved": 2, "credits_consumed": 2 }}Two things worth designing around:
not_foundis a first-class part of the response. IDs that do not resolve come back in their own array rather than silently vanishing, so you can report them to the user instead of writing blank rows.- Batch size and cost scale with your tier. Starter resolves 100 IDs per request at 1 credit each; Pro does 500 at 0.5 credits; Business does 1,000 at 0.1. On Business that works out to roughly 500,000 resolutions a day.
Processing a CSV
const BATCH = 500; // Pro tier limit
async function resolveAll(ids) { const out = new Map(); for (let i = 0; i < ids.length; i += BATCH) { const res = await fetch('https://api.tcgapi.dev/v1/bulk/resolve/tcgplayer', { method: 'POST', headers: { 'X-API-Key': process.env.TCGAPI_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ ids: ids.slice(i, i + BATCH) }) }); const { data } = await res.json(); for (const row of data.resolved) out.set(row.tcgplayer_id, row); if (data.not_found.length) { console.warn(`${data.not_found.length} unresolved in batch ${i / BATCH}`); } } return out;}When the ID is missing
Real exports are messy. Rows scraped from marketplace listings or typed by hand often have a name and a set but no product ID. There is a second endpoint for exactly that case:
curl -X POST "https://api.tcgapi.dev/v1/bulk/resolve/name" \ -H "X-API-Key: YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{"names": ["Charizard ex", "Lightning Bolt"]}'Name matching returns a confidence score per result, because names are ambiguous — dozens of cards are called Charizard, and reprints share a name across sets. Treat a low score as a prompt to ask the user, not as a match.
If the source data uses collector numbers rather than names, /v1/search matches those
directly too — a query of OP01-060 finds the card without needing its name at all.
Why this beats a mapping table
A local mapping table has to be built, stored, and refreshed on every set release, and it goes stale silently — the failure mode is a card that resolves to last year’s printing, or not at all. Resolving against a live catalog moves that maintenance off your side, and new sets are already present when you query them.
Ready to try it? Get a free API key — 100 requests a day, no credit card. Bulk resolve starts on the Starter plan, and the full request and response reference lives in the bulk endpoints docs.
Ready to get started?
Free tier includes 100 requests per day. No credit card required.