datagrit

datagrit › Guides › TCGplayer Price Trends & Sales Velocity Tracker

Guide

How to track TCGplayer price changes and sales velocity with Python

Get 30 and 90-day price change, units sold, sales per day and days of supply for Pokemon, Magic and other TCG cards and sealed products.

Published 2026-10-01 · uses the TCGplayer Price Trends & Sales Velocity Tracker Actor

A card's price today tells you very little on its own. A 15-dollar card that sells forty copies a month and rose 9% in thirty days is a very different item from a 15-dollar card that sold twice in a quarter. Resellers deciding what to buy, collectors timing a sale, store owners pricing singles and people building repricing tools all need the same three things: where the price is going, how fast the item actually sells, and how much stock is sitting on the market.

TCGplayer, a marketplace for trading card games from Pokemon and Magic to Lorcana and One Piece, shows sales history and listings on its product pages. But turning that into a ranked list of risers, fallers and fast movers across a whole set or game means collecting per-printing histories for hundreds of products and doing the arithmetic yourself.

This post covers what TCGplayer exposes, what gets in the way, and how to get computed trend metrics as rows.

Where the data comes from

The data comes from TCGplayer's public catalogue: its product search and the price history behind each product page. The history is reported in 3-day windows, separately for each printing (Normal, Holofoil, Reverse Holofoil and so on), language and condition, with the market price, units sold and sale price range of each window.

Several details make the raw history awkward to use:

Step by step

1. Define the input

Pokemon cards from one set, priced at least 5 dollars, with at least 10 sales in 30 days, sorted by TCGplayer relevance:


{
  "searchQueries": [],
  "productLine": "pokemon",
  "setNames": ["sv-scarlet-and-violet-151"],
  "rarities": [],
  "productType": "cards",
  "printings": [],
  "languages": ["English"],
  "minMarketPrice": 5,
  "maxMarketPrice": 0,
  "minSold30d": 10,
  "minSold90d": 0,
  "sortBy": "relevance",
  "maxProductsToScan": 200,
  "maxItems": 300
}

To list risers instead, add "minPriceChange30dPct": 20; for fallers, "maxPriceChange30dPct": -15. productLine covers 25 product lines, including magic, yugioh, lorcana-tcg, one-piece-card-game and pokemon-japan, or all. Leave searchQueries empty to read a whole set or game, or add terms such as charizard or booster box.

2. Run it from Python


from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")

run_input = {
    "searchQueries": ["charizard"],
    "productLine": "pokemon",
    "productType": "cards",
    "minSold30d": 5,
    "maxProductsToScan": 100,
    "maxItems": 200,
}

run = client.actor("datagrit/tcgplayer-price-trend-tracker").call(run_input=run_input)
rows = [r for r in client.dataset(run["defaultDatasetId"]).iterate_items() if r.get("found")]

for r in rows[:10]:
    print(r["productName"], r["variant"], r["marketPrice"], r["priceChange30dPct"], r["soldPerDay"])

3. Or with cURL


curl -X POST \
  "https://api.apify.com/v2/acts/datagrit~tcgplayer-price-trend-tracker/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"searchQueries": ["booster box"], "productLine": "pokemon", "productType": "sealed", "maxProductsToScan": 30, "maxItems": 50}'

Each product needs one extra request for its sales history and the Actor pauses between batches, so plan for roughly 6 seconds per 10 products, about a minute for 100.

What you get

One row per printing and language of a product:

FieldExampleMeaning
productNameCharizard GX - 9/68 (#60 Charizard Stamped)Card or sealed product
setNameBattle AcademySet or expansion
variant / languageHolofoil / EnglishPrinting and language of this row
marketPrice14.57Market price, latest 3-day window, main condition
priceChange30dPct8.8Change versus about 30 days ago
priceChange90dPct12.1Change versus the oldest window of the 90-day history
sold30d / sold90d35 / 138Units sold at the main condition
soldPerDay1.53Average daily sales over the 90-day history
daysOfSupply54.2Active listings divided by average daily sales
marketPriceLightlyPlayed10.67Market price of the Lightly Played condition

Rows also include setCode, cardNumber, rarity, releaseDate, condition, sealed, lowSalePrice90d, highSalePrice90d, lastSaleWindowStart, allConditionsSold90d, prices for Moderately Played, Heavily Played and Damaged, listingsCount, lowestListingPrice, medianListingPrice, productUrl and imageUrl. Note that listing counts, listing prices and daysOfSupply refer to the whole product, not to one printing.

Recipes

Weekly risers report

Schedule a weekly run per game you trade with minPriceChange30dPct: 15 and minSold30d: 10, and write the dataset to a Google Sheet. The sales threshold keeps out cards whose price moved on one or two sales.

Sealed stock tightness

Run with productType: "sealed" and a term like booster box, then sort by daysOfSupply. A low number means listings would sell out in a few days at the current pace; a high one means plenty of stock.

Buy list in pandas


import pandas as pd

df = pd.DataFrame(rows)
df = df.dropna(subset=["priceChange30dPct", "soldPerDay"])

candidates = df[(df["soldPerDay"] >= 1) & (df["priceChange30dPct"].between(0, 25))].copy()
candidates["lp_discount_pct"] = (1 - candidates["marketPriceLightlyPlayed"] / candidates["marketPrice"]) * 100

cols = ["productName", "variant", "marketPrice", "priceChange30dPct", "soldPerDay", "daysOfSupply", "lp_discount_pct"]
print(candidates.sort_values("soldPerDay", ascending=False)[cols].head(25))

This lists steadily rising, frequently sold printings and shows how much cheaper Lightly Played copies trade.

Limitations

The Actor reads publicly available catalogue and sales information; it does not log in or bypass access controls.

Wrapping up

If you price, buy or analyse cards, computed trend and velocity per printing saves most of the spreadsheet work; pricing is pay per result, the Actor is on Apify, and all fields are described in the documentation.

Try TCGplayer Price Trends & Sales Velocity Tracker on ApifyActor documentation

More guides