Skip to content
· 10 min read

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.

tutorialpokemonjavascriptprice tracking

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

Step 1: Get Your API Key

  1. Create an account at tcgapi.dev/signup
  2. Log in to your dashboard
  3. Create a new API key
  4. 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 out
const 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:

Terminal window
node tracker.js

You should see something like:

Charizard ex (Obsidian Flames)
Normal: $12.45
Foil: $45.99
Charizard ex (Paldea Evolved)
Normal: $8.75
Foil: $32.50

Step 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:

Terminal window
# Search for cards
node tracker.js search "pikachu vmax"
# Add a card to your watchlist by ID
node tracker.js add 12345
# Update all watchlist prices
node tracker.js update
# Check today's biggest movers
node tracker.js movers

Next Steps

From here you could:

  • Add a cron job to run update daily 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 game parameter (try magic, yugioh, one-piece-card-game, or any of the 89+ supported games)

Full API Reference

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.