For developers and agents
Market data API
Public read API for The Lab TCG market data. No API key. Card search, nightly prices, sets, weekly reports and sealed product expected value. Prices are US dollars.
Base URL: https://cpv6bzwui2.execute-api.us-east-1.amazonaws.com
Dates are YYYY-MM-DD. A variant id is a TCGplayer product id, a colon, and the printing, for example 84196:Holofoil. Errors are JSON with a detail field and the HTTP status. Please cache responses and send a User-Agent that names your client. Field names such as market price are defined on the definitions page. Account, billing, newsletter and Haggle routes are not part of this reference.
Cards
- GET /cards/search?q=&limit=20&cursor=
- Name search, optionally qualified by set words or card number (trubbish white flare, trubbish 140, white flare trubbish). Ranked: every printing of the card by that name first (a collector number like "- 125/197" or a variant like "(Secret)" still counts as the card), then things that only share the prefix (sealed boxes, collections); within each, English before Japanese, base printing before reverse holo, newest set first. Returns {items, next_cursor, total}; pass next_cursor back to page. limit 1-50.
- GET /cards/{variant_id}
- Catalog row for one printing (name, set, number, rarity, image) with the latest snapshot price overlaid and price_as_of.
- GET /cards/{variant_id}/history?days=90&sources=
- Daily price series. sources selects the TCGplayer series plus eBay sold/listed feeds where present. Closed months of the TCGplayer series are served from tcg_price_history_months once CompactPriceHistory has compacted the card; the rows are identical either way.
- GET /cards/{variant_id}/page?days=2000
- Everything a card page render needs in one request: {variant_id, detail, history, sales}, each exactly what GET /cards/{variant_id}, GET /cards/{variant_id}/history?days= (all sources) and GET /sales/{variant_id} return. 404 when the card does not exist; sales is null when the card has no sales data. The site uses it in place of three calls per server render (fetchCardPageData in web/src/lib/api.ts) and falls back to the three routes on any error.
- GET /sales/{variant_id}
- Everything a card page render needs in one request: {variant_id, detail, history, sales}, each exactly what GET /cards/{variant_id}, GET /cards/{variant_id}/history?days= (all sources) and GET /sales/{variant_id} return. 404 when the card does not exist; sales is null when the card has no sales data. The site uses it in place of three calls per server render (fetchCardPageData in web/src/lib/api.ts) and falls back to the three routes on any error.
- GET /cards/index?segment=&total_segments=&cursor=&limit=&category_ids=
- Paginated variant-id listing that feeds the card sitemaps. Public and cacheable. Without category_ids it lists the Pokémon catalog (3 and 85, plus rows with no category); category_ids=68 lists one game for its own sitemap. lastmod maps each id to price_changed_on, the snapshot date its headline price (market, else lowest listing) last changed, set by the nightly ingest; ids with no date yet are absent from it.
- GET /cards/hubs/index?letter=&cursor=&limit=
- Published card hubs (2+ printings) from the PublishedIndex GSI: slug, name, updated_at, printings_count, price_low, price_high. letter is one of a-z0-9. Feeds /sitemap-hubs and /cards.
- GET /cards/hubs/{slug}
- One card hub: every English and Japanese printing with market price and 30-day units, card text, counts and cheapest / most valuable / most traded refs. 404 when unpublished. Rebuilt nightly by ComputeCardHubsDaily (22:30 UTC) into tcg_card_hubs_<env>.
Sets
- GET /sets?era=all|modern|vintage&sort_by=value|change|name&limit=200
- Set index: total market value, change and coverage per set, with snapshot_date.
- GET /sets/releases?category_id=3
- Every set with its TCGplayer release date (published_on), newest first, with distinct singles and sealed product counts. Includes presale sets (sealed listed, no singles yet), which /sets leaves out. One meta row per catalog (SET_RELEASES#<category>), written nightly by compute_set_index_daily; groups without a release date are left out. Feeds /release-dates.
- GET /sets/{group_id}
- One set with its top contributors.
- GET /sets/{group_id}/cards?limit=&offset=&product_type=
- Cards in a set with latest prices (served from the GroupIndex GSI, batched price reads). market_price is null when the latest snapshot has none (presale sealed, no sales), never 0; priced rows sort first, highest first. The sorted list is cached per API container for up to an hour, keyed by set, product_type and the latest snapshot date, so a new day's prices are never served from an older entry.
- GET /sets/{group_id}/history?days=
- Set value history. Each item has set_total_value (the cards priced that day, so it jumps when the card list changes), comparable_value (moves only with prices: each day's like-for-like set_pct_change chained and scaled so the latest day equals its total), card_count, coverage_ratio and card_list_changed. card_list_changes lists those days with card_count, prev_card_count and value_effect_pct, the share of the total the list change alone moved.
Reports and signals
- GET /reports/weekly?week_ending=latest&limit=15
- Sunday market report sections (gainers, losers, breakouts, set movers, sealed).
- GET /reports/available?type=weekly&limit=60
- Report periods that exist.
- GET /reports/28d
- Trailing-28-day and month-close reports (same section shape as weekly).
- GET /reports/monthly
- Trailing-28-day and month-close reports (same section shape as weekly).
- GET /reports/daily?date=&category=
- Daily market report. GET /reports/daily/available (and its alias /reports/daily/history) lists dates.
- GET /reports/daily/available
- Daily market report. GET /reports/daily/available (and its alias /reports/daily/history) lists dates.
- GET /signals/top?type=&limit=&date=
- Top daily signals of one type (min_abs_z, min_spread_pct filters for divergence).
Sold data
- GET /sales/most-sold?window=7d
- Top 100 cards by TCGplayer units sold in the last 7 days, with NM realized price ranges.
Sealed
- GET /sealed/ev
- Latest booster-box EV per active config, ranked by EV ratio, with pull-rate assumptions (pull_rates, tier_breakdown, cards_per_pack, source_url) so the page can show its working, and up to 10 top_contributors. Active configs come from the EV config table plus lambdas/common/booster_ev_configs.py for sets the table lacks.
Grading
- GET /grading/leaderboard
- ranks raw cards by the gross uplift to a PSA 10 using our own eBay sold comps (public, complete).
Platform
- GET /health
- Liveness.
- GET /metrics
- Catalog, user and snapshot counts for the homepage (cached an hour per container).
Pokémon Japan
- TCGplayer category 85 is ingested nightly (02:30 UTC, an hour before the English run) and indexed alongside English. Day completeness stays gated on category 3 only.
- GET /sets?category_id=85 lists Japanese sets; category_id=3 English; omit for both. Every row carries category_id (rows written before 2026-09-08 default to 3).
- GET /cards/search?q=pikachu 100 japanese: the words japanese, japan and jp select the Japanese catalog (plus English cards whose name says Japanese) and are not treated as part of the card name. Unqualified queries rank English before Japanese. Search items carry category_id.
- GET /cards/search?q=delta reign etb: sealed products are listed by full name, so the qualifiers etb (Elite Trainer Box), pc (Pokemon Center), bb (booster box) and upc (Ultra-Premium Collection) also match those names.
- GET /cards/{variant_id} and history are unchanged; Japanese cards have category_id: 85.
- eBay comps for Japanese cards are searched with the Japanese qualifier so English slabs do not pollute them.
