Build a Pokemon Price Tracker in 15 Minutes
Step-by-step tutorial to build a Pokemon card price tracker using TCG API and JavaScript. Track prices, monitor changes, and find deals.
Want to track Pokemon card prices programmatically? In this tutorial, you’ll build a price tracker that monitors your favorite cards and alerts you to price changes — all in about 15 minutes.
We’ll use TCG API for the pricing data and plain JavaScript (Node.js). No frameworks, no build tools, no complexity.
What You’ll Build
A Node.js script that:
- Searches for Pokemon cards by name
- Fetches current market prices (Normal and Foil)
- Tracks 24h, 7d, and 30d price changes
- Saves a watchlist to a JSON file
- Flags cards with significant price movements
Prerequisites
- Node.js 18+ installed
- A free TCG API key (sign up here)
Step 1: Get Your API Key
- Create an account at tcgapi.dev/signup
- Log in to your dashboard
- Create a new API key
- Copy the key — you’ll need it in a moment
The free tier gives you 100 requests per day, which is plenty for tracking a personal collection.
Step 2: Search for Cards
Let’s start by searching for a card. Create a file called tracker.js:
const API_KEY = 'your-api-key-here';const BASE_URL = 'https://api.tcgapi.dev/v1';
async function searchCards(query, game = 'pokemon') { const url = `${BASE_URL}/search/cards?q=${encodeURIComponent(query)}&game=${game}&per_page=5`; const response = await fetch(url, { headers: { 'Authorization': `Bearer ${API_KEY}` } }); const data = await response.json(); return data.data;}
// Try it outconst results = await searchCards('charizard ex');results.forEach(card => { console.log(`${card.name} (${card.set_name})`); if (card.prices?.Normal) { console.log(` Normal: $${card.prices.Normal.market_price}`); } if (card.prices?.Holofoil) { console.log(` Foil: $${card.prices.Holofoil.market_price}`); }});Run it:
node tracker.jsYou should see something like:
Charizard ex (Obsidian Flames) Normal: $12.45 Foil: $45.99Charizard ex (Paldea Evolved) Normal: $8.75 Foil: $32.50Step 3: Build the Watchlist
Now let’s add a watchlist system that tracks cards you care about:
const fs = require('fs');
const WATCHLIST_FILE = './watchlist.json';
function loadWatchlist() { try { return JSON.parse(fs.readFileSync(WATCHLIST_FILE, 'utf8')); } catch { return { cards: [], lastUpdated: null }; }}
function saveWatchlist(watchlist) { watchlist.lastUpdated = new Date().toISOString(); fs.writeFileSync(WATCHLIST_FILE, JSON.stringify(watchlist, null, 2));}
async function addToWatchlist(cardId) { const response = await fetch(`${BASE_URL}/cards/${cardId}`, { headers: { 'Authorization': `Bearer ${API_KEY}` } }); const card = await response.json();
const watchlist = loadWatchlist(); const existing = watchlist.cards.findIndex(c => c.id === cardId);
const entry = { id: card.data.id, name: card.data.name, set: card.data.set_name, prices: card.data.prices, addedAt: new Date().toISOString() };
if (existing >= 0) { watchlist.cards[existing] = entry; } else { watchlist.cards.push(entry); }
saveWatchlist(watchlist); console.log(`Added: ${entry.name} (${entry.set})`);}Step 4: Price Change Detection
The killer feature — find cards that are moving:
async function checkPriceMovers(game = 'pokemon') { const url = `${BASE_URL}/games/${game}/prices/movers?period=24h&per_page=10`; const response = await fetch(url, { headers: { 'Authorization': `Bearer ${API_KEY}` } }); const data = await response.json();
console.log('\n📈 Biggest Pokemon Price Movers (24h)\n');
const gainers = data.data?.gainers || []; const losers = data.data?.losers || [];
if (gainers.length > 0) { console.log('TOP GAINERS:'); gainers.slice(0, 5).forEach(card => { const change = card.price_change > 0 ? `+$${card.price_change.toFixed(2)}` : `$${card.price_change.toFixed(2)}`; const pct = card.price_change_pct > 0 ? `+${card.price_change_pct.toFixed(1)}%` : `${card.price_change_pct.toFixed(1)}%`; console.log(` ${card.name} — $${card.market_price.toFixed(2)} (${change}, ${pct})`); }); }
if (losers.length > 0) { console.log('\nTOP LOSERS:'); losers.slice(0, 5).forEach(card => { const change = `$${card.price_change.toFixed(2)}`; const pct = `${card.price_change_pct.toFixed(1)}%`; console.log(` ${card.name} — $${card.market_price.toFixed(2)} (${change}, ${pct})`); }); }}Step 5: Update Watchlist Prices
Refresh prices for all tracked cards:
async function updateWatchlist() { const watchlist = loadWatchlist();
console.log(`\nUpdating ${watchlist.cards.length} tracked cards...\n`);
for (const card of watchlist.cards) { const response = await fetch(`${BASE_URL}/cards/${card.id}`, { headers: { 'Authorization': `Bearer ${API_KEY}` } }); const data = await response.json(); const newPrices = data.data.prices;
// Compare prices for (const printing of Object.keys(newPrices)) { const oldPrice = card.prices?.[printing]?.market_price; const newPrice = newPrices[printing]?.market_price;
if (oldPrice && newPrice) { const diff = newPrice - oldPrice; const pct = ((diff / oldPrice) * 100).toFixed(1);
if (Math.abs(diff) > 0.50) { const arrow = diff > 0 ? '📈' : '📉'; console.log(`${arrow} ${card.name} (${printing}): $${oldPrice.toFixed(2)} → $${newPrice.toFixed(2)} (${pct}%)`); } } }
card.prices = newPrices; }
saveWatchlist(watchlist); console.log('\nWatchlist updated!');}Step 6: Put It All Together
Add a simple CLI to your script:
const command = process.argv[2];
switch (command) { case 'search': const query = process.argv.slice(3).join(' '); const results = await searchCards(query); results.forEach(card => { console.log(`[${card.id}] ${card.name} (${card.set_name})`); Object.entries(card.prices || {}).forEach(([printing, prices]) => { console.log(` ${printing}: $${prices.market_price} (${prices.change_24h >= 0 ? '+' : ''}${prices.change_24h?.toFixed(2) || '0.00'} 24h)`); }); }); break;
case 'add': await addToWatchlist(process.argv[3]); break;
case 'update': await updateWatchlist(); break;
case 'movers': await checkPriceMovers(); break;
default: console.log('Usage:'); console.log(' node tracker.js search <card name>'); console.log(' node tracker.js add <card-id>'); console.log(' node tracker.js update'); console.log(' node tracker.js movers');}Now you can:
# Search for cardsnode tracker.js search "pikachu vmax"
# Add a card to your watchlist by IDnode tracker.js add 12345
# Update all watchlist pricesnode tracker.js update
# Check today's biggest moversnode tracker.js moversNext Steps
From here you could:
- Add a cron job to run
updatedaily and log price changes - Send Discord notifications when a tracked card drops below a target price
- Build a web dashboard with a simple HTML page that reads the watchlist JSON
- Track sealed products (booster boxes, ETBs) — they use the same API endpoints
- Expand to other games — just change the
gameparameter (trymagic,yugioh,one-piece-card-game, or any of the 89+ supported games)
Full API Reference
- Search Cards — Full-text card search with filters
- Card Data — Individual card details and prices
- Price Movers — Top gainers and losers
- Games List — All supported games
- Rate Limits — Stay within your daily quota
The free tier (100 req/day) is enough to track ~50 cards with daily updates. Need more? Check out paid plans starting at $9.99/mo.
Questions? Join us on Discord — we’re happy to help debug your tracker.
Ready to get started?
Free tier includes 100 requests per day. No credit card required.