
Graded Prices Now Include Fanatics Collect Sales
Graded Pokémon prices on PokePriceTracker now combine eBay sold listings with Fanatics Collect auction and buy-now sales, and every sale shows which marketplace it sold on.

Team

PokemonPriceTracker Team

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:
# 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
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", params={"tcgPlayerId": tcg_player_id, "limit": 1})
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']}")
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()
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
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}")
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)
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()
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
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.
Free Tier: 100 credits/day
Recommendation: API tier ($9.99/mo) for larger portfolios.

Python Automation Experts

Graded Pokémon prices on PokePriceTracker now combine eBay sold listings with Fanatics Collect auction and buy-now sales, and every sale shows which marketplace it sold on.

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

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.

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