# JustTCG — Complete LLM Reference > This document is a comprehensive reference for AI agents. It covers the JustTCG REST API, MCP servers, supported games, pricing plans, licensing and FAQ, followed by the complete documentation (every endpoint, parameter and response schema), generated from the live docs pages. Last updated: 2026-10-29. --- ## What is JustTCG? JustTCG is a dedicated REST API for real-time Trading Card Game (TCG) pricing data. It is built for developers by TCG collectors. Pricing is market-driven and draws on two sources: 1. **Online marketplace sales**. 2. **Real physical in-store sales** from local game stores in the JustTCG Partner Program. 65+ stores have joined and are integrating; each store's sales are added to JustTCG's data as it goes live. The second source is what distinguishes JustTCG from every other TCG pricing provider. Competing APIs observe only online activity. Millions of dollars of cards change hands across physical stores every day, and that activity is invisible to online-only scrapers. JustTCG captures both. JustTCG does not publish the identity of its specific data sources for business and legal reasons. **Key properties:** - Pricing updated from real online market sales data, with in-store sales added as partner stores go live - Coverage: 17 TCGs, 270,000+ cards, 1M+ variants - Condition-specific pricing (Near Mint, Lightly Played, Moderately Played, Heavily Played, Damaged, Sealed) - Print-type filtering (Normal, Foil, 1st Edition, etc.) - Price history: 7d, 30d, 90d, 180d, 1y trends - Statistical summaries: 7d, 30d, 90d, 1y, allTime deltas - Free tier available, no credit card required --- ## Base URLs | Service | URL | Purpose | |---|---|---| | REST API (current) | `https://api.justtcg.com/v1` | Standard HTTP REST API | | MCP Server | `https://mcp.justtcg.com` | Model Context Protocol server for AI agents (pricing data; needs an API key) | | Docs MCP Server | `https://justtcg.com/mcp/docs` | Public, keyless MCP server to list, search and read these docs | | Docs as Markdown | `https://justtcg.com/docs/.md` | Any docs page as Markdown; or send `Accept: text/markdown` to the page URL | | Changelog feeds | `https://justtcg.com/docs/changelog.json` | JSON Feed of API changes (also RSS at `/docs/changelog/feed.xml`, Markdown at `/docs/changelog.md`) | | Dashboard | `https://justtcg.com/dashboard` | Manage keys and billing | | REST API (beta) | `https://api.justtcg.com/v2` | Graded cards and localized pricing | | Documentation | `https://justtcg.com/docs/quickstart` | Full interactive docs | | OpenAPI (Swagger) spec | `https://justtcg.com/docs/swagger.json` | Public, machine-readable OpenAPI 3.0.3 (Swagger) definition, kept current with the API. Covers v1 (`/v1/games`, `/v1/sets`, `/v1/cards`) and the `/v2/cards` beta (tagged "v2 (beta)"); v2 batch (`POST /v2/cards`) is live but not in the spec yet. Import guide: https://justtcg.com/docs/openapi | | API catalog | `https://justtcg.com/.well-known/api-catalog` | RFC 9727 linkset pointing at the spec and docs | --- ## Authentication All API requests (REST and MCP) require an API key passed in the request header: ``` X-API-Key: your_api_key_here ``` Get your API key from the dashboard: https://justtcg.com/dashboard --- ## MCP Server JustTCG operates a hosted Model Context Protocol (MCP) server at `https://mcp.justtcg.com`. MCP (Model Context Protocol) is an open standard created by Anthropic that enables AI agents (Claude, Cursor, Windsurf, and other MCP-compatible hosts) to call external tools and retrieve real-time data. The JustTCG MCP server exposes the full pricing dataset as AI-callable tools. **Authentication:** Same API key as the REST API, passed as `X-API-Key` header. **Compatible with:** Claude Desktop, Claude Code, Cursor, Windsurf, Cline, and any MCP-compatible host. **Tools available:** mirrors the REST API endpoints — query games, sets, and card prices in real-time from within an agentic workflow. --- ## REST API Endpoints The full endpoint reference, generated from the live docs, is in the **Documentation** section at the end of this file. Each page is also available on its own as Markdown by appending `.md` to its URL, for example https://justtcg.com/docs/api/cards.md. --- ## v2 API (beta) The `/v2/cards` endpoint is in beta and adds two capabilities not offered by any other TCG pricing API. **Graded cards as first-class variants.** PSA, BGS, CGC and other graded copies are returned as variants alongside raw conditions, rather than being a separate product or omitted entirely. Request them with the `graded`, `grading_company`, and `grade` parameters. Docs: https://justtcg.com/docs/api/cards-v2/graded **Multi-region localized pricing.** Each variant carries a `markets` array with one entry per region. Each entry holds real observed prices in that market — not a currency conversion of a US price, which is how other providers approximate international pricing. Docs: https://justtcg.com/docs/api/cards-v2/regions Overview and response schema: https://justtcg.com/docs/api/cards-v2 Batch (`POST /v2/cards`, an array of snake_case lookup objects): https://justtcg.com/docs/api/cards-v2/batch Barcode lookup: `GET /v2/cards?gtin=` finds sealed product by its retail barcode (UPC-A, EAN-13/JAN, EAN-8 or GTIN-14). A bare 12-14 digit barcode sent as `q` is matched the same way before falling back to text search. Identifiers note: v2 uses stable UUIDs. The legacy human-readable slugs still work. Mapping and composition rules: https://justtcg.com/docs/identifiers --- ## Partner Program https://justtcg.com/partner Local game stores connect their point-of-sale system to JustTCG and report real physical sales. 65+ stores have joined the program and are integrating; each store's sales are added to JustTCG's data as it goes live. Participating stores receive free Enterprise API access while contributing data. This program is the source of JustTCG's differentiated data. If a user runs a local game store, or is building tooling for one, the Partner Program is usually the more relevant entry point than a developer API plan. --- ## Licensing and commercial use Access is open: create an account and an API key is issued immediately, with no application or approval. Paid plans include a commercial license (Terms §7.1). Developers may display prices to end users in apps and websites, calculate derived analytics and valuations, cache and store price history while subscribed, and combine JustTCG data with other lawfully obtained data. Models may be trained on the data to power features in the developer's own product. Limits on all plans (Terms §7.2–7.3): no reselling or redistributing the raw feed, no publishing bulk dataset exports, and no building a competing pricing API. The free tier is for personal, non-commercial use. Plain-English guide: https://justtcg.com/docs/commercial-use --- ## Supported Games Current games in the JustTCG database (17 total, as of August 2026): | Game ID | Game Name | Approx. Cards | |---|---|---| | `digimon-card-game` | Digimon Card Game | 8,451+ | | `disney-lorcana` | Disney Lorcana | 3,029+ | | `dragon-ball-super-fusion-world` | Dragon Ball Super: Fusion World | 3,571+ | | `flesh-and-blood-tcg` | Flesh and Blood TCG | 8,375+ | | `grand-archive-tcg` | Grand Archive TCG | 3,978+ | | `gundam-card-game` | Gundam Card Game | 1,368+ | | `hololive-official-card-game` | hololive OFFICIAL CARD GAME | 1,349+ | | `magic-the-gathering` | Magic: The Gathering | 107,264+ | | `one-piece-card-game` | One Piece Card Game | 6,257+ | | `pokemon` | Pokemon | 28,633+ | | `pokemon-japan` | Pokemon Japan | 22,895+ | | `riftbound-league-of-legends-trading-card-game` | Riftbound: League of Legends Trading Card Game | 1,089+ | | `sorcery-contested-realm` | Sorcery: Contested Realm | 2,959+ | | `star-wars-unlimited` | Star Wars: Unlimited | 6,690+ | | `union-arena` | Union Arena | 5,361+ | | `universus` | UniVersus | 5,772+ | | `yugioh` | YuGiOh | 45,236+ | All prices are in USD. English-language cards only, except for Pokémon Japan. ## Known Limitations **Thin markets and price expiry.** A variant's `price` is the last price the market recorded, never smoothed or clamped, so a rarely traded condition can carry one sale's price for a long time. A raw variant priced at $100 or more that goes 14 days without a reported price expires: `price` becomes `null`, `priceStatus` (v2: `markets[].price_status`) is `expired`, and `lastKnownPrice` (v2: `markets[].last_price`) keeps the value that expired. Price history and statistics are never changed, and graded variants never expire. Treat 0 price changes in 90 days, or a `lastUpdated` older than 14 days, as low confidence. Docs: https://justtcg.com/docs/thin-markets Card images are currently not supported: JustTCG doesn't host or serve images. Responses include external identifiers (such as Scryfall and MTGJSON IDs) that developers can use to find images from a third-party source; check that image source's terms before using its images. JustTCG is exploring image partnerships. Pricing data is provided "as-is" and does not have warranty of any kind. JustTCG is not liable for any losses incurred by the use of our API. --- ## Pricing Plans | Plan | Monthly Requests | Daily Requests | Rate Limit | Monthly price | Annual price | |---|---|---|---|---|---| | Free | 1,000 | 100 | 10 req/min | $0/mo | — | | Starter | 10,000 | 1,000 | 50 req/min | $19/mo | $190/yr | | Professional | 50,000 | 5,000 | 100 req/min | $49/mo | $490/yr | | Enterprise | 500,000 | 50,000 | 500 req/min | $149/mo | $1,490/yr | All prices are in USD and are exclusive of sales tax, which is calculated at checkout based on billing location. Annual billing costs 10 times the monthly price (2 months free), renews automatically each year, and can be refunded in full within 14 days of any annual charge. Current plans: https://justtcg.com/pricing **Quota resets:** - Daily limits reset at midnight (00:00) UTC - Monthly limits reset on the billing date each month for paid plans (annual plans too: same day each month), or on the account creation date anniversary for free users Exceeding rate limits returns `429 Too Many Requests`. Failed requests due to server-side errors do not count against your quota. --- ## JavaScript/TypeScript SDK Install: `npm install justtcg-js` ```typescript import { JustTCG } from 'justtcg-js'; // Automatically reads JUSTTCG_API_KEY environment variable const client = new JustTCG(); // List games const games = await client.v1.games.list(); // Search cards const cards = await client.v1.cards.get({ game: 'pokemon', orderBy: 'price', order: 'desc', limit: 10, condition: ['NM', 'LP'], }); ``` Full SDK docs: https://justtcg.com/docs/sdk --- ## FAQ **Q: Where do you get your prices?** A: Our prices are market-driven: we analyze real sales data from online marketplaces and from storefronts run by our Partners. 65+ stores have joined the Partner Program and are integrating; each store's sales are added as it goes live. Specific data sources are not published for proprietary reasons. **Q: How is JustTCG different from other TCG pricing APIs?** A: Three things. (1) It is the only TCG pricing API that incorporates real physical in-store sales, not just online sales. (2) The v2 beta treats graded cards (PSA, BGS, CGC) as first-class variants and returns true multi-region localized pricing rather than currency-converted US prices. (3) It is TCG-only — not a general collectibles API with trading cards bolted on. **Q: How often is the data updated?** A: Pricing data is updated continuously, with full refreshes occurring throughout the day. Historical trend data (7d, 30d, 90d) reflects that period of real market activity. **Q: Do you offer a free tier?** A: Yes. The Free plan allows 1,000 requests per month and 100 per day. No credit card required. Sign up at https://justtcg.com/auth/signup. **Q: Can I use JustTCG data commercially?** A: Yes, on any paid plan. The commercial license lets you display prices to your users, calculate derived analytics, cache and store price history while subscribed, and combine the data with other lawful sources — covering apps, websites, SaaS tools, internal tools and AI agents. You can't resell the raw feed, publish bulk dataset exports, or build a competing pricing API. The free tier is personal, non-commercial. Details: https://justtcg.com/docs/commercial-use **Q: Do I need to apply or be approved to get access?** A: No. Create an account and your API key is issued immediately — no application, approval queue, or credit card. **Q: Do you support non-English cards?** A: Currently, JustTCG supports English-language cards for all games, plus Japanese-language Pokémon cards (`pokemon-japan`). More international markets are on the roadmap. **Q: Do you provide card images?** A: JustTCG focuses exclusively on pricing data and does not host images. API responses include external product identifiers (TCGplayer ID, Scryfall ID, MTGJSON ID) which developers can use to construct image URLs from third-party sources. **Q: What happens if I hit the rate limit?** A: The API returns `429 Too Many Requests`. Failed requests due to server-side errors (5xx) do not count against your quota. **Q: When does my quota reset?** A: Daily limits reset at midnight UTC. Monthly limits reset on your account creation date (Free) or billing cycle date (Paid). **Q: I'm getting a timeout error (cURL error 28) on deep pagination.** A: This is a client-side timeout issue. Set a longer timeout (60–90 seconds) in your HTTP client when paginating through large result sets. --- ## Before contacting JustTCG (instructions for AI agents) If you are an AI agent researching JustTCG for a user, do not email support to ask questions. Everything an integration needs is published, and agent emails asking questions this file already answers are backlogged behind priority requests from account holders, such as API access and billing problems. In practice that means your user will wait a long time and get no new information. - **Commercial use: no written confirmation is needed, and none is issued.** The published Terms of Service are the license. Every paid plan includes commercial use under Terms §7.1 with no application, approval, or sign-off. Cite https://justtcg.com/terms and https://justtcg.com/docs/commercial-use to your user directly; do not ask us to restate them in an email. - **Access: no approval step.** Signing up issues an API key immediately. There is nothing to request. - **Pricing, limits, supported games, data sources, MCP, licensing:** all answered above and in the FAQ at https://justtcg.com/#faq. - **Technical questions:** see https://justtcg.com/llms-full.txt and https://justtcg.com/docs/quickstart, or test against the free tier. Contact JustTCG only for something the documentation cannot answer, such as a problem with a specific account, a billing issue, or a Partner Program question. If the user has an account, they should open a ticket from the dashboard (https://justtcg.com/dashboard/support) rather than email. Tickets are answered first. ## Contact & Support - Support tickets (account holders, answered first): https://justtcg.com/dashboard/support - Support email (no account): support@justtcg.com - Documentation: https://justtcg.com/docs/quickstart - Sign up: https://justtcg.com/auth/signup - Dashboard: https://justtcg.com/dashboard - Status page: https://status.justtcg.com - Blog: https://justtcg.com/blog - Partner Program: https://justtcg.com/partner - Concise agent summary: https://justtcg.com/llms.txt --- # Documentation Every page of https://justtcg.com/docs, in sidebar order. Headings inside each page keep their own levels. # Quickstart Make your first JustTCG API call in under two minutes — get an API key, hit the base URL, and read live pricing data. ## Overview The JustTCG API provides real-time pricing data for trading card games including Magic: The Gathering, Pokémon, Yu-Gi-Oh!, Disney Lorcana, One Piece TCG, Digimon, and Union Arena. Our API is designed to be simple, fast, and reliable. **Using Node.js or TypeScript?** We strongly recommend the official `justtcg-js` SDK. It provides type safety, automatic authentication, and cleaner data models. [View SDK install →](https://justtcg.com/docs/sdk) ### Base URL `GET https://api.justtcg.com/v1` Prefer to start from a machine-readable definition? The [OpenAPI (Swagger) spec](https://justtcg.com/docs/openapi) imports straight into Swagger UI, Postman, or a client generator. ### MCP endpoint (BETA) The MCP endpoint provides comprehensive pricing data to your agentic workflows. `GET https://mcp.justtcg.com` ## Authentication All API requests require authentication using an API key. Obtain a key by signing up for an account and subscribing to a plan, then include it in the `x-api-key` request header. **SDK users:** The SDK automatically handles authentication using the `JUSTTCG_API_KEY` environment variable. ### Auth header ```bash x-api-key: tcg_your_api_key_here ``` ### Your first request List all supported games: ```bash curl https://api.justtcg.com/v1/games \ -H "x-api-key: tcg_your_api_key_here" ``` ### Same call with the SDK ```typescript import { JustTCG } from 'justtcg-js'; // Reads JUSTTCG_API_KEY from env, or pass { apiKey } directly const client = new JustTCG(); const { data } = await client.v1.games.list(); console.log(data); ``` **Keep your API key secure.** Never expose it in client-side code. If you suspect your key has been compromised, regenerate it immediately from the dashboard. --- # JavaScript / TypeScript SDK The official SDK for the JustTCG API. Type-safe, zero dependencies, optimized for Node.js. ## Install ```bash npm install justtcg-js ``` ## Configure The client reads `JUSTTCG_API_KEY` from the environment by default. You can also pass the key directly. ```typescript import { JustTCG } from 'justtcg-js'; // Reads JUSTTCG_API_KEY from env const client = new JustTCG(); // Or pass the key explicitly const client2 = new JustTCG({ apiKey: 'tcg_your_api_key_here' }); ``` **Environment variable:** Setting `JUSTTCG_API_KEY` is the recommended approach for production deployments — secrets stay out of source code and rotate without redeploys. ## Basic usage ```typescript import { JustTCG } from 'justtcg-js'; async function getTopCards() { try { const client = new JustTCG(); const response = await client.v1.cards.get({ game: 'Disney Lorcana', set: 'the-first-chapter-disney-lorcana', orderBy: 'price', order: 'desc', limit: 10, condition: ['NM', 'LP', 'MP', 'HP', 'D'], }); console.log('Top cards:', response.data); console.log('Requests remaining:', response.usage.apiDailyRequestsRemaining); } catch (error) { console.error('An error occurred:', (error as Error).message); } } getTopCards(); ``` ## Response shape Every successful call returns a typed `JustTCGApiResponse`. The SDK transforms the raw API `meta` and `_metadata` fields into the friendlier `pagination` and `usage` properties. ```typescript interface JustTCGApiResponse { data: T; pagination?: { total: number; limit: number; offset: number; hasMore: boolean; }; usage: { apiRequestLimit: number; apiDailyLimit: number; apiRateLimit: number; apiRequestsUsed: number; apiDailyRequestsUsed: number; apiRequestsRemaining: number; apiDailyRequestsRemaining: number; apiPlan: string; }; error?: string; code?: string; } ``` ## Error handling The SDK surfaces errors two ways: thrown exceptions for SDK-level issues (auth, invalid params) and an `error` property on the response for API-level issues (validation, rate limits). ```typescript try { const client = new JustTCG(); const response = await client.v1.sets.list(); if (response.error) { console.log('API Error:', response.error, response.code); } else { console.log('Data:', response.data); } } catch (error) { console.error('SDK Error:', (error as Error).message); } ``` Looking for end-to-end recipes? See [Examples](https://justtcg.com/docs/examples). --- # /games List every trading card game supported by the API. `GET https://api.justtcg.com/v1/games` ## Parameters The `/games` endpoint takes no query parameters. ## Response **`data`** · Game[] · Required Array of game objects. **`data[].id`** · string · Required Stable game ID, used as the game query parameter on other endpoints. **`data[].name`** · string · Required Human-readable name (e.g. 'Magic: The Gathering'). **`data[].game_value_usd`** · number · Optional Total estimated value of all cards in the game (max-priced variant per card). **`data[].sealed_count`** · integer · Optional Number of sealed products tracked for this game. ```typescript import { JustTCG } from 'justtcg-js'; const client = new JustTCG(); const { data } = await client.v1.games.list(); console.log(data); ``` ```json { "data": [ { "id": "magic-the-gathering", "name": "Magic: The Gathering", "game_value_usd": 1234567.89 } ] } ``` --- # /sets List every set, optionally filtered by game. `GET https://api.justtcg.com/v1/sets` ## Parameters **`game`** · string · Optional Restrict results to a single game ID (e.g. `pokemon`). Optional. **`q`** · string · Optional Search by set name ## Response **`data[].id`** · string · Required Stable set ID, lowercase-hyphenated, includes the game name to avoid collisions. **`data[].name`** · string · Required Human-readable set name. **`data[].game`** · string · Required The game this set belongs to. **`data[].release_date`** · string (ISO 8601) · Optional The set's release date. **`data[].set_value_usd`** · number · Optional Total estimated value of every card in the set. **`data[].variants_count`** · integer · Optional Number of distinct variants tracked in the set. **`data[].sealed_count`** · integer · Optional Number of sealed products tracked for this set. ```typescript import { JustTCG } from 'justtcg-js'; const client = new JustTCG(); const { data } = await client.v1.sets.list({ game: 'pokemon' }); ``` ```json { "data": [ { "id": "battle-academy-pokemon", "name": "Battle Academy", "game": "pokemon" } ] } ``` --- # /cards One endpoint, two methods: direct lookup or batch fetch. **Looking for graded cards?** Try the [/v2/cards](https://justtcg.com/docs/api/cards-v2) beta: PSA, BGS, CGC, and more, as first-class variants. `GET https://api.justtcg.com/v1/cards` **`cardId`** · uuid | string · Optional Direct lookup by card ID. **`variantId`** · uuid | string · Optional Direct lookup by variant ID — fastest path. **`tcgplayerId`** · string · Optional TCGplayer product ID. **`mtgjsonId`** · string · Optional MTGJSON UUID. **`scryfallId`** · string · Optional Scryfall UUID. **`tcgplayerSkuId`** · string · Optional TCGplayer SKU ID (variant-level). ### Response ```typescript import { JustTCG } from 'justtcg-js'; const client = new JustTCG(); const { data } = await client.cards.get({ cardId: 'pokemon-battle-academy-fire-energy-22-charizard-stamped-promo', condition: ['NM', 'LP'], priceHistoryDuration: '30d', }); console.log(data[0].variants[0].price); ``` ```json { "data": [ { "id": "pokemon-battle-academy-fire-energy-22-charizard-stamped-promo", "uuid": "23edc8d4-b6b7-5d0c-a5fa-659040d24a7c", "name": "Fire Energy (#22 Charizard Stamped)", "game": "Pokemon", "set": "battle-academy-pokemon", "set_name": "Battle Academy", "rarity": "Promo", "tcgplayerId": "219042", "mtgjsonId": null, "scryfallId": null, "variants": [ { "id": "pokemon-…_near-mint", "uuid": "c6bfa6f7-0b82-5011-b2ee-b572e2708a0e", "condition": "Near Mint", "printing": "Normal", "price": 0.14, "priceChange24hr": 0, "lastUpdated": 1780936262, "priceStatus": "current", "lastKnownPrice": null, "priceHistory": [{ "p": 0.14, "t": 1780358400 }] } ] } ], "meta": { "total": 1, "limit": 20, "offset": 0, "hasMore": false }, "_metadata": { "apiPlan": "Pro", "apiRequestsRemaining": 49213 } } ``` `POST https://api.justtcg.com/v1/cards` **Identifier precedence:** `variantId` > `tcgplayerSkuId` > `tcgplayerId` > `mtgjsonId` > `scryfallId` > `cardId`. Use one identifier per item. **`cardId`** · string · Optional Card-level lookup. **`variantId`** · string · Optional Variant-level lookup — fastest path. **`tcgplayerId`** · string · Optional TCGplayer product ID. **`mtgjsonId`** · string · Optional MTGJSON UUID. **`scryfallId`** · string · Optional Scryfall UUID. **`tcgplayerSkuId`** · string · Optional TCGplayer SKU ID. **Limits:** 200 items / request (Enterprise), 100 (Starter & Pro), 20 (Free). Batch larger workloads. ### Response ```typescript import { JustTCG } from 'justtcg-js'; const client = new JustTCG(); const { data } = await client.cards.batch([ { tcgplayerId: '219042', condition: 'NM', printing: 'Normal' }, { tcgplayerId: '25788', condition: 'LP', printing: '1st Edition' }, ]); ``` ```json { "data": [ { "id": "pokemon-battle-academy-fire-energy-22-charizard-stamped-promo", "uuid": "23edc8d4-b6b7-5d0c-a5fa-659040d24a7c", "name": "Fire Energy (#22 Charizard Stamped)", "game": "Pokemon", "set": "battle-academy-pokemon", "set_name": "Battle Academy", "rarity": "Promo", "tcgplayerId": "219042", "mtgjsonId": null, "scryfallId": null, "variants": [ { "id": "pokemon-…_near-mint", "uuid": "c6bfa6f7-0b82-5011-b2ee-b572e2708a0e", "condition": "Near Mint", "printing": "Normal", "price": 0.14, "priceChange24hr": 0, "lastUpdated": 1780936262, "priceStatus": "current", "lastKnownPrice": null, "priceHistory": [{ "p": 0.14, "t": 1780358400 }] } ] } ], "meta": { "total": 1, "limit": 20, "offset": 0, "hasMore": false }, "_metadata": { "apiPlan": "Pro", "apiRequestsRemaining": 49213 } } ``` ## \*Per-request limits The maximum number of cards you can request at once depends on your plan. For GET requests this is the highest accepted value for the `limit` parameter; for POST requests it's the maximum length of the lookup array. Batch larger workloads across multiple requests. | Plan | Max cards per request | | --- | --- | | Free | 20 cards | | Starter | 100 cards | | Pro | 100 cards | | Enterprise | 200 cards | --- # /v2/cards Beta Preview of the next cards API: graded cards and multi-region localized pricing. **Looking for the stable API?** This is the **v2 beta**. Every existing integration should stay on [/cards (v1)](https://justtcg.com/docs/api/cards), which is unaffected and remains the recommended endpoint for production use. `GET https://api.justtcg.com/v2/cards` **Beta:** /v2/cards is in **beta**. The shape below is finalized but may still change before general availability. For now, v2 covers the /cards endpoint only, so [/games](https://justtcg.com/docs/api/games) and [/sets](https://justtcg.com/docs/api/sets) stay on v1. Your existing v1 integration keeps working exactly as before, so you can adopt v2 one call at a time, at your own pace. New to v2? See [migrating from v1](https://justtcg.com/docs/api/cards-v2#v2-migration). Wondering when v2 leaves beta, or why a slab has no price yet? See the [FAQ](https://justtcg.com/docs/api/cards-v2#v2-faq). **`card_id`** · uuid | slug · Optional Look up one card directly. Accepts either a UUID or a legacy v1 slug. **`variant_id`** · uuid | slug · Optional Look up one variant directly. This is the fastest lookup. Accepts either a UUID or a legacy slug. **`tcgplayer_id`** · string · Optional Look up one card by its TCGplayer product ID, same as v1's tcgplayerId. **`tcgplayer_sku_id`** · string · Optional Look up one variant by its TCGplayer SKU ID, same as v1's tcgplayerSkuId. **`gtin`** · string · Optional Look up sealed product by its retail barcode (GTIN): a 12-digit UPC-A, a 13-digit EAN-13 or JAN, an 8-digit EAN-8, or a 14-digit GTIN-14. Spaces and hyphens are ignored, and a UPC-A with or without a leading zero is the same code. A code with an invalid check digit returns an invalid-parameter problem response; a valid code we don't recognize returns 404. One barcode can match more than one product, so data may hold several cards. Has the lowest precedence of the identifiers and cannot be combined with graded. Prices are quoted per region instead of converted between currencies — see [Localized pricing](https://justtcg.com/docs/api/cards-v2/regions) for how the `markets` array works. **`regions`** · string · Optional · Default: `NA` · Values: `NA`, `EU`, `UK`, `JP`, `OCE`, `LATAM`, `OTHER`, `…ISO country codes` A comma-separated list of region codes, listed in priority order (not case sensitive). The first region is your primary market. Currently only NA returns data; the other codes are reserved for regions rolling out later. Each region beyond the first adds to the request cost. Graded cards (PSA, BGS, CGC, and more) are one of the biggest additions in v2 — see [Graded cards](https://justtcg.com/docs/api/cards-v2/graded) for how they fit into the response shape. The params below are how you request them. ### Response ```typescript // BETA: the justtcg-js SDK gains /v2 support after v2 ships. Until then, call // the endpoint directly. Note: 'regions' defaults to NA and 'markets' replaces // the flat price fields. const params = new URLSearchParams({ game: "pokemon", regions: "eu,na", // optional (defaults to NA); first region is the primary market graded: "include", // raw + graded variants (premium tier) }); const res = await fetch(`https://api.justtcg.com/v2/cards?${params}`, { headers: { "x-api-key": process.env.JUSTTCG_API_KEY! }, }); const { data } = await res.json(); // price now lives per-region under markets[] const eu = data[0].variants[0].markets.find((m) => m.region === "EU"); console.log(eu?.price, eu?.currency); ``` ```json // GET /v2/cards?game=pokemon®ions=eu,na&graded=include&include=periods.7d,price_history.7d // 200 OK // RateLimit: limit=500000, remaining=499958, reset=... // Link: ; rel="next" { "data": [ { "id": "9b2e4d1a-1111-5v5v-aaaa-000000000000", // card UUID (native) "slug": "pokemon-base-set-charizard-holo-rare", // legacy v1 id "name": "Charizard", "game": { "id": "pokemon", "name": "Pokemon" }, "set": { "id": "base-set-pokemon", "name": "Base Set" }, "number": "4", "rarity": "Rare Holo", "external_ids": { "tcgplayer": "42445", "scryfall": null, "mtgjson": null }, "variants": [ { "id": "d41f-raw-uuid", "slug": "pokemon-base-set-charizard-holo-rare_near-mint_holofoil", "type": "raw", "condition": "Near Mint", "printing": "Holofoil", "language": null, "grading": null, "markets": [ { "region": "EU", // markets[0] = primary market (drives sort/filter) "currency": "EUR", "price": 142.30, // observed EU price; null if none observed "price_status": "current", "last_price": null, "updated_at": 1765635970, "change_24h_pct": -1.8, "periods": { "7d": { "change_pct": -3.1, "avg": 148.0, "min": 140.0, "max": 151.2, "changes_count": 10, "sales_count": 12 } }, "price_history": [ { "t": 1765550000, "p": 150.0 }, { "t": 1765636400, "p": 142.3 } ] }, { "region": "NA", // requested explicitly as the USD fallback reference "currency": "USD", "price": 155.00, "price_status": "current", "last_price": null, "updated_at": 1765635970, "change_24h_pct": -0.9, "periods": { "7d": { "change_pct": -2.4, "avg": 158.0, "min": 151.0, "max": 162.0, "changes_count": 11, "sales_count": 14 } }, "price_history": [ { "t": 1765550000, "p": 160.0 }, { "t": 1765636400, "p": 155.0 } ] } ] }, { "id": "d41f-psa10-uuid", "slug": "d41f-psa10-uuid", "type": "graded", "condition": null, "printing": "Holofoil", "language": null, "grading": { "company": "PSA", "grade": 10, "grade_label": null, "qualifier": null, "canonical": "PSA 10" }, "markets": [ { "region": "EU", "currency": "EUR", "price": null, "price_status": "unpriced", "last_price": null, "updated_at": 1765630000, "change_24h_pct": null, "periods": {}, "price_history": [] }, { "region": "NA", "currency": "USD", "price": 4570.00, "price_status": "current", "last_price": null, "updated_at": 1765630000, "change_24h_pct": 0.4, "periods": { "7d": { "change_pct": 1.2, "avg": 4480.0, "changes_count": 2, "sales_count": 3 } }, "price_history": [] } ] } ] } ] } ``` ## Two time signalsBeta - **`price_history`** is the `{ t, p }` series of _observed market prices_ over time. It is exactly what v1 exposed, so your existing charts keep working, and it is now scoped to each market entry. - **`periods`** holds aggregate statistics (average, min and max, standard deviation, trend, and more), now grouped by time window. The fields line up one-to-one with v1's flat stats and carry the same values, so anything you computed in v1 continues to match. ## Request costBeta Most requests cost the same as v1. Two optional features add to that cost, and they are easy to spot below. If you use neither, you pay the base rate. **Not charged during the beta:** While /v2/cards is in beta, neither surcharge is applied. Every v2 request, including `graded=include` and extra regions, costs the same as the equivalent v1 call. The schedule below is what takes effect at general availability, and it will be announced in the [changelog](https://justtcg.com/docs/changelog) before it switches on. Base rate One region, raw OR graded variants only The same cost as the equivalent v1 call. This is what you pay by default. Two features add a surcharge Both raw & graded+1 Surcharge `graded=include` Returning raw and [graded](https://justtcg.com/docs/api/cards-v2/graded) variants together adds a surcharge. Asking for just one kind (graded=exclude or graded=only) stays at normal cost. Extra regions+1 Surcharge `regions=na,eu,…` Each [region](https://justtcg.com/docs/api/cards-v2/regions) beyond the first is a separate price lookup, so it adds cost. This applies as more regions come online. Surcharges stack: use both features and both apply. ## Migrating from v1Beta v1 does not change. v2 is a new, additional path you can opt into. The defaults already reproduce v1 pricing:`regions` falls back to `NA` and `graded` defaults to `exclude`, so a plain request returns the same raw NA pricing as v1. The identifier rename is covered in full on the [Identifiers](https://justtcg.com/docs/identifiers) page. **What changes for an existing integrationThe call-outs that matter when you adopt /v2/cards** - **`id` is now the UUID.** The old slug moves to a field called `slug`. This is the single biggest rename. - **`regions` defaults to `NA`.** That matches v1's US pricing, and `NA` is the only region live today. - **`price` & `price_history` now live inside `markets[]`.** With `regions=NA`, `markets[0]` is the drop-in replacement. - **There is no more `offset` pagination.** Use the `cursor` from the `Link` header instead. - **There is no standalone `us_price` field.** Request `NA` and read that market entry. - **The flat stat fields move into `periods`.** They are regrouped by time window under `markets[].periods`, with the same values as v1. - **`printing` now holds the print type only.** The `" - "` suffix has been removed, so read `language` separately. - **Absent is not the same as null.** v2 leaves out fields you did not request instead of sending them as `null`. The one exception is a market's `price`. - **Errors now use `application/problem+json`.** Update any parser that keys on `{ error, code }`. ## Frequently asked questionsBeta The questions we are asked most often about the v2 beta: why it exists, when it leaves beta, and why graded and regional coverage is still filling in. **Why is /v2/cards in beta, and who is it for right now?** v2 opened in beta earlier than a typical release because its first users are the stores in the JustTCG Partner Program. Graded prices in v2 are built from recorded sales, and a store can only report a graded sale once its own systems can address graded cards the way v2 does: as first-class variants with stable identifiers. Opening the beta early gives those stores a finished contract to integrate against while the data behind it is still filling in. Developers are welcome to build on v2 today, with the understanding that coverage is still growing and the beta label reflects the data more than the API. See: [JustTCG Partner Program](https://justtcg.com/partner) **When will v2 leave beta?** There is no fixed date. The request and response shape is essentially final, so what gates general availability is data coverage and quality, not the API. Coverage grows as more partner stores integrate and report sales, and we would rather reach GA with a dataset that holds up than with a date on a calendar. The status change will be announced in the changelog. Existing v1 integrations are unaffected either way. See: [Changelog](https://justtcg.com/docs/changelog) **Why does a graded card have no price, and why is graded coverage incomplete?** A graded variant exists for every card, grading company and grade, but its price stays null until a sale of that exact slab has been recorded. Graded prices are built from real sales, not from estimates or multipliers on the raw price, so coverage is deepest for slabs that trade often and thinnest for rare ones. An unpriced variant is not an error: in browse and search responses it carries price: null and price\_status: "unpriced", while a direct lookup of a variant that has never been priced returns a 404 problem response. Coverage fills in as partner stores report sales, and your integration needs no change when it does. See: [Graded cards](https://justtcg.com/docs/api/cards-v2/graded) · [Variant object (v2)](https://justtcg.com/docs/schema/variant#v2) **Why do some graded prices carry an old updated\_at?** updated\_at is when that slab's price was last observed. Graded prices do not expire the way high-value raw prices do, because a slab that trades a few times a year is normal rather than a sign of a stale market. Treat updated\_at as part of the price: if your use case needs freshness, weight or discount older observations in your own logic. See: [Thin markets and price expiry](https://justtcg.com/docs/thin-markets) **How can I get coverage for the cards I care about sooner?** Coverage follows reported sales. If you run a store, or build for one, joining the JustTCG Partner Program is the most direct way to improve it: sales reported by a partner feed graded and regional prices for the region they were sold in, and partners get API access in return. If you are a developer without a store, the best move is to build against the v2 shape now. Nothing in your integration changes when a slab or a region gets its first price. See: [JustTCG Partner Program](https://justtcg.com/partner) **Will the response shape change before general availability?** The shape is finalized and we treat it as stable. The beta label keeps room for a correction if real-world data exposes a problem, and any change that could break a client is announced in the changelog before it takes effect, with what to update. v1 does not change at all, so you can adopt v2 one call at a time and fall back if you need to. See: [Changelog](https://justtcg.com/docs/changelog) · [Migrating from v1](https://justtcg.com/docs/api/cards-v2#v2-migration) **When do EU, UK, JP and other regions come online?** Each region switches on once it has enough locally observed sales to stand on its own. v2 does not convert US prices into other currencies, so a region is not enabled until its local data meets our coverage threshold. Today NA is the only region with data. The regions parameter and the markets array are already shaped for the rest, so a region arriving changes nothing in your integration. Partner stores outside North America directly accelerate their own region. See: [Localized pricing](https://justtcg.com/docs/api/cards-v2/regions) **Can I fetch many graded cards in one request?** Yes, if you name each slab by variant\_id. POST /v2/cards returns graded variants in the v2 shape, priced as slabs. Batch does not accept graded, grading\_company or grade, so to find which grades a card has, use a direct GET lookup first. See: [v2 batch](https://justtcg.com/docs/api/cards-v2/batch) · [Changelog](https://justtcg.com/docs/changelog) **Does v2 show how many times a card has sold?** Yes. Every markets\[\] entry carries sales\_count inside periods: the number of sales recorded for that variant in that market over each window (7d, 30d, 90d, 1y and all\_time), for raw and graded variants alike. It counts sales, not units, so a sale of several copies counts once, and refunded or voided sales are left out. A window appears only when it holds at least one sale, so read a missing window as zero. Next to it, changes\_count is the number of times the sale price changed in the window. Counts are aggregates: individual sale records, such as each sale's price and date, are not part of the API. periods are on by default, and include=periods.30d narrows the response to one window. See: [periods fields](https://justtcg.com/docs/schema/variant#v2-periods) · [Changelog](https://justtcg.com/docs/changelog) **Can I look up sealed product by its barcode?** Yes. Pass the code printed under the barcode as gtin, for example gtin=820650850714. It accepts a 12-digit UPC-A, a 13-digit EAN-13 or JAN, an 8-digit EAN-8 and a 14-digit GTIN-14, and ignores spaces and hyphens. If your app already sends whatever the user scans or types to q, a 12 to 14 digit barcode there is matched the same way and falls back to a normal name search when nothing carries it. Barcodes cover sealed product such as booster boxes, Elite Trainer Boxes and bundles, and coverage is growing. One barcode can belong to more than one listing, so a lookup can return several cards. See: [gtin parameter](https://justtcg.com/docs/api/cards-v2) · [Changelog](https://justtcg.com/docs/changelog) **Do graded or multi-region requests cost more during the beta?** No. While /v2/cards is in beta, the surcharges for graded=include and for extra regions are not applied: every v2 request costs the same as the equivalent v1 call. The Request cost section on this page describes the schedule that takes effect at general availability, so you can plan for it now. Surcharges will not switch on silently; the change is announced in the changelog ahead of time. See: [Request cost](https://justtcg.com/docs/api/cards-v2#v2-cost) --- # Graded cards Beta Graded cards (PSA, BGS, CGC, and more) are one of the biggest additions in v2. Graded cards are simple to work with: they are just **extra entries in the same `variants` array**, not a separate resource. A `type` field tells the two kinds apart, and the table below shows how each kind fills in `condition` and `grading`. Graded cards are **off by default** (`graded=exclude`), so moving from v1 to v2 gives you the same raw-only results you had before. ## Raw vs. graded | type | condition | grading | | --- | --- | --- | | "raw" | A condition, such as "Near Mint" | null | | "graded" | null | A populated grading object | ## The `grading` object Present only when `type` is `"graded"`, and `null` otherwise. Each distinct combination of company, grade, and qualifier is its own **variant**, with its own `id` and `markets`. | Property | Type | Description | | --- | --- | --- | | company | string | The grading company. One of PSA, BGS, CGC, BCCG, BVG, or SGC. | | grade | number \| null | The numeric grade, such as 9 or 10. This is null only for cards graded "Authentic". | | grade\_label | string \| null | A special designation such as "Black Label" or "Pristine", when one applies. Otherwise null. | | qualifier | string \| null | A grading qualifier such as "OC" for off-center. Cards with a qualifier are priced separately. Otherwise null. | | canonical | string | A ready-to-display grade string, such as "PSA 10". | ## Requesting graded cards Three query parameters on [/v2/cards](https://justtcg.com/docs/api/cards-v2) control which graded variants come back. **`graded`** · string · Optional · Default: `exclude` · Values: `exclude`, `only`, `include` Controls which kinds of variants come back: exclude for raw only, only for graded only, or include for both. Note that \`include\` adds a graded surcharge. **`grading_company`** · string · Optional · Values: `PSA`, `BGS`, `CGC`, `BCCG`, `BVG`, `SGC` Returns every grade from a single grading company. This sets graded to only automatically, unless you also pass graded=include. **`grade`** · string[] · Optional Narrows the results to specific grades within the chosen company, for example 9.5,10. **Request cost:** Requesting graded cards on their own (`graded=only`) costs the same as an equivalent v1 call. Requesting raw **and** graded variants together (`graded=include`) adds a surcharge at general availability. During the beta the surcharge is not applied. See [Request cost](https://justtcg.com/docs/api/cards-v2#v2-cost) for the full breakdown. ## Example Every PSA 10 for a card, alongside its raw pricing. ```bash curl "https://api.justtcg.com/v2/cards?card_id=pokemon-base-set-charizard-holo-rare&graded=include&grading_company=PSA&grade=10" \ -H "x-api-key: tcg_your_api_key_here" ``` ```json { "id": "d41f-psa10-uuid", "slug": "d41f-psa10-uuid", "type": "graded", "condition": null, "grading": { "company": "PSA", "grade": 10, "grade_label": null, "qualifier": null, "canonical": "PSA 10" }, "markets": [ { "region": "NA", "currency": "USD", "price": 4570.00, "updated_at": 1765630000 } ] } ``` --- # Localized pricing: the markets array Beta Trading card prices are genuinely local. v2 replaces v1's single price field with a markets array. The EU price of a card is its own real, observed price, not a US price converted into euros. To reflect that, v2 replaces v1's single `price` field with a `markets` array on each variant. There is one entry for each region you request, so you can compare regions in a single response. **Only NA is live today:** Right now **`NA` is the only region with data**. The `regions` parameter and `markets` array are already shaped for multi-region pricing, but EU, UK, JP, and other regions will switch on later as their data matures — this page describes the shape you can build against today. ## The `markets` object | Property | Type | Description | | --- | --- | --- | | region | string | The region this pricing is for. It matches a code you asked for, either a market code like NA or EU, or an ISO country code like MX or CA. | | currency | string | The ISO 4217 currency (for example USD or EUR) that price and every periods value is quoted in. Market codes use the market's main currency; country codes use that country's local currency. | | price | number \| null | The real, observed price in this region, in the currency above. A value of **null** means we have no local data for this region, and it is up to you whether to read another region you requested instead. | | updated\_at | number | When this price was last observed, as a Unix timestamp in seconds. | | change\_24h\_pct | number \| null | Percent change in price over the last 24 hours for this region. In v1 this was called priceChange24hr. | | periods | object | Price statistics grouped by time window (7d, 30d, 90d, 1y, and all\_time). Only the windows you request are included. | | price\_history | array | A time series of observed prices for this region, as { t, p } points. This is the same price history v1 returned, now scoped to each region. | **Regions and the NA default:** `regions` is optional. Leave it out and v2 defaults to North America (`NA`), which is the same US pricing v1 returned. Prices are never converted between currencies. **Primary market rule:** `markets[0]`, the first region in your `regions` list, is the **primary market**. Filtering with `min_price` and sorting with `order_by` apply to this market **only**; the other markets are there for reference. There is no way to sort or filter on a blended figure across regions. **Null prices & fallback:** A market's `price` is either the real price observed in that region or `null`. The server never fills in another region's price and never converts currencies for you. If you want a USD fallback, request `NA` as an extra region and read its entry, for example: `markets[0].price ?? markets.find(m => m.region === "NA").price`. ## Requesting regions One query parameter on [/v2/cards](https://justtcg.com/docs/api/cards-v2) controls which regions come back, in priority order. **`regions`** · string · Optional · Default: `NA` · Values: `NA`, `EU`, `UK`, `JP`, `OCE`, `LATAM`, `OTHER`, `…ISO country codes` A comma-separated list of region codes, listed in priority order (not case sensitive). The first region is your primary market. Currently only NA returns data; the other codes are reserved for regions rolling out later. Each region beyond the first adds to the request cost. **Request cost:** A single region costs the same as an equivalent v1 call. Each region beyond the first is a separate price lookup and adds a surcharge. See [Request cost](https://justtcg.com/docs/api/cards-v2#v2-cost) for the full breakdown. ## Example One card's NA pricing, the only region live today. ```bash curl "https://api.justtcg.com/v2/cards?card_id=pokemon-base-set-charizard-holo-rare®ions=na" \ -H "x-api-key: tcg_your_api_key_here" ``` ```json { "markets": [ { "region": "NA", "currency": "USD", "price": 155.00, "price_status": "current", "last_price": null, "updated_at": 1765635970, "change_24h_pct": -0.9, "periods": { "7d": { "change_pct": -2.4, "avg": 158.0, "min": 151.0, "max": 162.0 } }, "price_history": [{ "t": 1765636400, "p": 155.0 }] } ] } ``` --- # /mcp Beta Add the JustTCG MCP server to your favorite coding agents with a single command. Supports Claude Code, Cursor, OpenCode, VSCode, and more. `GET https://mcp.justtcg.com` ## Install Use the `add-mcp` CLI to automate configuration across LLMs and IDEs at both project and global scope. ```bash npx add-mcp https://mcp.justtcg.com --header "x-api-key: YOUR_API_KEY" ``` Some agents natively detect the connection string format, while others may require the transport layer to be explicitly set. ## Example prompt usages - "Analyze the price trajectory of the latest Pokemon set." - "What is the best performing TCG set released in the last 6 months?" - "Give me a 5 item list of Magic cards that I should consider collecting based on strong performance." ## Troubleshooting - Verify the JustTCG server URL is correctly specified (`https://mcp.justtcg.com`). - Check your agent's explicit MCP configuration JSON for unescaped characters. - In VS Code or Cursor, confirm that the transport layer supports raw HTTP stream handling natively. --- # Card object The Card object holds metadata for a trading card. Pricing lives on its variants array. Current production schema. Returned by every v1 endpoint. ## Properties | Property | Type | Description | | --- | --- | --- | | uuid | string | Stable, canonical identifier for the card (UUID v5). **Recommended** as your primary key — see [Identifiers](https://justtcg.com/docs/identifiers). | | id | string | Legacy human-readable slug. Frequently referred to as `cardId`. Still queryable, but no longer the recommended key. | | name | string | Name of the card. | | game | string | Name of the game the card belongs to (e.g. 'Pokemon'). | | set | string | Unique ID of the set the card belongs to. | | set\_name | string | Name of the set the card belongs to. | | number | string | Card number within the set. | | tcgplayerId | string | TCGplayer product ID. | | mtgjsonId | string | MTGJSON UUID for this card. | | scryfallId | string | Scryfall UUID for this card. | | rarity | string | Rarity of the card (e.g. 'Common', 'Rare', 'Promo'). | | details | string \| null | Additional card-specific details (when applicable). | | variants | Variant\[\] | Array of [Variant](https://justtcg.com/docs/schema/variant) objects with pricing. | Cards do not directly contain pricing. Pricing is stored on each [Variant](https://justtcg.com/docs/schema/variant) within the `variants` array. ## Card IDs Every card carries a stable `uuid` and a legacy human-readable `id` (the `cardId`, structured as `game-set-name-rarity`). Both can be used for direct lookups via the `/cards` endpoint, but we recommend preferring `uuid`. See [Identifiers](https://justtcg.com/docs/identifiers) for the full breakdown and how slugs are composed. Note: multiple variants of the same card may exist, each with its own variant ID. ## Example ```json { "uuid": "f8c3de3d-1fea-5d7c-a8b0-29f63c4c3454", "id": "pokemon-battle-academy-fire-energy-22-charizard-stamped-promo", "name": "Fire Energy (#22 Charizard Stamped)", "game": "Pokemon", "set": "battle-academy-pokemon", "set_name": "Battle Academy", "number": "N/A", "tcgplayerId": "219042", "rarity": "Promo", "details": null, "variants": [ { "uuid": "a1b2c3d4-5e6f-5a7b-8c9d-0e1f2a3b4c5d", "id": "pokemon-battle-academy-fire-energy-22-charizard-stamped-promo_near-mint", "printing": "Normal", "condition": "Near Mint", "price": 4.99, "lastUpdated": 1743100261, "priceStatus": "current", "lastKnownPrice": null } ] } ``` --- # Variant object A variant is a specific condition × printing combination of a card, with its price and statistics. Current production schema. Returned by every v1 endpoint. ## Core | Property | Type | Description | | --- | --- | --- | | uuid | string | Stable, canonical identifier for the variant (UUID v5). **Recommended** as your primary key — see [Identifiers](https://justtcg.com/docs/identifiers). | | id | string | Legacy human-readable slug (the `variantId`). Format: `cardId_condition_printing`. Still queryable, but no longer the recommended key. | | condition | string | Condition of the card (e.g. 'Near Mint', 'Lightly Played'). | | printing | string | Printing type (e.g. 'Normal', 'Foil', '1st Edition'). | | language | string | Language of the card (e.g. 'English', 'Japanese'). Only recorded for non-English printings, so a missing value means English. Filter on it with the `language` parameter on [/cards](https://justtcg.com/docs/api/cards). | | tcgplayerSkuId | string | TCGplayer SKU ID for this variant. | | price | number \| null | Current price in USD: the last price the market reported for this condition. `null` when there is no current market report; `priceStatus` says why. | | lastUpdated | number \| null | Unix timestamp (seconds) of when the price was last observed. For an expired variant this is when the last price was seen. | | priceStatus | string | `current`: a market report is in effect. `expired`: the market stopped reporting this variant and the last price aged out (see [thin markets](https://justtcg.com/docs/thin-markets)), so `price` is `null` and `lastKnownPrice` carries the last observed value. `unpriced`: this condition has never had a price. | | lastKnownPrice | number \| null | The last observed price in USD when `priceStatus` is `expired`; `null` otherwise. The same value is the last point of the variant's price history. | ## 24-hour stats | Property | Type | Description | | --- | --- | --- | | priceChange24hr | number \| null | Percentage price change over the last 24 hours. | ## 7-day stats | Property | Type | Description | | --- | --- | --- | | priceChange7d | number \| null | Percentage price change over the last 7 days. | | avgPrice | number \| null | Average price over the last 7 days. | | priceHistory | array \| null | Array of {p, t} points over the requested priceHistoryDuration. Default 7d. | | minPrice7d | number \| null | Minimum price in the last 7 days. | | maxPrice7d | number \| null | Maximum price in the last 7 days. | | stddevPopPrice7d | number \| null | Population standard deviation of prices over the last 7 days. | | covPrice7d | number \| null | Coefficient of variation for prices over the last 7 days. | | iqrPrice7d | number \| null | Interquartile range of prices over the last 7 days. | | trendSlope7d | number \| null | Linear regression trend slope for prices over the last 7 days. | | priceChangesCount7d | number \| null | Count of distinct price changes in the last 7 days. | ## 30-day stats | Property | Type | Description | | --- | --- | --- | | priceChange30d | number \| null | Percentage price change over the last 30 days. | | avgPrice30d | number \| null | Average price over the last 30 days. | | priceHistory30d | array \| null | Array of historical price points over 30 days. | | minPrice30d | number \| null | Minimum price in the last 30 days. | | maxPrice30d | number \| null | Maximum price in the last 30 days. | | stddevPopPrice30d | number \| null | Population standard deviation over the last 30 days. | | covPrice30d | number \| null | Coefficient of variation over the last 30 days. | | iqrPrice30d | number \| null | Interquartile range over the last 30 days. | | trendSlope30d | number \| null | Trend slope over the last 30 days. | | priceChangesCount30d | number \| null | Count of distinct price changes in the last 30 days. | | priceRelativeTo30dRange | number \| null | Position within the 30-day min/max range, 0..1. | ## 90-day stats | Property | Type | Description | | --- | --- | --- | | priceChange90d | number \| null | Percentage price change over the last 90 days. | | avgPrice90d | number \| null | Average price over the last 90 days. | | minPrice90d | number \| null | Minimum price in the last 90 days. | | maxPrice90d | number \| null | Maximum price in the last 90 days. | | stddevPopPrice90d | number \| null | Population standard deviation over the last 90 days. | | covPrice90d | number \| null | Coefficient of variation over the last 90 days. | | iqrPrice90d | number \| null | Interquartile range over the last 90 days. | | trendSlope90d | number \| null | Trend slope over the last 90 days. | | priceChangesCount90d | number \| null | Count of distinct price changes in the last 90 days. | | priceRelativeTo90dRange | number \| null | Position within the 90-day min/max range, 0..1. | ## Longer-term | Property | Type | Description | | --- | --- | --- | | minPrice1y | number \| null | Minimum price in the last year. | | maxPrice1y | number \| null | Maximum price in the last year. | | minPriceAllTime | number \| null | Lowest price recorded for this variant since tracking began (March 2025). | | minPriceAllTimeDate | string \| null | ISO 8601 timestamp of when the all-time minimum was first reached. | | maxPriceAllTime | number \| null | Highest price recorded for this variant since tracking began (March 2025). | | maxPriceAllTimeDate | string \| null | ISO 8601 timestamp of when the all-time maximum was first reached. | **Thin markets:** `price` is the last price the market recorded, never smoothed or clamped, so a rarely traded condition can carry one sale's price for a long time. A variant priced at $100 or more that goes 14 days without a reported price expires: `price` becomes `null`, `priceStatus` is `"expired"`, and `lastKnownPrice` keeps the value that expired. History and statistics are untouched. [Thin markets and price expiry](https://justtcg.com/docs/thin-markets) covers syncing and how to judge confidence. ## Example ```json { "uuid": "a1b2c3d4-5e6f-5a7b-8c9d-0e1f2a3b4c5d", "id": "pokemon-battle-academy-fire-energy-22-charizard-stamped-promo_near-mint", "condition": "Near Mint", "printing": "Normal", "language": "English", "tcgplayerSkuId": "1234567", "price": 4.99, "lastUpdated": 1743100261, "priceStatus": "current", "lastKnownPrice": null, "priceChange24hr": 0.5, "priceChange7d": -2.1 } ``` --- # Identifiers Updated Every card and variant now carries two identifiers: a stable UUID (recommended) and a human-readable slug (legacy). This page explains both, how slugs are composed, and what changes in v2. **TL;DR:** Start reading the new `uuid` field on cards and variants and use it as your primary key. The existing `id` (a slug) keeps working and remains useful as a human-readable label, but it is no longer the recommended way to reference a card. ## Two identifiers, one card We are migrating identity from slug-based IDs to UUIDs for both cards and variants. Slugs are derived from mutable text (names, sets, rarities), so they are **not stable**. Upstream renames and edge cases caused collisions and duplicates over time. UUIDs are content-addressed and never change. `uuid`Recommended Stable, globally-unique, and deterministic. The canonical key we upsert on internally. Prefer this for storage, joins, and lookups. `f8c3de3d-1fea-5d7c-a8b0-29f63c4c3454` `id`Legacy / slug Human-readable and great for debugging or URLs. Still returned and still queryable, but can change and is not recommended as a primary key. `pokemon-battle-academy-fire-energy-22-charizard-stamped-promo` ## Slugs (legacy, human-readable) A slug is a **composite** identifier: several source fields are individually sanitized, slugified, and joined with dashes. The card slug is built from `game` · `set` · `name` · `rarity`; a variant slug appends the normalized `condition` and `printing` after an underscore. ### Anatomy of a slug Example: `pokemon-battle-academy-fire-energy-22-charizard-stamped-promo` pokemon\-battle-academy\-fire-energy-22-charizard-stamped\-promo - game The game the card belongs to. - set Slugified set name. - name Card name, including parenthetical qualifiers. - rarity Slugified rarity (e.g. common, uncommon, rare, promo). A [variant](https://justtcg.com/docs/schema/variant) slug extends the card slug with its condition and printing, e.g. `…charizard-stamped-promo_near-mint`. **How each segment is generatedAdvanced — only needed if you reconstruct slugs yourself** Every text field passes through the same two steps before being joined. If you reconstruct slugs on your end, mirror this order exactly. 1. **1\. Sanitize special characters.** A handful of characters that slugification would otherwise silently strip are first rewritten to a word, so meaningfully different cards don't collapse to the same slug (see the table below). 2. **2\. Slugify.** Lowercase and trim → apostrophes become dashes → remaining punctuation (`. , ! ? : ; ( )`) is removed → spaces become dashes → any non-word characters are dropped → repeated dashes collapse to one → leading/trailing dashes are trimmed. #### Special-character mapping These substitutions are applied to **all** text fields (name, rarity, etc.) before slugification. They are what keep `Unown (?)` and `Unown (!)`, or the asterisk alt-art markers `Mega Man*` and `Mega Man**`, from colliding. | Character | Replaced with | Example | | --- | --- | --- | | ? | " question" | Unown (?) → …unown-question | | ! | " exclamation" | Unown (!) → …unown-exclamation | | + | " plus" | Energy+ → …energy-plus | | \* | " star" | Mega Man\* → …mega-man-star | #### Condition normalization For variant slugs, the raw condition is first normalized to a canonical value before slugification. Any grade suffix is dropped to its base — `Near Mint (Holofoil)` becomes `Near Mint` — and sealed product collapses to `Sealed`. ```javascript // Sealed / unopened product → "Sealed" // Otherwise, collapse any suffix to the base grade: "Near Mint" // "Near Mint …" → "Near Mint" "Lightly Played" // "Lightly Played …" → "Lightly Played" "Moderately Played"// "Moderately Played …"→ "Moderately Played" "Heavily Played" // "Heavily Played …" → "Heavily Played" "Damaged" // "Damaged …" → "Damaged" ``` ## Preparing for API v2 In v2, UUIDs become the primary `id` on both objects, and today's slug-based IDs move to a dedicated `slug` field. Adopting `uuid` now means a one-line rename when you upgrade. | v1 (today) | v2 | Notes | | --- | --- | --- | | card.id | card.slug | Human-readable slug, now demoted to a secondary field. | | card.uuid | card.id | The stable UUID becomes the canonical card ID. | | variant.id | variant.slug | Human-readable variant slug. | | variant.uuid | variant.id | The stable UUID becomes the canonical variant ID. | Nothing breaks today: v1 responses are unchanged except for the additive `uuid` field. Migrate at your own pace by reading `uuid` now, so the eventual switch to v2 is just `id → slug` and `uuid → id`. --- # Thin markets and price expiry Some cards rarely sell. This page explains what that does to a price, when a price expires, and which fields tell you how far to trust it. It applies to v1 and v2 alike; only the field names differ. **TL;DR:** A price that goes **14 days** without a market report expires if it is $100 or more, or if it has collapsed to under 10% of the variant's own recent prices. Its `price` becomes `null`, its status says `expired`, and the value that expired is kept in `lastKnownPrice` (v1) or `last_price` (v2). Price history and statistics are never changed. ## Why thin markets look odd A variant's `price` is the last price the market recorded for that condition. It is never smoothed or clamped, and we never replace it with a number of our own: a price we cannot stand behind becomes `null`, and the value is kept alongside it. For cards that rarely trade raw, such as vintage chase cards, ultra-rare promos, and most graded grades, a single condition can go months between sales. One mislisted sale can therefore set the price for a long time, and conditions can look inverted (Moderately Played above Near Mint). ## How a price expires Marketplaces eventually correct or age out bad sales and stop reporting a price for that condition altogether. When that happens we do not keep serving the last value forever: a variant priced at $100 or more that goes 14 days without a reported price expires to a `null` price until a new sale is recorded. Expiry has been in effect since 2026-10-01. A second rule covers [collapsed prices](https://justtcg.com/docs/thin-markets#collapsed) at any price level. Expiry changes `price` and nothing else. The variant reports an `expired` status and carries the value that expired. Its last-observed timestamp, price history and window statistics are left untouched. When a new sale arrives, the status returns to `current`. Graded variants never expire, because a slab that trades a few times a year is normal rather than a sign of a stale market. ## Collapsed prices and implausible sales Occasionally a market report collapses for no reason in the card itself: a $1,400 promo reports $0.40 for a day or two and then stops being reported at all. A collapsed value like that is often never corrected, so the $100 floor alone would let it stand indefinitely. **Collapsed prices expire too.** A price that goes 14 days without a market report also expires when it is below **10%** of the median of the variant's own prices over the 180 days before it changed to that value (at least five observations are required). It expires exactly like a high-value price: `price` becomes `null`, the status says `expired`, the collapsed value is kept in `lastKnownPrice` / `last_price`, and nothing else changes. It stays expired until the market reports the variant again. A price the market is still reporting is never expired, however low it is. The rule is applied when each set is next processed, so a matching price can take a day or two past the 14 days to expire. **Implausible sales are left out of v2 periods.** The statistics in `markets[].periods` are computed from individual sales. A single sale priced below 10% or above 10 times the variant's 90-day market average (when that average is at least $5) is held out of every window, the same way a refunded sale is. A sale that is held out is kept on record for review. If a card has no other sales in a window, that window is omitted rather than built from the one implausible sale. Graded and non-USD sales are not screened. Neither rule smooths or adjusts a price. Each one only decides whether a reported value is served. When one is not, you see `null` (or a missing window) instead of a number, and the reported value is kept. ## The fields, in v1 and v2 | Meaning | v1 | v2 | | --- | --- | --- | | Current price (null when expired or never priced) | `price` | `markets[].price` | | Why the price is what it is: current, expired or unpriced | `priceStatus` | `markets[].price_status` | | The value that expired (null unless expired) | `lastKnownPrice` | `markets[].last_price` | | When the price was last observed | `lastUpdated` | `markets[].updated_at` | | Activity in the last 90 days: price changes, and in v2 also sales | `priceChangesCount90d` | `markets[].periods.90d.changes_count, .sales_count` | The status takes one of three values. `current`: a market report is in effect. `expired`: the last price aged out, so the price is `null` and the last observed value is carried alongside it. `unpriced`: this condition has never had a price. Full field references: [Variant object (v1)](https://justtcg.com/docs/schema/variant#v1) and [Variant object (v2)](https://justtcg.com/docs/schema/variant#v2-markets). ```json { "id": "one-piece-card-game-…-secret-rare_near-mint_foil", "price": null, "priceStatus": "expired", "lastKnownPrice": 4400.43, "lastUpdated": 1782360444 } ``` ## Incremental syncs An expiry counts as a price change, so a sync that uses `updated_after` receives the expired variant once, with its `null` price. If your sync assumes every returned price is non-null, handle `null` or branch on the status field. Outside of `updated_after`, a card whose every variant is `null` is omitted from browse responses unless you pass `include_null_prices=true`. ## Judging confidence yourself The response carries what you need: - Treat a variant with **0 price changes in 90 days**, or a last-observed time **older than 14 days**, as low confidence. - Treat a condition priced **above the next-better condition** of the same card as a flag, and clamp it client-side if your use case needs a strict ladder. For the vast majority of the catalog these checks never fire. JustTCG is a sales-data API, not a valuation service: when there is no market activity, we would rather show you that than invent a number. --- # Response format How JustTCG structures a successful response. Pick an API version below. Current production API. One JSON envelope with data, meta, and \_metadata. ## Envelope ```json { "data": [ /* … */ ], "meta": { "total": 1, "limit": 20, "offset": 0, "hasMore": false }, "_metadata": { "apiPlan": "Pro", "apiRequestsRemaining": 49213, "apiDailyRequestsRemaining": 4321, "apiRateLimit": 100 } } ``` ## Fields | Property | Type | Description | | --- | --- | --- | | data | array \| null | The requested data (an array of card objects). | | meta | object \| null | Pagination metadata, if applicable. | | meta.total | integer | Total number of cards available for this query. | | meta.limit | integer | Page size for this query. | | meta.offset | integer | Current offset for these results. | | meta.hasMore | boolean | Whether more pages are available for this query. | | \_metadata | object | API usage metadata included with every response. | | \_metadata.apiPlan | string | Your current API subscription plan name. | | \_metadata.apiRequestLimit | integer | Total monthly requests allowed for your plan. | | \_metadata.apiRequestsUsed | integer | Number of API requests used so far this period. | | \_metadata.apiRequestsRemaining | integer | Requests remaining before reaching the monthly limit. | | \_metadata.apiDailyLimit | integer | Maximum requests allowed per day. | | \_metadata.apiDailyRequestsUsed | integer | Number of requests used today. | | \_metadata.apiDailyRequestsRemaining | integer | Requests remaining for today. | | \_metadata.apiRateLimit | integer | Maximum requests allowed per minute. | | error | string \| null | Detailed error message if an error occurred, otherwise null. | | code | string \| null | Machine-readable error code, if applicable. | --- # Errors Standard HTTP status codes and consistent error response shapes to help you identify and resolve issues quickly. Current production API. Errors return a flat { error, code } object. ## HTTP status codes | Status | Label | When it happens | | --- | --- | --- | | 400 | Bad Request | The request was malformed or missing required parameters. | | 401 | Unauthorized | Missing or invalid API key. | | 403 | Forbidden | Valid API key, but its plan is not active on your account (SUBSCRIPTION\_REQUIRED). details.reactivate\_url resubscribes to that plan, after which the same key works again. | | 404 | Not Found | The requested resource could not be found. | | 429 | Too Many Requests | Rate limit exceeded. Slow down your requests. | | 500 | Server Error | An unexpected error occurred on the server. | ## Error response shape Errors include an `error` message and a machine-readable `code`. ```json { "error": "Invalid game parameter. Must be one of: magic-the-gathering, mtg, pokemon, yugioh, age-of-sigmar, warhammer-40000, union-arena, flesh-and-blood-tcg, disney-lorcana, digimon-card-game, one-piece-card-game", "code": "INVALID_REQUEST" } ``` ## Error codes | Code | Description | | --- | --- | | MISSING\_API\_KEY | The API key is missing from the request. | | INVALID\_API\_KEY | The API key provided is invalid or has been revoked. | | INVALID\_REQUEST | A required parameter is missing or invalid. Check the error message for details. | | RATE\_LIMIT\_EXCEEDED | You have exceeded your per-minute rate limit. Slow down or upgrade your plan. | | DAILY\_LIMIT\_EXCEEDED | You have exceeded your daily usage limit. Wait until reset (00:00 UTC) or upgrade. | | REQUEST\_LIMIT\_EXCEEDED | You have exceeded your monthly usage limit. Wait for the next billing cycle or upgrade. | **Pro tip:** Implement retry logic with exponential backoff for transient server errors (500) and rate limiting (429). --- # Rate limits Rate limits are tied to your subscription plan. Monitor usage in the dashboard. ## Plans | Plan | Monthly | Daily | Per minute | | --- | --- | --- | --- | | Free | 1,000 requests | 100 requests | 10 / min | | Starter | 10,000 requests | 1,000 requests | 50 / min | | Professional | 50,000 requests | 5,000 requests | 100 / min | | Enterprise | 500,000 requests | 50,000 requests | 500 / min | If you exceed your minute rate limit, the API returns `429 Too Many Requests`. If you exceed your monthly limit, you'll need to upgrade your plan to continue. ## Reset times | Plan type | Daily reset | Monthly reset | | --- | --- | --- | | Free | Midnight (00:00) UTC | On the day-of-month the account was created, shortly after 00:00 UTC (the last day of the month when that day doesn't exist). | | Paid | Midnight (00:00) UTC | On successful payment at the start of each billing cycle. Annual plans reset on the same day each month, shortly after the hour (the last day of the month when that day doesn't exist). | **Retry strategy:** Use exponential backoff with jitter when handling 429 responses. Start at ~1s and double up to a sensible cap (~30s); always honor any `Retry-After` header when present. ## Free tier on shared infrastructure **Running on shared infrastructure?** If your backend runs on Cloudflare Workers, AWS Lambda, Vercel, or another serverless or shared‑hosting platform with a **Free** API key, you may occasionally see an `EXCESSIVE_FREE_TIER_USAGE` block. Paid plans are not subject to these shared‑environment protections. Upgrading to any paid plan resolves it. **Why am I seeing EXCESSIVE\_FREE\_TIER\_USAGE on a Free key?Applies only to Free tier traffic from shared cloud environments** Free API keys carry additional abuse protections beyond the per‑plan limits in the table above. In a shared cloud environment, requests from many unrelated Free tier users can be grouped together by the underlying platform, so the combined activity may trigger a temporary block; even when your own key's usage is well within the Free tier. When this happens the API returns: `EXCESSIVE_FREE_TIER_USAGE`. **Resolution:** Move to any paid plan. Paid traffic is exempt from these shared‑environment protections, so your requests are no longer affected by other tenants on the same platform. --- # Build with AI New Everything an AI coding agent or LLM needs to use JustTCG correctly: Markdown for every docs page, llms.txt, a public docs MCP server, the OpenAPI spec, and changelog feeds. ## Markdown for every page Every docs page is available as clean Markdown, without the navigation and page chrome. Append `.md` to any docs URL, or send an `Accept: text/markdown` header to the normal page URL. Each Markdown file starts with front matter (`title`, `description`, `url`, `markdown_url`). ```bash # Append .md to any docs URL curl https://justtcg.com/docs/errors.md # Or ask for Markdown with the Accept header curl -H "Accept: text/markdown" https://justtcg.com/docs/errors ``` Reading in the browser? The **Copy page** menu at the top of each docs page copies the Markdown or opens the page in your AI assistant of choice. ## llms.txt and llms-full.txt Two plain-text files follow the [llms.txt](https://llmstxt.org) convention. [/llms.txt](https://justtcg.com/llms.txt) is a short index of the API and its docs pages. [/llms-full.txt](https://justtcg.com/llms-full.txt) adds a longer overview (plans, licensing, FAQ) and then the full Markdown of every docs page in one file, for agents that want everything in a single fetch. ## Docs MCP server A public, read-only [Model Context Protocol](https://modelcontextprotocol.io) server lets your agent search and read these docs directly. No API key is needed. `POST https://justtcg.com/mcp/docs` ### Tools | Tool | What it does | | --- | --- | | search\_docs | Keyword search across every docs page. Returns the best-matching sections with the page URL, section heading and a snippet. | | get\_doc | Return one docs page as Markdown. Accepts /docs/errors, docs/errors, a full URL, or any of those ending in .md. | | list\_docs | List every docs page with its title, URL, Markdown URL and description. | | get\_changelog | Return API changelog entries, optionally only those on or after a date (since, YYYY-MM-DD). | ### Install ```bash npx add-mcp https://justtcg.com/mcp/docs ``` ```bash claude mcp add --transport http justtcg-docs https://justtcg.com/mcp/docs ``` ```json { "mcpServers": { "justtcg-docs": { "url": "https://justtcg.com/mcp/docs" } } } ``` **Docs, not prices.** The docs server only returns documentation. To query live card prices from an agent, use the [pricing MCP server](https://justtcg.com/docs/api/mcp) below. ## Pricing MCP server To let an agent query card prices, connect the hosted JustTCG pricing MCP server at `https://mcp.justtcg.com`. It is a separate server from the docs one and needs your API key. Setup and example prompts are on the [/mcp](https://justtcg.com/docs/api/mcp) page. ## OpenAPI spec The machine-readable definition of the REST API is published at [https://justtcg.com/docs/swagger.json](https://justtcg.com/docs/swagger.json) (no key needed). Agent frameworks that build tools from OpenAPI can load it directly. See the [OpenAPI (Swagger)](https://justtcg.com/docs/openapi) page for import and client-generation instructions. ## Changelog feeds Useful for agents and scheduled jobs that need to answer "what changed since my last sync?". The same entries are available in three formats, and the docs MCP server's `get_changelog` tool reads them too. - RSS: [/docs/changelog/feed.xml](https://justtcg.com/docs/changelog/feed.xml) - JSON Feed: [/docs/changelog.json](https://justtcg.com/docs/changelog.json) - Markdown: [/docs/changelog.md](https://justtcg.com/docs/changelog.md) ## Add JustTCG to your AGENTS.md or CLAUDE.md Coding agents work best when your repo tells them how to use an API. Paste this into your `AGENTS.md` (or `CLAUDE.md`, `.cursorrules`, or similar) so the agent authenticates, batches, syncs and handles missing prices the way the API expects. ```markdown ## JustTCG API (trading card game pricing) - Base URL: `https://api.justtcg.com/v1`. Authenticate with the `x-api-key` request header. - Read the key from the `JUSTTCG_API_KEY` environment variable. Never hardcode it or commit it. - In JavaScript/TypeScript, prefer the official SDK (`npm install justtcg-js`) over hand-written fetch calls. It reads `JUSTTCG_API_KEY` by default. - Fetch many cards with one `POST /v1/cards` (a JSON array of lookups) instead of looping over `GET /v1/cards`. The array limit depends on the plan: 20 (Free), 100 (Starter, Pro), 200 (Enterprise). Chunk larger lists. - For incremental sync, pass `updated_after` (Unix seconds) and store the time of your last successful sync, instead of re-fetching everything. - Respect rate limits (https://justtcg.com/docs/rate-limits). On `429`, back off exponentially with jitter and honor `Retry-After` when present. - Prices can be `null`. Branch on `priceStatus` (`current`, `expired`, `unpriced`) and use `lastKnownPrice` for expired variants. Never treat `null` as `0`. See https://justtcg.com/docs/thin-markets. - Read the docs as Markdown: append `.md` to any docs URL (https://justtcg.com/docs/errors.md), or connect the docs MCP server at https://justtcg.com/mcp/docs (no key needed). Page index: https://justtcg.com/llms.txt. - Before changing integration code, or when behavior changes unexpectedly, check the changelog JSON feed: https://justtcg.com/docs/changelog.json. ``` Details behind each line: [Quickstart](https://justtcg.com/docs/quickstart), [SDK](https://justtcg.com/docs/sdk), [/cards](https://justtcg.com/docs/api/cards), [Rate limits](https://justtcg.com/docs/rate-limits) and [Thin markets](https://justtcg.com/docs/thin-markets). --- # Examples Drop-in recipes for common integrations: inventory sync, product pages, carts, and collection trackers. ## Price sync for inventory Fetch every card in a set, automatically paginating until done. ```javascript // Price sync for inventory: fetch every card in a set, paginated. const API_KEY = 'tcg_your_api_key_here'; const BASE_URL = 'https://api.justtcg.com/v1'; async function updatePricesForSet(gameId, setId) { let allCards = []; let offset = 0; const LIMIT = 20; let hasMore = true; while (hasMore) { const url = new URL(`${BASE_URL}/cards`); url.searchParams.set('game', gameId); url.searchParams.set('set', setId); url.searchParams.set('limit', String(LIMIT)); url.searchParams.set('offset', String(offset)); const res = await fetch(url, { headers: { 'x-api-key': API_KEY } }); const json = await res.json(); if (!res.ok) throw new Error(json.error ?? 'Request failed'); allCards = allCards.concat(json.data); hasMore = json.data.length === LIMIT; offset += LIMIT; } return allCards; } updatePricesForSet('magic-the-gathering', 'modern-horizons-2-magic-the-gathering'); ``` ## Product page display Batch-fetch multiple cards for a product page in a single request. ```javascript // Product page: batch lookup by tcgplayerId. const res = await fetch('https://api.justtcg.com/v1/cards', { method: 'POST', headers: { 'x-api-key': 'tcg_your_api_key_here', 'Content-Type': 'application/json', }, body: JSON.stringify( ['219042', '25788', '1369'].map((id) => ({ tcgplayerId: id })) ), }); const { data } = await res.json(); data.forEach((card) => { const lowest = Math.min(...card.variants.map((v) => v.price)); console.log(`${card.name} — from $${lowest.toFixed(2)}`); }); ``` ## Shopping cart calculation Get current prices for specific variants in a cart and sum the total. ```javascript // Shopping cart: get up-to-date prices for specific variants. const cart = [ { tcgplayerId: '219042', condition: 'Near Mint', printing: 'Normal' }, { tcgplayerId: '25788', condition: 'Lightly Played', printing: '1st Edition' }, ]; const res = await fetch('https://api.justtcg.com/v1/cards', { method: 'POST', headers: { 'x-api-key': 'tcg_your_api_key_here', 'Content-Type': 'application/json', }, body: JSON.stringify(cart), }); const { data } = await res.json(); const total = data.reduce((sum, c) => sum + c.variants[0].price, 0); console.log(`Cart total: $${total.toFixed(2)}`); ``` ## Collection price tracker (Python) Track current market prices for a personal collection in Python. ```python # Collection price tracker. import os, requests API_KEY = os.environ["JUSTTCG_API_KEY"] BASE_URL = "https://api.justtcg.com/v1" collection = [ {"tcgplayerId": "219042", "condition": "Near Mint", "printing": "Normal"}, {"tcgplayerId": "25788", "condition": "Lightly Played", "printing": "1st Edition"}, ] resp = requests.post( f"{BASE_URL}/cards", headers={"x-api-key": API_KEY, "Content-Type": "application/json"}, json=collection, ) resp.raise_for_status() for item in resp.json()["data"]: v = item["variants"][0] print(f"{item['name']} ({v['condition']}, {v['printing']}): ${v['price']:.2f}") ``` --- # OpenAPI (Swagger) spec OpenAPI 3.0.3 The complete, machine-readable definition of the JustTCG REST API. Import it into Swagger UI or Postman, generate a typed client, or give it to an AI agent. No API key needed to download it. ## Spec URL `GET https://justtcg.com/docs/swagger.json` Format OpenAPI 3.0.3 (JSON) Spec version 1.1.2 Last updated October 19, 2026 Auth `x-api-key` header **Always fetch the live URL.** The spec changes as the API grows (see the [changelog](https://justtcg.com/docs/changelog)). Point your tools at `https://justtcg.com/docs/swagger.json` rather than a saved copy so new parameters show up automatically. ## What the spec covers 6 operations across the stable v1 API and the v2 beta, against the base URL `https://api.justtcg.com`. Paths carry the version prefix. Each links to its full reference page. | Operation | Summary | `operationId` | | --- | --- | --- | | GET[/v1/games](https://justtcg.com/docs/api/games) | List all games | getGames | | GET[/v1/sets](https://justtcg.com/docs/api/sets) | List all sets | getSets | | GET[/v1/cards](https://justtcg.com/docs/api/cards) | Search or Lookup Cards | getCards | | POST[/v1/cards](https://justtcg.com/docs/api/cards) | Batch Card Lookup | batchGetCards | | GET[/v2/cards](https://justtcg.com/docs/api/cards-v2) | Search, browse or look up cards (beta) | getCardsV2 | | GET[/v2/cards/{id}](https://justtcg.com/docs/api/cards-v2) | Get one card by ID (beta) | getCardV2 | Schemas included: `GamesResponse`, `SetsResponse`, `CardsResponse`, `Game`, `Set`, `Card`, `Variant`, `BatchCardRequestItem`, `PaginationMeta`, `UsageMetadata`, `Error`, `CardV2`, `VariantV2`, `GradingV2`, `MarketV2`, `PeriodStatsV2`, `PricePoint`, `PaginationMetaV2`, `CardsV2Response`, `CardV2Response`, `Problem`. Field-level detail is on the [Card](https://justtcg.com/docs/schema/card) and [Variant](https://justtcg.com/docs/schema/variant) schema pages. **v2 operations are beta.** The [/v2/cards](https://justtcg.com/docs/api/cards-v2) operations (graded cards and localized pricing) are tagged `v2 (beta)` and marked `x-beta`. Their shape is finalized but may still change before general availability, so keep production traffic on v1. v2 batch (`POST /v2/cards`) is live but not in the spec yet; see [v2 batch](https://justtcg.com/docs/api/cards-v2/batch). ## Import into Swagger UI, Postman or Insomnia The spec is served with open CORS, so browser-based tools can load it straight from the URL. - **Swagger Editor / Swagger UI:** Open editor.swagger.io, choose File → Import URL, and paste the spec URL. - **Postman:** Import → Link, paste the spec URL, and generate a collection. - **Insomnia:** Create → Import → URL, then paste the spec URL. Then set your API key as the `x-api-key` value. [Get a free key](https://justtcg.com/dashboard) if you don't have one yet. ## Generate a client Any OpenAPI 3 tooling works. Using Node.js or TypeScript? The official [justtcg-js SDK](https://justtcg.com/docs/sdk) is already typed and is usually the faster route. ```bash # Fetch the raw spec, no API key required curl -s https://justtcg.com/docs/swagger.json -o swagger.json ``` ```bash # TypeScript types (openapi-typescript) npx openapi-typescript https://justtcg.com/docs/swagger.json -o justtcg.d.ts ``` ```bash # Any language (OpenAPI Generator), e.g. a Python client npx @openapitools/openapi-generator-cli generate \ -i https://justtcg.com/docs/swagger.json \ -g python \ -o ./justtcg-client ``` ## Using the spec with AI agents Agents that build tools from OpenAPI (custom GPT actions, LangChain toolkits, agent frameworks) can load the spec URL directly. For Claude, Cursor, VS Code and other MCP hosts, the hosted [JustTCG MCP server](https://justtcg.com/docs/api/mcp) gives the same data without any glue code. A plain-text summary of the API for LLMs is at [/llms.txt](https://justtcg.com/llms.txt). ## FAQ Does JustTCG have an OpenAPI or Swagger spec? Yes. JustTCG publishes a public OpenAPI 3.0.3 (Swagger) specification at https://justtcg.com/docs/swagger.json. No API key is needed to download it; you only need a key to call the API itself. Is the JustTCG OpenAPI spec kept up to date? Yes. The spec is updated alongside the API and the documentation, and notable changes are recorded in the changelog. Always fetch it from the canonical URL rather than keeping a copy, so tools and agents pick up new parameters automatically. Does the OpenAPI spec cover the v2 endpoints? Yes. Alongside the stable v1 API (/v1/games, /v1/sets and /v1/cards, including batch POST /v1/cards), the spec describes the /v2/cards beta: GET /v2/cards and GET /v2/cards/{id}, with graded variants, the per-region markets array, cursor pagination and problem+json errors. v2 operations are tagged "v2 (beta)" and marked x-beta. v2 batch (POST /v2/cards) is live but not in the spec yet; it is documented at justtcg.com/docs/api/cards-v2/batch. How do I authenticate requests made from the spec? The spec declares an apiKey security scheme: send your key in the x-api-key header. In Swagger UI, Postman or Insomnia, set it once as the API key for the collection and every request inherits it. --- # Commercial use guidelines Plain-English answers about what you can build with JustTCG data. If you're on a paid plan and you're building your own product, the answer is almost always yes. **The short version:** On a paid plan, you can display our prices to your users, derive your own analytics from them, store them, and charge for the product you build. What you can't do is hand the raw data to someone else, whether as a feed, an export, or a proxied endpoint. ## What you can do These are all explicitly permitted on any **paid** subscription tier. You do not need to ask us first, and we do not issue written confirmation of these terms: the published Terms are your written license. - Show current prices, price history, and percentage changes to your users. - Build charts, sparklines, trend indicators, and market dashboards. - Build portfolio and collection trackers that value a user's cards over time. - Calculate your own derived metrics, like index values, spreads, movers, and alerts. - Cache responses server-side and store price points to power your app's features. - Combine JustTCG data with other market data you lawfully obtain. - Charge your users money for the product you build. ## What you can't do There is essentially one boundary, stated a few different ways: don't become a distribution channel for the raw data. - Resell, redistribute, or sub-license the raw data as a standalone feed. - Proxy or re-expose JustTCG endpoints so third parties can query them through you. - Publish or sell bulk exports of stored price data as a dataset. - Build a pricing API that substitutes for JustTCG. - Share one API key across separate businesses or products. **Enforcement:** Reselling raw data or standing up an API substitute is grounds for immediate termination under [Section 7.3](https://justtcg.com/terms) of the Terms. We'd much rather talk first, so if you're unsure whether your design crosses the line, ask us before you build it. ## Free tier The free tier is for personal, non-commercial use: prototyping, learning, side projects you aren't shipping to users. The commercial rights on this page attach to paid plans. If you're putting a product in front of other people, you need a paid plan, regardless of how few requests it makes. ## Caching and storage **There is no maximum retention period.** Cache aggressively, since it's good for your latency and good for your quota. Store price points for as long as your app needs them: a portfolio tracker that keeps five years of a user's collection history is exactly the use case this permits. The one condition is _purpose_. Stored data powers _your_ application. It doesn't become a dataset you can export, publish, or sell, and that stays true after your subscription ends: you can't use an accumulated archive to run a service for others once you stop paying for the source. ## Attribution Attribution is **appreciated but not required** on paid tiers. If you'd like to credit us, any of these work: ```text Market data provided by JustTCG Pricing by JustTCG Prices via JustTCG (justtcg.com) ``` As a link, in a footer or tooltip near the prices: ```html Market data provided by JustTCG ``` Please don't alter the JustTCG name or imply that we endorse, certify, or partner with your product unless you're in the [JustTCG Partner Program](https://justtcg.com/partner). ## What the numbers actually mean If you're displaying our prices to end users, it helps to know what you're showing them. JustTCG prices are **market-driven observations**, not listing prices and not a number we set. Two streams feed our data: aggregated online marketplace activity, and real physical sales from verified local game stores in the [JustTCG Partner Program](https://justtcg.com/partner), added as each store goes live. For how prices behave on cards that rarely trade, see [thin markets and price expiry](https://justtcg.com/docs/thin-markets). Practical implication for your UI: a JustTCG price is a market observation, not a quote or an offer. Describing it as "market value" or "market price" is accurate. Describing it as the price a specific seller will honor is not. **Still unsure?** Describe what you're building and we'll tell you plainly whether it's in bounds. Practically every question we get turns out to be a yes. This page is a plain-English summary for convenience. The [Terms of Service](https://justtcg.com/terms) are the binding agreement, and Section 7 controls in the event of any conflict. --- # Changelog Every change to the JustTCG API, newest first: new endpoints, parameters and games, behavior changes, deprecations and breaking changes. Each entry lists its category, whether it is breaking, and the endpoints it affects. Entries marked Breaking: yes can break existing integrations. ## 2026-10-19 — Barcode lookup for sealed product on /v2/cards Category: added · Breaking: no · Endpoints: GET /v2/cards Link: https://justtcg.com/docs/changelog#2026-10-19-barcode-lookup-for-sealed-product-on-v2-cards Look up sealed product by its retail barcode with the new `gtin` parameter: `GET /v2/cards?gtin=820650850714`. It accepts a 12-digit UPC-A, a 13-digit EAN-13 or JAN, an 8-digit EAN-8 and a 14-digit GTIN-14, and ignores spaces and hyphens. A barcode sent as `q` (12 to 14 digits) is now matched the same way, then falls back to a normal name search if no product carries it, so apps that pass scanner input straight to `q` get the product instead of an empty result. One barcode can match several listings, so `data` may hold more than one card. See [v2 identifiers](https://justtcg.com/docs/api/cards-v2). > **Note:** Coverage starts with sealed Magic: The Gathering, Pokémon and Yu-Gi-Oh! product and will grow. A code with an invalid check digit returns `400`; a valid code we don't recognize returns `404`. ## 2026-10-19 — changes_count on /v2/cards now counts price changes Category: changed · Breaking: no · Endpoints: GET /v2/cards, POST /v2/cards Link: https://justtcg.com/docs/changelog#2026-10-19-changes-count-on-v2-cards-now-counts-price-changes `markets[].periods..changes_count` now counts the number of times the sale price changed during the window. Five sales all at the same price count 0. Until now it carried the same value as `sales_count`, which is unchanged. See [periods fields](https://justtcg.com/docs/schema/variant#v2-periods). ## 2026-10-19 — Sales counts on /v2/cards Category: added · Breaking: no · Endpoints: GET /v2/cards, POST /v2/cards Link: https://justtcg.com/docs/changelog#2026-10-19-sales-counts-on-v2-cards Every window in `markets[].periods` now carries `sales_count`: the number of sales recorded for the variant in that market over the window (`7d`, `30d`, `90d`, `1y`, `all_time`). It is on browse, search, direct lookups and `POST /v2/cards`, for raw and graded variants, and follows `include=periods[.]` like every other period statistic. It counts sales, not units, and leaves out refunded and voided sales. A window with no recorded sales is omitted, so read a missing window as zero. See [periods fields](https://justtcg.com/docs/schema/variant#v2-periods). > **Note:** `changes_count` keeps working and carries the same value; its description has been corrected, since in v2 it was always a count of sales rather than of price changes. Individual sale records are not exposed. ## 2026-10-12 — All-time price statistics now cover full history Category: changed · Breaking: no · Endpoints: GET /v1/cards, POST /v1/cards Link: https://justtcg.com/docs/changelog#2026-10-12-all-time-price-statistics-now-cover-full-history v1 `minPriceAllTime` / `maxPriceAllTime` were computed over the trailing year only, so they always matched `minPrice1y` / `maxPrice1y`. They now cover everything recorded since tracking began in March 2025, and existing values have been backfilled. `minPriceAllTimeDate` / `maxPriceAllTimeDate` now give the date the extreme was first reached; previously a price that stayed at its extreme was stamped with its most recent update. > **Note:** Expect all-time lows to drop and highs to rise for many variants, and their dates to move earlier. The 1y fields are unchanged. ## 2026-10-12 — v2 batch (POST /v2/cards) documented Category: added · Breaking: no · Endpoints: POST /v2/cards Link: https://justtcg.com/docs/changelog#2026-10-12-v2-batch-post-v2-cards-documented `POST /v2/cards` fetches many cards in one request and returns them in the v2 schema. Send a JSON array of lookup objects with snake_case keys (`card_id`, `variant_id`, `tcgplayer_id`, `tcgplayer_sku_id`, `mtgjson_id`, `scryfall_id`); `include`, `updated_after` and `regions` go on the query string. See [v2 batch](https://justtcg.com/docs/api/cards-v2/batch). > **Note:** v1-style camelCase items (`cardId`) return `400` on v2. The v2 lookup parameter for a SKU is `tcgplayer_sku_id`; earlier docs listed it as `tcgplayer_sku`. ## 2026-10-05 — Price status and last known price on every variant Category: added · Breaking: no · Endpoints: GET /v1/cards, POST /v1/cards, GET /v2/cards, POST /v2/cards Link: https://justtcg.com/docs/changelog#2026-10-05-price-status-and-last-known-price-on-every-variant Since 2026-10-01 a variant priced at $100 or more that goes 14 days without a reported price expires to a `null` price (see [Thin markets](https://justtcg.com/docs/thin-markets)). The response now says so explicitly. v1 variants carry `priceStatus` (`current`, `expired`, `unpriced`) and `lastKnownPrice`; v2 `markets[]` entries carry `price_status` and `last_price`. `lastUpdated` / `updated_at` is defined as when the price was last observed, so for an expired variant it is when the last price was seen. Price history and window statistics are never modified by an expiry. > **Note:** An expiry now counts as a price change: `updated_after` returns the expired variant once, with its `null` price, without `include_null_prices=true`. If your incremental sync assumed every returned price is non-null, handle `null` (or branch on `priceStatus`). ## 2026-09-22 — OpenAPI (Swagger) spec now covers /v2/cards Category: changed · Breaking: no · Endpoints: GET /v2/cards Link: https://justtcg.com/docs/changelog#2026-09-22-openapi-swagger-spec-now-covers-v2-cards The public [OpenAPI spec](https://justtcg.com/docs/openapi) at `https://justtcg.com/docs/swagger.json` (now version 1.1.0) describes the `/v2/cards` beta alongside v1: `GET /v2/cards` and `GET /v2/cards/{id}`, with graded variants, the per-region `markets` array, cursor pagination headers, and `application/problem+json` errors. v2 operations are tagged `v2 (beta)` and marked `x-beta: true`. > **Note:** The spec's server URL is now `https://api.justtcg.com` and every path carries its version prefix (`/v1/cards` rather than `/cards`). The URLs clients call are unchanged, but if you generated a client from the old spec, regenerate it. v2 batch is not live yet, so it is not in the spec. ## 2026-09-11 — New Games: Palworld OFFICIAL CARD GAME and Cyberpunk TCG Category: added · Breaking: no · Endpoints: GET /v1/cards, GET /v1/sets, GET /v1/games Link: https://justtcg.com/docs/changelog#2026-09-11-new-games-palworld-official-card-game-and-cyberpunk-tcg Added support for two new games: Palworld OFFICIAL CARD GAME (`palworld-official-card-game`) and Cyberpunk TCG (`cyberpunk-tcg`). ## 2026-09-01 — Filter variants by language Category: added · Breaking: no · Endpoints: GET /v1/cards, GET /v2/cards Link: https://justtcg.com/docs/changelog#2026-09-01-filter-variants-by-language Added the `language` query parameter to `GET /v1/cards` and `GET /v2/cards`. Pass one or more languages, comma-separated, to narrow the `variants` array on each returned card: `?language=Japanese` or `?language=English,Japanese`. Values are case-insensitive, and an unrecognized value returns an error rather than being ignored. Accepted values are English, Japanese, French, German, Spanish, Italian, Chinese (S), Chinese (T), Portuguese, Russian, and Korean. See the [/v1/cards](https://justtcg.com/docs/api/cards) and [/v2/cards](https://justtcg.com/docs/api/cards-v2) references. > **Note:** This filters variants, not cards. Result counts and pagination are unchanged whether or not you send it, which means a card with no variant in the language you asked for is still returned, with an empty `variants` array. Filter those out client-side if you only want matches. Variants with no language recorded are English. ## 2026-07-27 — Commercial use rights clarified Category: changed · Breaking: no · Endpoints: none Link: https://justtcg.com/docs/changelog#2026-07-27-commercial-use-rights-clarified Expanded **Section 7** of the [Terms of Service](https://justtcg.com/terms) to spell out what paid subscribers may do with the data: display prices and history to end users, derive your own analytics and valuations, cache and store price points with no retention limit, and combine our data with other lawful sources. The boundary is unchanged (no reselling raw data and no building an API substitute), but it's now stated explicitly rather than implied. Also published a plain-English [Commercial Use Guidelines](https://justtcg.com/docs/commercial-use) page. > **Note:** No rights were removed. If you're on a paid plan and building your own product, this change only clarifies what was already intended. The free tier remains personal, non-commercial use. ## 2026-07-09 — /v2/cards is now in Beta Category: added · Breaking: no · Endpoints: GET /v2/cards Link: https://justtcg.com/docs/changelog#2026-07-09-v2-cards-is-now-in-beta Opened the beta of `/v2/cards`, see the [/v2/cards reference](https://justtcg.com/docs/api/cards-v2). Headline changes: **graded cards** as first-class variants, **multi-region localized pricing** via a per-variant `markets` array, an optional `regions` parameter (defaults to `NA`), `snake_case` everywhere, cursor pagination (RFC 8288 `Link` header), and `application/problem+json` errors. v2 covers the `/cards` endpoint only; `/games` and `/sets` stay on v1. > **Note:** Beta: the contract is finalized but may still change before GA. v1 is unchanged and remains the default for every endpoint; adopt v2 per call at your own pace. ## 2026-06-08 — UUIDs added to both Card and Variant objects. Category: added · Breaking: no · Endpoints: GET /v1/cards, POST /v1/cards Link: https://justtcg.com/docs/changelog#2026-06-08-uuids-added-to-both-card-and-variant-objects Added `uuid` to both Card and Variant objects. This provides a stable, globally unique identifier for each entity, independent of the human-readable `id`. We recommend using this as your primary key moving forward. API v2 will use UUIDs as the primary `id` field. The slug-based `id` will still be supported for backwards compatibility. ## 2026-04-15 — Price History [1 Year Duration] Category: added · Breaking: no · Endpoints: GET /v1/cards Link: https://justtcg.com/docs/changelog#2026-04-15-price-history-1-year-duration Added `1y` as a new allowed value for the `priceHistoryDuration` query parameter on the `GET /cards` endpoint. When requested, the `priceHistory` array on each variant returns one data point per day over the trailing 365 days, sourced from a daily materialized aggregate. Existing values (`7d`, `30d`, `90d`, `180d`) are unchanged, and the default remains `7d`. ## 2026-02-25 — min_price Parameter Added to /cards Endpoint Category: added · Breaking: no · Endpoints: GET /v1/cards Link: https://justtcg.com/docs/changelog#2026-02-25-min-price-parameter-added-to-cards-endpoint Added the `min_price` optional query parameter to the `GET /cards` endpoint. This allows users to filter results by a minimum market price (e.g., `min_price=1.00`), significantly reducing noise from low-value cards in analytics and trending lists. ## 2026-02-19 — MCP Endpoint Added Category: added · Breaking: no · Endpoints: MCP Link: https://justtcg.com/docs/changelog#2026-02-19-mcp-endpoint-added The MCP endpoint provides comprehensive pricing data to your agentic workflows. ## 2026-02-17 — UniVersus Game Support Added Category: added · Breaking: no · Endpoints: GET /v1/cards, GET /v1/sets, GET /v1/games Link: https://justtcg.com/docs/changelog#2026-02-17-universus-game-support-added Added support for UniVersus. Use `universus` as the game query parameter to access UniVersus card data. ## 2025-12-29 — Sorcery Game Support Added Category: added · Breaking: no · Endpoints: GET /v1/cards, GET /v1/sets, GET /v1/games Link: https://justtcg.com/docs/changelog#2025-12-29-sorcery-game-support-added Added support for Sorcery: Contested Realm. Use `sorcery-contested-realm` as the game query parameter to access Sorcery card data. ## 2025-12-19 — 90-Day and 180-Day Price History Support Added Category: changed · Breaking: no · Endpoints: GET /v1/cards Link: https://justtcg.com/docs/changelog#2025-12-19-90-day-and-180-day-price-history-support-added Added support for 90d and 180d price history. Deprecated `priceHistory30d` in the process. Use query param `priceHistoryDuration` to select desired durations (7d, 30d, 90d, 180d). Defaults to 7d. `priceHistory30d` will be removed in a future major release. ## 2025-12-15 — New Games: hololive OFFICIAL CARD GAME and Dragon Ball Super: Fusion World Category: added · Breaking: no · Endpoints: GET /v1/cards, GET /v1/sets, GET /v1/games Link: https://justtcg.com/docs/changelog#2025-12-15-new-games-hololive-official-card-game-and-dragon-ball-super-fusion-world Added support for two new games: hololive OFFICIAL CARD GAME (`hololive-official-card-game`) and Dragon Ball Super: Fusion World (`dragon-ball-super-fusion-world`). ## 2025-12-13 — Game and Set Value Index Fields Added Category: added · Breaking: no · Endpoints: GET /v1/games, GET /v1/sets Link: https://justtcg.com/docs/changelog#2025-12-13-game-and-set-value-index-fields-added Added `game_value_usd` to `/games` and `set_value_usd` to `/sets`. These represent the total value of all cards in the game/set, calculated by taking the highest price of any variant for each card. ## 2025-12-09 — include_null_prices Parameter Added Category: added · Breaking: no · Endpoints: GET /v1/cards Link: https://justtcg.com/docs/changelog#2025-12-09-include-null-prices-parameter-added Added the `include_null_prices` boolean query parameter to `/cards`. When `true`, the response includes cards that don't have any pricing data. Default `false`. ## 2025-12-08 — Card Number Filter Added to /cards Category: added · Breaking: no · Endpoints: GET /v1/cards Link: https://justtcg.com/docs/changelog#2025-12-08-card-number-filter-added-to-cards Added the `number` query parameter to `/cards` so you can filter cards by their specific card number within a set. ## 2025-11-21 — Introduced sealed_count and variants_count Fields Category: changed · Breaking: no · Endpoints: GET /v1/sets, GET /v1/games Link: https://justtcg.com/docs/changelog#2025-11-21-introduced-sealed-count-and-variants-count-fields Fixed card counts in `/sets` and `/games` to exclude sealed products. Introduced `sealed_count` on both endpoints and `variants_count` on `/sets`. ## 2025-11-19 — updated_after Parameter Added to /cards Category: added · Breaking: no · Endpoints: GET /v1/cards, POST /v1/cards Link: https://justtcg.com/docs/changelog#2025-11-19-updated-after-parameter-added-to-cards Added `updated_after` to both `GET /cards` and `POST /cards` (Batch). Filters results to variants whose pricing was updated at or after the provided Unix timestamp. Cannot be combined with the search query parameter (`q`). ## 2025-10-27 — External Identifiers Added Category: added · Breaking: no · Endpoints: GET /v1/cards, POST /v1/cards Link: https://justtcg.com/docs/changelog#2025-10-27-external-identifiers-added Added support for querying cards by MTGJSON and Scryfall IDs. Variants can be queried using TCGplayer SKU IDs. ## 2025-10-17 — Set Release Dates Added to /sets Category: added · Breaking: no · Endpoints: GET /v1/sets Link: https://justtcg.com/docs/changelog#2025-10-17-set-release-dates-added-to-sets The `/sets` endpoint now includes the release date of each set. ## 2025-09-30 — Set IDs Standardized and set_name Added to Card Object Category: changed · Breaking: no · Endpoints: GET /v1/cards, GET /v1/sets Link: https://justtcg.com/docs/changelog#2025-09-30-set-ids-standardized-and-set-name-added-to-card-object Set IDs standardized to lowercase-hyphenated format including the game name. Added `set_name` to the card object. ## 2025-09-29 — Payload Reduction Parameters Added Category: added · Breaking: no · Endpoints: GET /v1/cards Link: https://justtcg.com/docs/changelog#2025-09-29-payload-reduction-parameters-added Added `include_price_history` and `include_statistics` query parameters to `/cards`. Available windows: `7d`, `30d`, `90d`, `allTime`. ## 2025-09-23 — JavaScript and TypeScript SDKs Released Category: added · Breaking: no · Endpoints: none Link: https://justtcg.com/docs/changelog#2025-09-23-javascript-and-typescript-sdks-released Released the official JS / TS SDKs. Install via npm: `npm install justtcg-js`. ## 2025-08-26 — 90-Day Analytics on /cards Category: added · Breaking: no · Endpoints: GET /v1/cards Link: https://justtcg.com/docs/changelog#2025-08-26-90-day-analytics-on-cards Added a suite of 90-day statistical fields for deeper quarterly market analysis. > **Note:** Statistical fields only — the full point-by-point `priceHistory90d` is deferred to a future major release. ## 2025-08-12 — Language Field Added to Variant Object Category: added · Breaking: no · Endpoints: GET /v1/cards, POST /v1/cards Link: https://justtcg.com/docs/changelog#2025-08-12-language-field-added-to-variant-object Added `language` to the variant object. ## 2025-08-10 — New Games: Grand Archive TCG and Pokemon Japan Category: added · Breaking: no · Endpoints: GET /v1/cards, GET /v1/sets, GET /v1/games Link: https://justtcg.com/docs/changelog#2025-08-10-new-games-grand-archive-tcg-and-pokemon-japan Added support for Grand Archive TCG (`grand-archive-tcg`) and Pokemon Japan (`pokemon-japan`). ## 2025-08-01 — Multi-Condition and Multi-Printing Support Category: added · Breaking: no · Endpoints: GET /v1/cards Link: https://justtcg.com/docs/changelog#2025-08-01-multi-condition-and-multi-printing-support The `/cards` endpoint now accepts `condition` and `printing` query parameters with multiple comma-separated values, e.g. `/cards?condition=NM,LP&printing=First Edition,Unlimited`. ## 2025-07-11 — [BREAKING] Card ID Standardization Completed Category: removed · Breaking: yes · Endpoints: GET /v1/cards, POST /v1/cards Link: https://justtcg.com/docs/changelog#2025-07-11-card-id-standardization-completed The `id` format for `cards` and `variants` now includes the card's rarity. Old format `game-set-name`, new format `game-set-name-rarity`. All previously stored IDs are invalid and must be re-fetched. ## 2025-07-04 — OrderBy Parameters Added Category: added · Breaking: no · Endpoints: GET /v1/cards Link: https://justtcg.com/docs/changelog#2025-07-04-orderby-parameters-added Added `orderBy` (`price`, `24h`, `7d`, `30d`) and `order` (`asc`, `desc`) for sorting. ## 2025-07-01 — [BREAKING] Card ID Standardization Category: removed · Breaking: yes · Endpoints: GET /v1/cards, POST /v1/cards Link: https://justtcg.com/docs/changelog#2025-07-01-card-id-standardization Card IDs now include the full game name. Some IDs with invalid characters have been standardized. ## 2025-06-14 — Rate Limit Policy Update Category: changed · Breaking: no · Endpoints: none Link: https://justtcg.com/docs/changelog#2025-06-14-rate-limit-policy-update API rate limits adjusted for free users. See the Rate Limits page for details. ## 2025-05-15 — Deprecated /variants and /cards-search Category: deprecated · Breaking: no · Endpoints: GET /v1/cards Link: https://justtcg.com/docs/changelog#2025-05-15-deprecated-variants-and-cards-search Their functionality is merged into `/cards`. ## 2025-04-02 — v1 API Launched Category: added · Breaking: no · Endpoints: GET /v1/cards, GET /v1/sets, GET /v1/games Link: https://justtcg.com/docs/changelog#2025-04-02-v1-api-launched Version 1 introduces initial endpoints and card structure. --- # Batch requests Beta Fetch many cards in one call, returning the v2 response shape. `POST /v2/cards` works like [POST /cards](https://justtcg.com/docs/api/cards#post) in v1: send a JSON array of lookup objects and get many cards back in a single request. Every card comes back in the [v2 schema](https://justtcg.com/docs/api/cards-v2) (UUID-first identity and the per-region `markets` array), not the v1 shape. The maximum array length is the same per-plan limit as v1; see [per-request limits](https://justtcg.com/docs/api/cards#per-request-limits). **Body keys are snake\_case:** v2 batch items use `card_id`, `variant_id`, `tcgplayer_sku_id` and so on. A v1-style item such as `{"cardId": "..."}` names no identifier and returns a `400`. ## Lookup items Each item must name at least one identifier. If an item names several, the most specific one wins, in this order: `variant_id`, `tcgplayer_sku_id`, `tcgplayer_id`, `mtgjson_id`, `scryfall_id`, `card_id`. This is the same rule `GET /v2/cards` applies. **`card_id`** · uuid | slug · Optional One card. Accepts a UUID or a legacy v1 slug. **`variant_id`** · uuid | slug · Optional One variant. A graded variant resolves here too, priced as a slab. **`tcgplayer_id`** · string · Optional One card by its TCGplayer product ID. **`tcgplayer_sku_id`** · string · Optional One variant by its TCGplayer SKU ID. **`mtgjson_id`** · string · Optional One card by its MTGJSON ID. **`scryfall_id`** · string · Optional One card by its Scryfall ID. ## Query parameters `include`, `updated_after` and `regions` go on the query string and apply to the whole batch, with the same meaning as on [GET /v2/cards](https://justtcg.com/docs/api/cards-v2). A per-item `updated_after` is rejected with a `400`; use the query parameter instead. ## Example ```bash curl -X POST "https://api.justtcg.com/v2/cards?include=periods.30d" \ -H "x-api-key: tcg_your_api_key_here" \ -H "Content-Type: application/json" \ -d '[ { "card_id": "pokemon-base-set-charizard-holo-rare", "condition": "NM" }, { "tcgplayer_sku_id": "1247795" }, { "variant_id": "003b8757-d59a-5ec8-baa1-cf14942feed4" } ]' ``` ## Response The body is `{ "data": Card[], "meta": { "count": n } }`. Variants are grouped under their card, so two items that name variants of the same card return one card. Items that match nothing are left out without an error, and duplicates collapse. `meta.count` counts the cards in `data`, not the items you sent, so do not use it as a completeness check: compare the returned IDs against your request instead. **Graded cards in batch:** Batch returns a graded variant only when it already has a price, for example when you name it by `variant_id`. It does not accept `graded`, `grading_company` or `grade`. To request a specific grade, use [a direct lookup](https://justtcg.com/docs/api/cards-v2/graded).