Back to Blog

Automate Pokemon Card Price Tracking with Python - Complete Developer Guide

PokemonPriceTracker Team

PokemonPriceTracker Team

8 min read
Automate Pokemon Card Price Tracking with Python - Complete Developer Guide

Automate Pokemon Card Price Tracking with Python

Stop Manually Checking Prices - Automate Everything

Imagine waking up to an email report showing which of your Pokemon cards increased in value overnight, complete with charts, trends, and investment recommendations - all generated automatically.

This guide teaches you to build professional Python automation for:

  • ✅ Automated daily price updates
  • ✅ Portfolio valuation tracking
  • ✅ Price alert notifications
  • ✅ Historical trend analysis
  • ✅ CSV/Excel export
  • ✅ Price charts with matplotlib
  • ✅ Scheduled cron jobs

Prerequisites

  • Python 3.8+ (download)
  • PokemonPriceTracker API key (get free)
  • Code editor (VS Code, PyCharm)

Quick Start

# Create virtual environment
python -m venv pokemon-tracker
source pokemon-tracker/bin/activate  # Windows: pokemon-tracker\Scripts\activate

# Install dependencies
pip install requests pandas matplotlib python-dotenv schedule openpyxl

Step 1: API Client Wrapper

Create api_client.py:

import requests
from typing import Dict, List
import os
from dotenv import load_dotenv

load_dotenv()

class PokemonPriceAPI:
    """Wrapper for PokemonPriceTracker API"""

    BASE_URL = "https://www.pokemonpricetracker.com/api/v2"

    def __init__(self, api_key: str = None):
        self.api_key = api_key or os.getenv('POKEMON_API_KEY')
        if not self.api_key:
            raise ValueError("API key is required")

        self.session = requests.Session()
        self.session.headers.update({
            'Authorization': f'Bearer {self.api_key}',
            'Content-Type': 'application/json'
        })

    def search_cards(self, query: str = "", **params) -> Dict:
        """Search for Pokemon cards"""
        params['search'] = query
        response = self.session.get(f"{self.BASE_URL}/cards", params=params)
        response.raise_for_status()
        return response.json()

    def get_card(self, tcg_player_id: str) -> Dict:
        """Get detailed card information"""
        response = self.session.get(f"{self.BASE_URL}/cards/{tcg_player_id}")
        response.raise_for_status()
        return response.json()

    def parse_title(self, title: str) -> Dict:
        """Fuzzy match card description"""
        response = self.session.post(
            f"{self.BASE_URL}/parse-title",
            json={'title': title}
        )
        response.raise_for_status()
        return response.json()

# Test it
if __name__ == "__main__":
    api = PokemonPriceAPI()
    results = api.search_cards("Charizard VMAX", limit=5)
    for card in results['data']:
        print(f"{card['name']} - {card['setName']}: ${card['prices']['market']}")

Step 2: Portfolio Tracker

Create portfolio_tracker.py:

import pandas as pd
from api_client import PokemonPriceAPI
from datetime import datetime
import os

class PortfolioTracker:
    """Track Pokemon card collection value"""

    def __init__(self, api: PokemonPriceAPI, collection_file: str = "my_collection.csv"):
        self.api = api
        self.collection_file = collection_file
        self.df = None

    def load_collection(self) -> pd.DataFrame:
        """Load collection from CSV"""
        if not os.path.exists(self.collection_file):
            raise FileNotFoundError(f"Collection file not found: {self.collection_file}")

        self.df = pd.read_csv(self.collection_file)
        self.df['Purchase Date'] = pd.to_datetime(self.df['Purchase Date'])
        return self.df

    def fetch_current_prices(self):
        """Fetch current market prices for all cards"""
        if self.df is None:
            self.load_collection()

        current_prices = []
        print("Fetching prices from API...")

        for idx, row in self.df.iterrows():
            try:
                search_query = f"{row['Card Name']} {row['Set']}"
                result = self.api.parse_title(search_query)

                if result['matches'] and len(result['matches']) > 0:
                    card = result['matches'][0]['card']
                    current_price = card['prices']['market']
                    print(f"✓ {row['Card Name']}: ${current_price:.2f}")
                else:
                    current_price = 0
                    print(f"✗ {row['Card Name']}: NOT FOUND")

                current_prices.append(current_price)
            except Exception as e:
                print(f"✗ Error fetching {row['Card Name']}: {e}")
                current_prices.append(0)

        self.df['Current Price'] = current_prices
        return self.df

    def calculate_portfolio_value(self) -> pd.DataFrame:
        """Calculate total value and gains/losses"""
        if 'Current Price' not in self.df.columns:
            self.fetch_current_prices()

        self.df['Total Purchase Cost'] = self.df['Purchase Price'] * self.df['Quantity']
        self.df['Current Total Value'] = self.df['Current Price'] * self.df['Quantity']
        self.df['Gain/Loss ($)'] = self.df['Current Total Value'] - self.df['Total Purchase Cost']
        self.df['Gain/Loss (%)'] = (
            (self.df['Current Total Value'] - self.df['Total Purchase Cost']) /
            self.df['Total Purchase Cost'] * 100
        )

        return self.df

    def get_summary(self) -> dict:
        """Get portfolio summary statistics"""
        if 'Current Total Value' not in self.df.columns:
            self.calculate_portfolio_value()

        total_invested = self.df['Total Purchase Cost'].sum()
        current_value = self.df['Current Total Value'].sum()
        total_gain = current_value - total_invested
        total_gain_pct = (total_gain / total_invested) * 100 if total_invested > 0 else 0

        return {
            'Total Invested': total_invested,
            'Current Value': current_value,
            'Total Gain/Loss': total_gain,
            'Total Gain/Loss %': total_gain_pct,
            'Total Cards': self.df['Quantity'].sum(),
            'Unique Cards': len(self.df),
            'Best Performer': self.df.loc[self.df['Gain/Loss (%)'].idxmax(), 'Card Name'],
            'Worst Performer': self.df.loc[self.df['Gain/Loss (%)'].idxmin(), 'Card Name'],
        }

    def export_report(self, output_file: str = "reports/portfolio_report.csv"):
        """Export portfolio report"""
        os.makedirs('reports', exist_ok=True)

        if 'Current Total Value' not in self.df.columns:
            self.calculate_portfolio_value()

        timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        self.df.to_csv(output_file, index=False)

        summary = self.get_summary()
        summary_file = output_file.replace('.csv', '_summary.txt')
        with open(summary_file, 'w') as f:
            f.write(f"Portfolio Report - {timestamp}\n")
            f.write("=" * 50 + "\n\n")
            for key, value in summary.items():
                if isinstance(value, float):
                    f.write(f"{key}: ${value:,.2f}\n")
                else:
                    f.write(f"{key}: {value}\n")

        print(f"✓ Report saved to {output_file}")
        return self.df

# Example usage
if __name__ == "__main__":
    api = PokemonPriceAPI()
    tracker = PortfolioTracker(api)
    tracker.load_collection()
    tracker.fetch_current_prices()
    tracker.calculate_portfolio_value()

    summary = tracker.get_summary()
    print("\n" + "=" * 50)
    print("PORTFOLIO SUMMARY")
    print("=" * 50)
    for key, value in summary.items():
        if isinstance(value, float):
            print(f"{key}: ${value:,.2f}")
        else:
            print(f"{key}: {value}")

    tracker.export_report()

Create Your Collection CSV

my_collection.csv:

Card Name,Set,Condition,Quantity,Purchase Price,Purchase Date
Charizard,Base Set,Near Mint,1,500,2023-01-15
Pikachu VMAX,Vivid Voltage,Mint,2,45,2023-06-20
Umbreon VMAX Alt Art,Evolving Skies,Near Mint,1,350,2024-03-10

Step 3: Price Alert System

Create price_alerts.py:

import json
import os
from api_client import PokemonPriceAPI
from datetime import datetime
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart

class PriceAlertSystem:
    """Monitor cards and send alerts"""

    def __init__(self, api: PokemonPriceAPI, alerts_file: str = "price_alerts.json"):
        self.api = api
        self.alerts_file = alerts_file
        self.alerts = self.load_alerts()

    def load_alerts(self) -> list:
        if os.path.exists(self.alerts_file):
            with open(self.alerts_file, 'r') as f:
                return json.load(f)
        return []

    def save_alerts(self):
        with open(self.alerts_file, 'w') as f:
            json.dump(self.alerts, f, indent=2)

    def add_alert(self, card_name: str, set_name: str, target_price: float,
                  alert_type: str = 'below'):
        """Add a price alert"""
        search_query = f"{card_name} {set_name}"
        result = self.api.parse_title(search_query)

        if not result['matches']:
            print(f"✗ Card not found: {search_query}")
            return False

        card = result['matches'][0]['card']

        alert = {
            'id': len(self.alerts) + 1,
            'card_name': card['name'],
            'set_name': card['setName'],
            'tcg_player_id': card['tcgPlayerId'],
            'target_price': target_price,
            'alert_type': alert_type,
            'current_price': card['prices']['market'],
            'created_at': datetime.now().isoformat(),
            'triggered': False
        }

        self.alerts.append(alert)
        self.save_alerts()

        print(f"✓ Alert added: {card['name']} - {set_name}")
        print(f"  Will notify when price is {alert_type} ${target_price:.2f}")
        return True

    def check_alerts(self) -> list:
        """Check all alerts and return triggered ones"""
        triggered_alerts = []

        for alert in self.alerts:
            if alert['triggered']:
                continue

            try:
                card = self.api.get_card(alert['tcg_player_id'])
                current_price = card['prices']['market']

                is_triggered = False
                if alert['alert_type'] == 'below' and current_price <= alert['target_price']:
                    is_triggered = True
                elif alert['alert_type'] == 'above' and current_price >= alert['target_price']:
                    is_triggered = True

                if is_triggered:
                    alert['triggered'] = True
                    alert['triggered_at'] = datetime.now().isoformat()
                    alert['triggered_price'] = current_price
                    triggered_alerts.append(alert)
                    print(f"🔔 ALERT: {alert['card_name']} is now ${current_price:.2f}")

            except Exception as e:
                print(f"✗ Error checking alert: {e}")

        if triggered_alerts:
            self.save_alerts()

        return triggered_alerts

    def send_email_alert(self, alerts: list):
        """Send email notification"""
        if not alerts:
            return

        smtp_server = os.getenv('SMTP_SERVER')
        smtp_user = os.getenv('SMTP_USER')
        smtp_password = os.getenv('SMTP_PASSWORD')
        recipient = os.getenv('ALERT_EMAIL')

        if not all([smtp_server, smtp_user, smtp_password, recipient]):
            print("✗ Email configuration missing")
            return

        msg = MIMEMultipart()
        msg['Subject'] = f'🔔 Pokemon Price Alert - {len(alerts)} cards'
        msg['From'] = smtp_user
        msg['To'] = recipient

        html = f"<h2>Price Alerts Triggered</h2>"
        for alert in alerts:
            html += f"""
            <div style="border: 1px solid #ddd; padding: 15px; margin: 10px 0;">
              <h3>{alert['card_name']}</h3>
              <p>Target: ${alert['target_price']:.2f}</p>
              <p>Current: ${alert['triggered_price']:.2f}</p>
            </div>
            """

        msg.attach(MIMEText(html, 'html'))

        try:
            with smtplib.SMTP(smtp_server, 587) as server:
                server.starttls()
                server.login(smtp_user, smtp_password)
                server.send_message(msg)
            print(f"✓ Email sent to {recipient}")
        except Exception as e:
            print(f"✗ Failed to send email: {e}")

Step 4: Automated Scheduling

Create scheduler.py:

import schedule
import time
from datetime import datetime
from api_client import PokemonPriceAPI
from portfolio_tracker import PortfolioTracker
from price_alerts import PriceAlertSystem

def daily_portfolio_update():
    """Update portfolio values daily"""
    print(f"\n[{datetime.now()}] Running daily portfolio update...")

    try:
        api = PokemonPriceAPI()
        tracker = PortfolioTracker(api)
        tracker.load_collection()
        tracker.fetch_current_prices()
        tracker.calculate_portfolio_value()

        timestamp = datetime.now().strftime('%Y-%m-%d')
        tracker.export_report(f"reports/portfolio_{timestamp}.csv")
        print("✓ Portfolio update complete")
    except Exception as e:
        print(f"✗ Portfolio update failed: {e}")

def check_price_alerts():
    """Check price alerts every hour"""
    print(f"\n[{datetime.now()}] Checking price alerts...")

    try:
        api = PokemonPriceAPI()
        alert_system = PriceAlertSystem(api)
        triggered = alert_system.check_alerts()

        if triggered:
            alert_system.send_email_alert(triggered)
            print(f"✓ Sent {len(triggered)} alerts")
        else:
            print("✓ No alerts triggered")
    except Exception as e:
        print(f"✗ Alert check failed: {e}")

if __name__ == "__main__":
    print("🤖 Pokemon Price Automation Bot Started")
    print("=" * 50)

    # Schedule jobs
    schedule.every().day.at("09:00").do(daily_portfolio_update)
    schedule.every().hour.do(check_price_alerts)

    print("📅 Scheduled jobs:")
    print("  - Daily portfolio update: 9:00 AM")
    print("  - Price alerts check: Every hour")
    print("\nPress Ctrl+C to stop\n")

    # Run forever
    while True:
        schedule.run_pending()
        time.sleep(60)

Step 5: Price Visualization

Create visualization.py:

import matplotlib.pyplot as plt
from api_client import PokemonPriceAPI
import os

def plot_price_history(tcg_player_id: str):
    """Create price history chart"""
    api = PokemonPriceAPI()
    card = api.get_card(tcg_player_id)

    # Extract price history
    history = card.get('priceHistory', [])
    if not history:
        print("No price history available")
        return

    dates = [entry['date'] for entry in history]
    prices = [entry['market'] for entry in history]

    plt.figure(figsize=(12, 6))
    plt.plot(dates, prices, linewidth=2)
    plt.title(f"{card['name']} - {card['setName']} Price History")
    plt.xlabel('Date')
    plt.ylabel('Price ($)')
    plt.grid(True, alpha=0.3)
    plt.xticks(rotation=45)
    plt.tight_layout()

    os.makedirs('reports', exist_ok=True)
    plt.savefig('reports/price_chart.png', dpi=300)
    print("✓ Chart saved to reports/price_chart.png")
    plt.close()

Deployment

Systemd Service (Linux)

Create /etc/systemd/system/pokemon-tracker.service:

[Unit]
Description=Pokemon Card Price Tracker
After=network.target

[Service]
Type=simple
User=your_username
WorkingDirectory=/path/to/pokemon-price-automation
ExecStart=/path/to/pokemon-tracker/bin/python scheduler.py
Restart=always

[Install]
WantedBy=multi-user.target
sudo systemctl enable pokemon-tracker
sudo systemctl start pokemon-tracker

Windows Task Scheduler

Create run_tracker.bat:

@echo off
cd C:\path\to\pokemon-price-automation
C:\path\to\python.exe scheduler.py

Then create a scheduled task in Task Scheduler.

API Usage & Costs

Free Tier: 100 credits/day

  • Portfolio of 10 cards updated daily: 10 credits
  • Hourly alert checks (5 cards): 120 credits/day

Recommendation: API tier ($9.99/mo) for larger portfolios.

Related Tutorials

Get Free API Key →

PokemonPriceTracker Team

PokemonPriceTracker Team

Python Automation Experts

Related Articles

Pokemon Card API: Complete Developer's Guide to Card Data APIs (2025)
API & Development
August 8, 2025

Pokemon Card API: Complete Developer's Guide to Card Data APIs (2025)

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

A Developer's Guide to the Best Pokémon Card Price APIs (2025)
API
August 7, 2025

A Developer's Guide to the Best Pokémon Card Price APIs (2025)

Building an app for Pokémon TCG? You need a reliable price API. This guide reviews the best options for developers and shows you how to get started.

PokemonPriceTracker Team

Pokemon Card Price API: Your Key to Real-Time & Historical TCG Data
API
May 19, 2025

Pokemon Card Price API: Your Key to Real-Time & Historical TCG Data

Unlock the full potential of Pokemon TCG data with the PokemonPriceTracker API. Designed for developers, collectors, and businesses, our API provides seamless access to real-time, historical, and bulk card pricing information from major marketplaces. Power your applications and market analysis with comprehensive, developer-friendly data.

Team

Team

Stay Updated

Subscribe to our newsletter for the latest Pokemon card market trends, investment opportunities, and exclusive insights delivered straight to your inbox.