Skip to content
· 10 min read

How to Get Bulk Card Price Data for Your App

Learn how to efficiently fetch thousands of card prices using TCG API's bulk endpoints. Build price databases, inventory tools, and collection trackers.

tutorialbulk dataapideveloper tools

If you’re building a price database, inventory management tool, or collection tracker, you need bulk data — not one card at a time. Fetching 50,000+ prices individually would take thousands of API calls and hours of time.

TCG API’s bulk endpoints solve this. Here’s how to efficiently fetch card data at scale.

The Problem with Single-Card Fetching

Say you want prices for every Pokemon card. With individual requests:

200+ sets × ~150 cards per set = 30,000+ API calls

At 100 requests/day on the free tier, that’s 300 days. Even on the Pro plan (10K/day), it’s 3 days of constant API calls. Not practical.

Bulk Endpoints to the Rescue

TCG API’s bulk endpoints let you fetch entire sets or games worth of data in a few requests. Available on Pro ($49.99/mo) and Business ($99.99/mo) plans.

Bulk Card Prices by Set

Fetch all card prices for an entire set in one request:

Terminal window
curl "https://api.tcgapi.dev/v1/sets/{set_slug}/cards/prices" \
-H "Authorization: Bearer YOUR_API_KEY"

Response:

{
"data": [
{
"card_id": 12345,
"name": "Charizard ex",
"prices": {
"Normal": {
"market_price": 12.45,
"low_price": 10.99,
"change_24h": 0.35,
"change_7d": -0.80
},
"Holofoil": {
"market_price": 45.99,
"low_price": 42.00,
"change_24h": 1.20,
"change_7d": 3.50
}
}
},
// ... hundreds more cards
],
"meta": {
"total": 198,
"set": "Obsidian Flames"
}
}

One request = every card in the set with per-printing prices.

Bulk Cards for a Full Game

Fetch paginated card data for an entire game:

Terminal window
# Page through all Pokemon cards
curl "https://api.tcgapi.dev/v1/games/pokemon/cards?per_page=100&page=1" \
-H "Authorization: Bearer YOUR_API_KEY"

With 100 cards per page, you can fetch all of Pokemon (~30K cards) in ~300 requests.

Efficient Data Pipeline Strategy

Here’s the approach we recommend for building a local price database:

Initial Load

const API_KEY = 'your-api-key';
const BASE = 'https://api.tcgapi.dev/v1';
async function fetchAllSets(game) {
let page = 1;
let allSets = [];
while (true) {
const res = await fetch(
`${BASE}/games/${game}/sets?per_page=100&page=${page}`,
{ headers: { 'Authorization': `Bearer ${API_KEY}` } }
);
const data = await res.json();
allSets.push(...data.data);
if (!data.meta.has_more) break;
page++;
}
return allSets;
}
async function fetchSetPrices(setSlug) {
const res = await fetch(
`${BASE}/sets/${setSlug}/cards/prices`,
{ headers: { 'Authorization': `Bearer ${API_KEY}` } }
);
return (await res.json()).data;
}
// Build the database
async function initialLoad(game) {
const sets = await fetchAllSets(game);
console.log(`Found ${sets.length} sets for ${game}`);
const allPrices = [];
for (const set of sets) {
const prices = await fetchSetPrices(set.slug);
allPrices.push(...prices);
console.log(` ${set.name}: ${prices.length} cards`);
// Respect rate limits
await new Promise(resolve => setTimeout(resolve, 200));
}
return allPrices;
}

Daily Updates with Price Movers

Once you have the initial data, use the price movers endpoint to get only changed prices:

async function dailyUpdate(game) {
// Get cards with price changes in the last 24h
const res = await fetch(
`${BASE}/games/${game}/prices/movers?period=24h&per_page=100`,
{ headers: { 'Authorization': `Bearer ${API_KEY}` } }
);
const movers = await res.json();
const updates = [
...(movers.data.gainers || []),
...(movers.data.losers || [])
];
console.log(`${updates.length} price changes today`);
// Update only changed cards in your database
for (const card of updates) {
await updateLocalDatabase(card.id, card);
}
}

This approach means your daily updates use minimal API calls — only fetching what actually changed.

Request Budget Planning

Here’s how many requests different use cases typically need:

Use CaseInitial LoadDaily UpdateRecommended Plan
Track 1 game (e.g., Pokemon)~300 requests~10-50Starter ($19.99/mo)
Track 3 games~800 requests~30-150Pro ($49.99/mo)
Track all 89+ games~5,000 requests~200-500Business ($99.99/mo)
Single card tracking1 per card1 per cardFree or Hobby

Best Practices

1. Cache Aggressively

Prices update at most daily (for top-tier games). No need to re-fetch more than once per day:

const CACHE_TTL = 24 * 60 * 60 * 1000; // 24 hours
function isCacheValid(lastFetched) {
return Date.now() - lastFetched < CACHE_TTL;
}

2. Respect Rate Limits

Check the x-ratelimit-remaining header in every response:

async function fetchWithRateLimit(url) {
const res = await fetch(url, {
headers: { 'Authorization': `Bearer ${API_KEY}` }
});
const remaining = parseInt(res.headers.get('x-ratelimit-remaining') || '0');
if (remaining < 10) {
console.warn(`Rate limit warning: ${remaining} requests remaining`);
}
return res.json();
}

3. Use Pagination Efficiently

Always use the maximum per_page value (100) to minimize total requests:

// Good: 300 requests for 30K cards
const url = `${BASE}/games/pokemon/cards?per_page=100&page=${page}`;
// Bad: 3,000 requests for the same data
const url = `${BASE}/games/pokemon/cards?per_page=10&page=${page}`;

4. Store Per-Printing Data

Don’t average Normal and Foil prices — they’re completely different markets:

// Good: store each printing separately
db.insert({
card_id: card.id,
printing: 'Normal',
market_price: card.prices.Normal.market_price,
low_price: card.prices.Normal.low_price
});
db.insert({
card_id: card.id,
printing: 'Holofoil',
market_price: card.prices.Holofoil.market_price,
low_price: card.prices.Holofoil.low_price
});

Common Patterns

Price Alert System

async function checkAlerts(watchlist) {
for (const item of watchlist) {
const res = await fetch(`${BASE}/cards/${item.cardId}`, {
headers: { 'Authorization': `Bearer ${API_KEY}` }
});
const card = await res.json();
const price = card.data.prices?.Normal?.market_price;
if (price && price <= item.targetPrice) {
notify(`${card.data.name} dropped to $${price} (target: $${item.targetPrice})`);
}
}
}

Collection Value Calculator

async function calculateCollectionValue(cardIds) {
let totalValue = 0;
// Batch into groups of 100 for bulk fetch
for (let i = 0; i < cardIds.length; i += 100) {
const batch = cardIds.slice(i, i + 100);
const ids = batch.join(',');
const res = await fetch(`${BASE}/cards/bulk?ids=${ids}`, {
headers: { 'Authorization': `Bearer ${API_KEY}` }
});
const data = await res.json();
for (const card of data.data) {
totalValue += card.prices?.Normal?.market_price || 0;
}
}
return totalValue;
}

Getting Started

  1. Sign up for free — start with 100 req/day
  2. Prototype with the free tier — build your data pipeline
  3. Upgrade when ready — Pro plan ($49.99/mo) unlocks bulk endpoints
  4. Check the API docs — full endpoint reference with examples

The bulk endpoints are the most cost-effective way to build a comprehensive card price database. One Pro subscription replaces what would take thousands of dollars in manual data collection.


Building something cool with bulk data? Share it on Discord — we love seeing what developers create.

Ready to get started?

Free tier includes 100 requests per day. No credit card required.