
Use Pokemon Price Tracker API with AI Coding Tools (Claude Code, Cursor, Cline)
Learn how to integrate the Pokemon Price Tracker API into your projects using AI coding assistants. Works with Claude Code, Cursor, Cline, GitHub Copilot, and more.

Team

PokemonPriceTracker Team

Imagine your Pokemon collecting Discord community being able to:
/price Charizard VMAX and instantly get current market prices/compare Umbreon Alt Art, Espeon Alt Art to see side-by-side analysis/grading Pikachu VMAX for PSA ROI calculations/trending to see hot cards this weekNo more leaving Discord to check prices. No more outdated information. Just instant, accurate data powered by a custom bot.
A full-featured Discord bot with these commands:
/price <card> - Get current card prices with embed/compare <card1>, <card2> - Compare multiple cards/grading <card> - PSA grading ROI analysis/trending - See market movers/history <card> - Price history chart/set <name> - View set information/search <query> - Advanced card searchIn the "Bot" section, enable these intents:
Under "OAuth2" β "URL Generator":
bot, applications.commandsCopy the generated URL and invite your bot to your test server.
mkdir pokemon-price-bot
cd pokemon-price-bot
npm init -y
# Install dependencies
npm install discord.js dotenv axios canvas chart.js chartjs-node-canvas
pokemon-price-bot/
βββ .env
βββ .gitignore
βββ package.json
βββ index.js
βββ commands/
β βββ price.js
β βββ compare.js
β βββ grading.js
β βββ trending.js
β βββ search.js
βββ utils/
β βββ api.js
β βββ embeds.js
β βββ charts.js
βββ deploy-commands.js
DISCORD_TOKEN=your_discord_bot_token
CLIENT_ID=your_discord_client_id
GUILD_ID=your_test_server_id
POKEMON_API_KEY=your_pokemonpricetracker_api_key
Create utils/api.js:
const axios = require('axios');
const API_BASE = 'https://www.pokemonpricetracker.com/api/v2';
class PokemonAPI {
constructor(apiKey) {
this.axios = axios.create({
baseURL: API_BASE,
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
}
});
}
async searchCards(query, options = {}) {
const { data } = await this.axios.get('/cards', {
params: {
search: query,
limit: options.limit || 10,
...options
}
});
return data;
}
async parseTitle(title) {
const { data } = await this.axios.post('/parse-title', {
title
});
return data;
}
async getCardDetails(tcgPlayerId) {
const { data } = await this.axios.get('/cards', {
params: { tcgPlayerId, limit: 1 }
});
return data;
}
async getSets(limit = 20) {
const { data } = await this.axios.get('/sets', {
params: { limit }
});
return data;
}
// NOTE: there is no public market-movers endpoint. Sort by price and
// compare against your own stored copy to find movers yourself.
async getTopCards(limit = 20) {
const { data } = await this.axios.get('/cards', {
params: { limit, sortBy: 'price', sortOrder: 'desc' }
});
return data;
}
}
module.exports = PokemonAPI;
Create utils/embeds.js for beautiful Discord embeds:
const { EmbedBuilder } = require('discord.js');
function createPriceEmbed(card) {
const embed = new EmbedBuilder()
.setColor(0x3B82F6)
.setTitle(card.name)
.setDescription(`**${card.setName}** β’ ${card.rarity}`)
.setThumbnail(card.imageUrl || card.image?.url)
.addFields(
{
name: 'π° Market Price',
value: `$${card.prices.market.toFixed(2)}`,
inline: true
},
{
name: 'π 30d Change',
value: card.priceChange30d ? `${card.priceChange30d > 0 ? '+' : ''}${card.priceChange30d.toFixed(1)}%` : 'N/A',
inline: true
},
{
name: 'π’ TCGPlayer ID',
value: card.tcgPlayerId,
inline: true
}
)
.setFooter({
text: 'Data from PokemonPriceTracker.com',
iconURL: 'https://www.pokemonpricetracker.com/icon.svg'
})
.setTimestamp();
// Add pricing breakdown if available
if (card.prices) {
let priceFields = [];
if (card.prices.low) priceFields.push(`Low: $${card.prices.low.toFixed(2)}`);
if (card.prices.mid) priceFields.push(`Mid: $${card.prices.mid.toFixed(2)}`);
if (card.prices.high) priceFields.push(`High: $${card.prices.high.toFixed(2)}`);
if (priceFields.length > 0) {
embed.addFields({
name: 'π΅ Price Range',
value: priceFields.join(' β’ '),
inline: false
});
}
}
return embed;
}
function createGradingEmbed(card, gradingData) {
const rawValue = card.prices.market;
const psa9Value = gradingData.psa9Average || rawValue * 1.15;
const psa10Value = gradingData.psa10Average || rawValue * 1.75;
const gradingCost = 30; // Average PSA cost
const psa9Profit = psa9Value - rawValue - gradingCost;
const psa10Profit = psa10Value - rawValue - gradingCost;
const embed = new EmbedBuilder()
.setColor(0x10B981)
.setTitle(`π― PSA Grading Analysis: ${card.name}`)
.setDescription(`**${card.setName}**`)
.setThumbnail(card.imageUrl || card.image?.url)
.addFields(
{
name: 'π¦ Raw Card Value',
value: `$${rawValue.toFixed(2)}`,
inline: true
},
{
name: 'π₯ PSA 9 Average',
value: `$${psa9Value.toFixed(2)}`,
inline: true
},
{
name: 'π₯ PSA 10 Average',
value: `$${psa10Value.toFixed(2)}`,
inline: true
},
{
name: 'π° PSA 9 ROI',
value: psa9Profit > 0
? `β
+$${psa9Profit.toFixed(2)} (${((psa9Profit/rawValue)*100).toFixed(1)}% profit)`
: `β -$${Math.abs(psa9Profit).toFixed(2)} (loss)`,
inline: false
},
{
name: 'π PSA 10 ROI',
value: psa10Profit > 0
? `β
+$${psa10Profit.toFixed(2)} (${((psa10Profit/rawValue)*100).toFixed(1)}% profit)`
: `β -$${Math.abs(psa10Profit).toFixed(2)} (loss)`,
inline: false
}
);
// Recommendation
let recommendation = '';
if (psa10Profit > 50) {
recommendation = 'π’ **Strong Grade Candidate** - Excellent ROI potential if card is gem mint.';
} else if (psa9Profit > 20) {
recommendation = 'π‘ **Moderate Grade Candidate** - Worthwhile if confident in PSA 9+.';
} else {
recommendation = 'π΄ **Not Recommended** - Better to sell raw unless pristine condition.';
}
embed.addFields({
name: 'π Recommendation',
value: recommendation,
inline: false
});
embed.setFooter({
text: 'Grading cost estimate: $30 β’ Data from PokemonPriceTracker.com',
iconURL: 'https://www.pokemonpricetracker.com/icon.svg'
});
return embed;
}
function createCompareEmbed(cards) {
const embed = new EmbedBuilder()
.setColor(0x8B5CF6)
.setTitle(`π Card Comparison (${cards.length} cards)`);
cards.forEach((card, index) => {
embed.addFields({
name: `${index + 1}. ${card.name}`,
value: [
`**Set**: ${card.setName}`,
`**Price**: $${card.prices.market.toFixed(2)}`,
`**Rarity**: ${card.rarity}`,
`**30d**: ${card.priceChange30d ? (card.priceChange30d > 0 ? '+' : '') + card.priceChange30d.toFixed(1) + '%' : 'N/A'}`
].join('\n'),
inline: true
});
});
embed.setFooter({
text: 'Data from PokemonPriceTracker.com',
iconURL: 'https://www.pokemonpricetracker.com/icon.svg'
});
return embed;
}
module.exports = {
createPriceEmbed,
createGradingEmbed,
createCompareEmbed
};
Create commands/price.js:
const { SlashCommandBuilder } = require('discord.js');
const { createPriceEmbed } = require('../utils/embeds');
module.exports = {
data: new SlashCommandBuilder()
.setName('price')
.setDescription('Get the current price of a Pokemon card')
.addStringOption(option =>
option.setName('card')
.setDescription('Card name (e.g., "Charizard VMAX Darkness Ablaze")')
.setRequired(true)
),
async execute(interaction, api) {
await interaction.deferReply();
try {
const cardQuery = interaction.options.getString('card');
// Use parse-title for fuzzy matching
const parseResult = await api.parseTitle(cardQuery);
if (!parseResult.matches || parseResult.matches.length === 0) {
return interaction.editReply({
content: `β Could not find a card matching "${cardQuery}". Try being more specific!`,
ephemeral: true
});
}
const topMatch = parseResult.matches[0];
const card = topMatch.card;
const confidence = topMatch.confidence;
const embed = createPriceEmbed(card);
// Add confidence indicator if not perfect match
if (confidence < 1.0) {
embed.setDescription(
`${embed.data.description}\n\n*Match confidence: ${(confidence * 100).toFixed(0)}%*`
);
}
await interaction.editReply({ embeds: [embed] });
} catch (error) {
console.error('Error in price command:', error);
await interaction.editReply({
content: 'β An error occurred while fetching card data. Please try again.',
ephemeral: true
});
}
}
};
Create commands/grading.js:
const { SlashCommandBuilder } = require('discord.js');
const { createGradingEmbed } = require('../utils/embeds');
module.exports = {
data: new SlashCommandBuilder()
.setName('grading')
.setDescription('Analyze PSA grading ROI for a card')
.addStringOption(option =>
option.setName('card')
.setDescription('Card name')
.setRequired(true)
),
async execute(interaction, api) {
await interaction.deferReply();
try {
const cardQuery = interaction.options.getString('card');
const parseResult = await api.parseTitle(cardQuery);
if (!parseResult.matches || parseResult.matches.length === 0) {
return interaction.editReply({
content: `β Could not find "${cardQuery}"`,
ephemeral: true
});
}
const card = parseResult.matches[0].card;
// Fetch detailed data with PSA info
const details = await api.getCardDetails(card.tcgPlayerId);
const gradingData = {
psa9Average: details.ebayData?.psa9Average,
psa10Average: details.ebayData?.psa10Average,
population: details.psaPopulation
};
const embed = createGradingEmbed(card, gradingData);
await interaction.editReply({ embeds: [embed] });
} catch (error) {
console.error('Error in grading command:', error);
await interaction.editReply({
content: 'β Error analyzing grading ROI',
ephemeral: true
});
}
}
};
Create commands/compare.js:
const { SlashCommandBuilder } = require('discord.js');
const { createCompareEmbed } = require('../utils/embeds');
module.exports = {
data: new SlashCommandBuilder()
.setName('compare')
.setDescription('Compare prices of multiple cards')
.addStringOption(option =>
option.setName('cards')
.setDescription('Card names separated by commas (e.g., "Charizard VMAX, Pikachu VMAX")')
.setRequired(true)
),
async execute(interaction, api) {
await interaction.deferReply();
try {
const cardsInput = interaction.options.getString('cards');
const cardQueries = cardsInput.split(',').map(q => q.trim());
if (cardQueries.length < 2) {
return interaction.editReply({
content: 'β Please provide at least 2 cards separated by commas',
ephemeral: true
});
}
if (cardQueries.length > 5) {
return interaction.editReply({
content: 'β Maximum 5 cards for comparison',
ephemeral: true
});
}
const cards = [];
for (const query of cardQueries) {
const parseResult = await api.parseTitle(query);
if (parseResult.matches && parseResult.matches.length > 0) {
cards.push(parseResult.matches[0].card);
}
}
if (cards.length === 0) {
return interaction.editReply({
content: 'β Could not find any matching cards',
ephemeral: true
});
}
const embed = createCompareEmbed(cards);
await interaction.editReply({ embeds: [embed] });
} catch (error) {
console.error('Error in compare command:', error);
await interaction.editReply({
content: 'β Error comparing cards',
ephemeral: true
});
}
}
};
Create index.js:
require('dotenv').config();
const { Client, Collection, GatewayIntentBits } = require('discord.js');
const fs = require('fs');
const path = require('path');
const PokemonAPI = require('./utils/api');
// Initialize Discord client
const client = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMessages,
GatewayIntentBits.MessageContent
]
});
// Initialize Pokemon API
const pokemonAPI = new PokemonAPI(process.env.POKEMON_API_KEY);
// Load commands
client.commands = new Collection();
const commandsPath = path.join(__dirname, 'commands');
const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith('.js'));
for (const file of commandFiles) {
const filePath = path.join(commandsPath, file);
const command = require(filePath);
client.commands.set(command.data.name, command);
}
// Bot ready event
client.once('ready', () => {
console.log(`β
Bot is online as ${client.user.tag}!`);
client.user.setActivity('/price | Pokemon Card Prices', { type: 'WATCHING' });
});
// Handle slash commands
client.on('interactionCreate', async interaction => {
if (!interaction.isChatInputCommand()) return;
const command = client.commands.get(interaction.commandName);
if (!command) return;
try {
await command.execute(interaction, pokemonAPI);
} catch (error) {
console.error(`Error executing ${interaction.commandName}:`, error);
await interaction.reply({
content: 'β There was an error executing this command!',
ephemeral: true
});
}
});
// Error handling
client.on('error', error => {
console.error('Discord client error:', error);
});
process.on('unhandledRejection', error => {
console.error('Unhandled promise rejection:', error);
});
// Login
client.login(process.env.DISCORD_TOKEN);
Create deploy-commands.js:
require('dotenv').config();
const { REST, Routes } = require('discord.js');
const fs = require('fs');
const commands = [];
const commandFiles = fs.readdirSync('./commands').filter(file => file.endsWith('.js'));
for (const file of commandFiles) {
const command = require(`./commands/${file}`);
commands.push(command.data.toJSON());
}
const rest = new REST({ version: '10' }).setToken(process.env.DISCORD_TOKEN);
(async () => {
try {
console.log(`Started refreshing ${commands.length} application (/) commands.`);
const data = await rest.put(
Routes.applicationGuildCommands(process.env.CLIENT_ID, process.env.GUILD_ID),
{ body: commands },
);
console.log(`β
Successfully reloaded ${data.length} application (/) commands.`);
} catch (error) {
console.error(error);
}
})();
Run it:
node deploy-commands.js
node index.js
Test in Discord:
/price Charizard VMAX/grading Umbreon VMAX Alt Art/compare Pikachu VMAX, Raichu VMAX# Install Heroku CLI
npm install -g heroku
# Login and create app
heroku login
heroku create pokemon-price-bot
# Set environment variables
heroku config:set DISCORD_TOKEN=your_token
heroku config:set POKEMON_API_KEY=your_api_key
heroku config:set CLIENT_ID=your_client_id
# Create Procfile
echo "worker: node index.js" > Procfile
# Deploy
git init
git add .
git commit -m "Initial commit"
git push heroku main
# Scale worker
heroku ps:scale worker=1
# On your VPS
git clone your-repo
cd pokemon-price-bot
npm install
# Use PM2 for process management
npm install -g pm2
pm2 start index.js --name pokemon-bot
pm2 save
pm2 startup
Add cooldowns to prevent spam:
const cooldowns = new Collection();
// In command execution
const { cooldowns } = interaction.client;
if (!cooldowns.has(command.data.name)) {
cooldowns.set(command.data.name, new Collection());
}
const now = Date.now();
const timestamps = cooldowns.get(command.data.name);
const cooldownAmount = (command.cooldown || 3) * 1000;
if (timestamps.has(interaction.user.id)) {
const expirationTime = timestamps.get(interaction.user.id) + cooldownAmount;
if (now < expirationTime) {
const timeLeft = (expirationTime - now) / 1000;
return interaction.reply({
content: `Please wait ${timeLeft.toFixed(1)} more seconds before using \`${command.data.name}\` again.`,
ephemeral: true
});
}
}
timestamps.set(interaction.user.id, now);
setTimeout(() => timestamps.delete(interaction.user.id), cooldownAmount);
Add buttons for pagination:
const { ActionRowBuilder, ButtonBuilder, ButtonStyle } = require('discord.js');
const row = new ActionRowBuilder()
.addComponents(
new ButtonBuilder()
.setCustomId('previous')
.setLabel('Previous')
.setStyle(ButtonStyle.Primary),
new ButtonBuilder()
.setCustomId('next')
.setLabel('Next')
.setStyle(ButtonStyle.Primary)
);
await interaction.editReply({
embeds: [embed],
components: [row]
});
Bot doesn't respond to commands:
deploy-commands.js)API errors:
Deployment issues:
Free Setup:
Production Setup:
Enhance your bot with:
Ready to build your bot? Get your free API key:
Need help? Join our Discord community or check the full source code on GitHub.

Discord Bot Specialists

Learn how to integrate the Pokemon Price Tracker API into your projects using AI coding assistants. Works with Claude Code, Cursor, Cline, GitHub Copilot, and more.

Team

Want to display up-to-date PokΓ©mon card prices in your application? This technical guide shows you how to use our price API to power your project.

Team

Complete Pokemon card API guide for developers. Learn about available APIs, integration methods, pricing data access, and how to build Pokemon card applications.
PokemonPriceTracker Team
Subscribe to our newsletter for the latest Pokemon card market trends, investment opportunities, and exclusive insights delivered straight to your inbox.