Skip to content
· 8 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?q=${encodeURIComponent(query)}&game=${game}&per_page=5`;
const response = await fetch(url, {
headers: { 'X-API-Key': API_KEY }
});
const data = await response.json();
return data.data;
}
// Try it out
const results = await searchCards('charizard ex');
// Search returns a flat row per card — `printing` and `market_price` live on the card
results.forEach(card => {
console.log(`${card.name} (${card.set_name})`);
console.log(` ${card.printing}: $${card.market_price ?? 'N/A'}`);
});

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) {
// Card row and prices are two calls: /cards/{id} has no prices field.
const [cardRes, priceRes] = await Promise.all([
fetch(`${BASE_URL}/cards/${cardId}`, { headers: { 'X-API-Key': API_KEY } }),
fetch(`${BASE_URL}/cards/${cardId}/prices`, { headers: { 'X-API-Key': API_KEY } })
]);
const card = await cardRes.json();
const prices = (await priceRes.json()).data;
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,
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:

// top-movers returns a flat array — ask for gainers or losers via `direction`.
// `price_change` is the percentage move over the requested period.
async function fetchMovers(game, direction) {
const url = `${BASE_URL}/prices/top-movers?game=${game}&direction=${direction}&period=24h&limit=5`;
const response = await fetch(url, {
headers: { 'X-API-Key': API_KEY }
});
return (await response.json()).data;
}
async function checkPriceMovers(game = 'pokemon') {
console.log('\n📈 Biggest Pokemon Price Movers (24h)\n');
for (const [label, direction] of [['TOP GAINERS', 'up'], ['TOP LOSERS', 'down']]) {
const cards = await fetchMovers(game, direction);
if (!cards.length) continue;
console.log(`${label}:`);
cards.forEach(card => {
const pct = `${card.price_change >= 0 ? '+' : ''}${card.price_change.toFixed(1)}%`;
console.log(` ${card.name} — $${card.market_price.toFixed(2)} (${pct})`);
});
console.log('');
}
}

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) {
// /cards/{id}/prices returns the printing rows directly as `data`
const response = await fetch(`${BASE_URL}/cards/${card.id}/prices`, {
headers: { 'X-API-Key': API_KEY }
});
const newPrices = (await response.json()).data;
// Compare prices
for (const row of newPrices) {
const printing = row.printing;
const oldPrice = (card.prices || []).find(p => p.printing === printing)?.market_price;
const newPrice = row.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})`);
console.log(` ${card.printing}: $${card.market_price ?? 'N/A'}`);
});
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 54 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.