Whatnot
Whatnot Price Tracker: Track Sold Prices and Auctions
Build a Whatnot price tracker with scheduled scrapes: capture asking prices, current bids and last-sale prices, store snapshots and get alerts on changes.
Whatnot
Build a Whatnot price tracker with scheduled scrapes: capture asking prices, current bids and last-sale prices, store snapshots and get alerts on changes.
If you buy or sell on Whatnot, you want to know what a card, sneaker or sealed box is going for this week, not three months ago. Whatnot has no price history page and no public API, so the practical approach is to snapshot search results on a schedule and compare them yourself.
This tutorial sets up that tracker with the Whatnot Search Scraper: a fixed list of queries, a daily schedule and a short Python script that saves each snapshot and flags price moves. If you also want seller or category data in the same runs, the all-in-one Whatnot Scraper accepts the same search input in search mode.
A Whatnot search returns several record types. Two of them matter for pricing:
Record type |
Price fields | What it tells you |
|---|---|---|
listing |
price, currentBid, currentBidCount, transactionType, quantity |
Live offers: buy-it-now asking prices and auction bids right now |
product |
listingPrice, lastSalePrice, numListings |
Catalog view of one product: current lowest listing, most recent sale, and supply |
lastSalePrice on product records is the field to use for “Whatnot sold prices”. It’s the most recent sale Whatnot reports for that catalog product, not a full transaction history, which is why snapshots matter. Run daily and the lastSalePrice values build your own sold-price series. The rest of this guide is about making that happen.
Prices come as { "amount": ..., "currency": ... }. In the README samples the amounts look like minor units (499 USD = $4.99), so check one record against the live site on your first run and divide by 100 if that holds.
Pick specific search terms. “pokemon” returns noise, while “pokemon 151 booster bundle” returns comparable items. Keep auctions and buy-it-now listings in separate series, since auction prices mid-show behave differently.
{
"searchQueries": [
"pokemon 151 booster bundle",
"one piece op-07 booster box",
"jordan 4 military black"
],
"vertical": "PRODUCT",
"maxResultsPerQuery": 100
}
If you’ve built a filtered or sorted search on whatnot.com, paste the URL into searchUrls instead. The URL’s own vertical and sort settings are kept.
Run it once from the Apify Console and look at the output to confirm the results match what you expect.
Each run writes a fresh dataset, and every record carries searchQuery and scrapedAt, so the timestamps line up across runs.
The script below runs the actor, appends every priced record to a CSV, and prints queries where the median asking price moved more than 10% since the previous snapshot. Run it from cron, GitHub Actions or anywhere that has Python.
import csv, os, statistics
from datetime import datetime, timezone
from pathlib import Path
from apify_client import ApifyClient
QUERIES = ["pokemon 151 booster bundle", "one piece op-07 booster box"]
HISTORY = Path("whatnot_prices.csv")
THRESHOLD = 0.10 # alert on a 10% move
client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("epicscrapers/whatnot-search-scraper").call(run_input={
"searchQueries": QUERIES,
"vertical": "PRODUCT",
"maxResultsPerQuery": 100,
})
def amount(p):
return p.get("amount") if isinstance(p, dict) else p
rows = []
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
if item["type"] == "listing" and item.get("transactionType") == "BUY_IT_NOW":
rows.append([item["scrapedAt"], item["searchQuery"], "ask",
amount(item.get("price")), (item.get("price") or {}).get("currency"),
item.get("title")])
elif item["type"] == "listing" and item.get("currentBid"):
rows.append([item["scrapedAt"], item["searchQuery"], "bid",
amount(item["currentBid"]), None, item.get("title")])
elif item["type"] == "product" and item.get("lastSalePrice"):
rows.append([item["scrapedAt"], item["searchQuery"], "last_sale",
amount(item["lastSalePrice"]), item["lastSalePrice"].get("currency"),
item.get("name")])
# Load the previous snapshot's medians before appending
previous = {}
if HISTORY.exists():
with HISTORY.open() as f:
hist = [r for r in csv.reader(f) if r[2] == "ask" and r[3]]
if hist:
last_run = max(r[0][:13] for r in hist) # group by hour
for q in QUERIES:
vals = [float(r[3]) for r in hist if r[1] == q and r[0][:13] == last_run]
if vals:
previous[q] = statistics.median(vals)
with HISTORY.open("a", newline="") as f:
csv.writer(f).writerows(rows)
for q in QUERIES:
now = [float(r[3]) for r in rows if r[1] == q and r[2] == "ask" and r[3]]
if now and q in previous:
change = (statistics.median(now) - previous[q]) / previous[q]
if abs(change) >= THRESHOLD:
print(f"{q}: median ask moved {change:+.0%}")
print(f"Saved {len(rows)} price points at {datetime.now(timezone.utc).isoformat()}")
Notes on the script:
ask, bid, last_sale), so bids and sales don’t get mixed in with asking prices.Useful rules to start with:
lastSalePrice rises across three consecutive runs: demand is building.numListings on a product jumps: supply is increasing and prices usually soften soon after.Send the printed alerts to Slack, email or Telegram from the same script, or use an Apify webhook that fires when the scheduled run succeeds.
Auction listings in search results show currentBid and currentBidCount at the moment of the scrape, with isLive telling you whether the item is in a live show. To watch a niche’s live auctions, run a LIVESTREAM search in the Whatnot Scraper to see which shows are running and how many activeViewers each has. The Whatnot scraper guide covers livestream and category deep-dive inputs.
The same snapshot approach works on other platforms. See Vinted price tracking and eBay sold listings to compare prices across platforms.
Whatnot doesn’t publish price history. A scheduled scraper is the simplest way to build one, and your history starts from the first snapshot.
You can see the most recent sale per catalog product through lastSalePrice. Collect it daily and you’ll have a sold-price trend.
Daily for most products. For new releases or hyped items, every few hours. Costs scale with results, so check the actor’s Pricing tab and keep maxResultsPerQuery tight.
No. Public search results are available without an account.
Save your queries as a task, schedule it daily and let the snapshots build up. Open the Whatnot Search Scraper to run your first snapshot.