{
  "openapi": "3.0.3",
  "info": {
    "title": "JustTCG API",
    "description": "Real-time pricing data for trading card games including Magic: The Gathering, Pokémon, Yu-Gi-Oh!, Lorcana, and more.\n\n**Authentication**: All requests require an API key via the `x-api-key` header.\n\n**Versions**: `/v1/*` is the stable API and the recommended choice for production. `/v2/cards` is in **beta**: graded cards (PSA, BGS, CGC and more) as first-class variants, and per-region pricing in a `markets` array. v2 covers cards only; `/games` and `/sets` stay on v1. v2 batch lookups are not live yet, so use `POST /v1/cards`.",
    "version": "1.1.0",
    "x-last-modified": "2026-09-22",
    "contact": {
      "name": "JustTCG Support",
      "url": "https://justtcg.com/support"
    }
  },
  "externalDocs": {
    "description": "JustTCG API documentation and OpenAPI (Swagger) spec guide",
    "url": "https://justtcg.com/docs/openapi"
  },
  "servers": [
    {
      "url": "https://api.justtcg.com",
      "description": "Production API. Paths carry the version prefix (/v1, /v2)."
    }
  ],
  "tags": [
    {
      "name": "v1",
      "description": "Stable API. Recommended for production.",
      "externalDocs": {
        "url": "https://justtcg.com/docs/api/cards"
      }
    },
    {
      "name": "v2 (beta)",
      "description": "Beta. Graded variants and multi-region localized pricing. The shape is finalized but may still change before general availability.",
      "externalDocs": {
        "url": "https://justtcg.com/docs/api/cards-v2"
      }
    }
  ],
  "security": [
    {
      "ApiKeyAuth": []
    }
  ],
  "paths": {
    "/v1/games": {
      "get": {
        "summary": "List all games",
        "description": "Retrieve a list of all supported trading card games and their market indices.",
        "operationId": "getGames",
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GamesResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "tags": ["v1"]
      }
    },
    "/v1/sets": {
      "get": {
        "summary": "List all sets",
        "description": "Retrieve a list of all sets, optionally filtered by game or search query.",
        "operationId": "getSets",
        "parameters": [
          {
            "name": "game",
            "in": "query",
            "description": "Filter by game ID (e.g., `mtg`, `pokemon`).",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "q",
            "in": "query",
            "description": "Search query for finding sets by name.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "orderBy",
            "in": "query",
            "description": "Field to sort by.",
            "schema": {
              "type": "string",
              "enum": ["name", "release_date"],
              "default": "name"
            }
          },
          {
            "name": "order",
            "in": "query",
            "description": "Sort order.",
            "schema": {
              "type": "string",
              "enum": ["asc", "desc"],
              "default": "desc"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SetsResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "tags": ["v1"]
      }
    },
    "/v1/cards": {
      "get": {
        "summary": "Search or Lookup Cards",
        "description": "Retrieve cards and variant prices. Supports filtering by various IDs (TCGPlayer, MTGJSON, etc.) or search queries.\n\n**Note**: Identifier parameters take precedence over search queries.",
        "operationId": "getCards",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "description": "General search query string.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "game",
            "in": "query",
            "description": "Filter by game ID.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "set",
            "in": "query",
            "description": "Filter by set ID.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "cardId",
            "in": "query",
            "description": "Slug-based ID or UUID of the card.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "variantId",
            "in": "query",
            "description": "Slug-based ID or UUID of the variant.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "tcgplayerId",
            "in": "query",
            "description": "TCGplayer Product ID.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "mtgjsonId",
            "in": "query",
            "description": "MTGJSON UUID.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "scryfallId",
            "in": "query",
            "description": "Scryfall UUID.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "tcgplayerSkuId",
            "in": "query",
            "description": "TCGPlayer SKU ID (Variant level).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "number",
            "in": "query",
            "description": "Filter by card number within a set.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "printing",
            "in": "query",
            "description": "Filter by printing type (e.g., \"Normal\", \"Foil\"). Comma-separated for multiple.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "language",
            "in": "query",
            "description": "Filter variants by language (e.g., \"English\", \"Japanese\"). Comma-separated for multiple, case-insensitive. This narrows the variants array on each card and does not remove cards from the result, so meta.total and pagination are unchanged. A card with no variant in the requested language is returned with an empty variants array. Variants with no language recorded are English. Ignored when looking up a single variant by variantId. An unrecognized value returns 400.",
            "schema": {
              "type": "string",
              "enum": [
                "English",
                "Japanese",
                "French",
                "German",
                "Spanish",
                "Italian",
                "Chinese (S)",
                "Chinese (T)",
                "Portuguese",
                "Russian",
                "Korean"
              ]
            }
          },
          {
            "name": "condition",
            "in": "query",
            "description": "Filter by condition (e.g., \"NM\", \"LP\"). Comma-separated for multiple.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "priceHistoryDuration",
            "in": "query",
            "description": "Duration of price history to include.",
            "schema": {
              "type": "string",
              "enum": ["7d", "30d", "90d", "180d", "1y"],
              "default": "7d"
            }
          },
          {
            "name": "include_price_history",
            "in": "query",
            "description": "Whether to include the price history array.",
            "schema": {
              "type": "boolean",
              "default": true
            }
          },
          {
            "name": "include_statistics",
            "in": "query",
            "description": "Timeframe statistics to include (comma-separated).",
            "schema": {
              "type": "string",
              "default": "7d,30d,90d,1y,allTime"
            }
          },
          {
            "name": "include_null_prices",
            "in": "query",
            "description": "Include cards with no pricing data. Ignored if search query `q` is present.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "updated_after",
            "in": "query",
            "description": "Filter results to cards updated after this Unix timestamp.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Max results to return.",
            "schema": {
              "type": "integer",
              "default": 20
            }
          },
          {
            "name": "offset",
            "in": "query",
            "description": "Pagination offset.",
            "schema": {
              "type": "integer",
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CardsResponse"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "tags": ["v1"]
      },
      "post": {
        "summary": "Batch Card Lookup",
        "description": "Retrieve multiple cards and their variants in a single request. \nIdeal for syncing inventory or calculating cart totals.",
        "operationId": "batchGetCards",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/BatchCardRequestItem"
                },
                "maxItems": 200
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CardsResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "413": {
            "description": "Payload Too Large (Too many items in batch)"
          }
        },
        "tags": ["v1"]
      }
    },
    "/v2/cards": {
      "get": {
        "tags": ["v2 (beta)"],
        "summary": "Search, browse or look up cards (beta)",
        "description": "**Beta.** Cards with raw and/or graded variants, each priced per requested region in a `markets` array. With no `regions`/`graded` params the response matches v1's raw NA pricing in the v2 shape. Identifier params take precedence over search. Browse/search responses are cursor-paginated.",
        "operationId": "getCardsV2",
        "x-beta": true,
        "parameters": [
          {
            "$ref": "#/components/parameters/V2CardId"
          },
          {
            "$ref": "#/components/parameters/V2VariantId"
          },
          {
            "$ref": "#/components/parameters/V2TcgplayerId"
          },
          {
            "$ref": "#/components/parameters/V2TcgplayerSku"
          },
          {
            "$ref": "#/components/parameters/V2Regions"
          },
          {
            "$ref": "#/components/parameters/V2Graded"
          },
          {
            "$ref": "#/components/parameters/V2GradingCompany"
          },
          {
            "$ref": "#/components/parameters/V2Grade"
          },
          {
            "$ref": "#/components/parameters/V2Include"
          },
          {
            "$ref": "#/components/parameters/V2Cursor"
          },
          {
            "$ref": "#/components/parameters/V2Limit"
          },
          {
            "$ref": "#/components/parameters/V2Q"
          },
          {
            "$ref": "#/components/parameters/V2Game"
          },
          {
            "$ref": "#/components/parameters/V2Set"
          },
          {
            "$ref": "#/components/parameters/V2Number"
          },
          {
            "$ref": "#/components/parameters/V2Language"
          },
          {
            "$ref": "#/components/parameters/V2MinPrice"
          },
          {
            "$ref": "#/components/parameters/V2OrderBy"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "Link": {
                "$ref": "#/components/headers/Link"
              },
              "X-Total-Count": {
                "$ref": "#/components/headers/X-Total-Count"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CardsV2Response"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ProblemBadRequest"
          },
          "401": {
            "$ref": "#/components/responses/ProblemUnauthorized"
          },
          "429": {
            "$ref": "#/components/responses/ProblemTooManyRequests"
          }
        }
      }
    },
    "/v2/cards/{id}": {
      "get": {
        "tags": ["v2 (beta)"],
        "summary": "Get one card by ID (beta)",
        "description": "**Beta.** Canonical direct lookup by card UUID or legacy slug. Returns `data` as a single Card object with no `meta`.",
        "operationId": "getCardV2",
        "x-beta": true,
        "parameters": [
          {
            "$ref": "#/components/parameters/V2CardPathId"
          },
          {
            "$ref": "#/components/parameters/V2Regions"
          },
          {
            "$ref": "#/components/parameters/V2Graded"
          },
          {
            "$ref": "#/components/parameters/V2GradingCompany"
          },
          {
            "$ref": "#/components/parameters/V2Grade"
          },
          {
            "$ref": "#/components/parameters/V2Include"
          },
          {
            "$ref": "#/components/parameters/V2Language"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CardV2Response"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ProblemBadRequest"
          },
          "401": {
            "$ref": "#/components/responses/ProblemUnauthorized"
          },
          "429": {
            "$ref": "#/components/responses/ProblemTooManyRequests"
          },
          "404": {
            "$ref": "#/components/responses/ProblemNotFound"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "x-api-key"
      }
    },
    "parameters": {
      "V2CardId": {
        "name": "card_id",
        "in": "query",
        "description": "Look up one card directly. Accepts either a UUID or a legacy v1 slug.",
        "schema": {
          "type": "string"
        }
      },
      "V2VariantId": {
        "name": "variant_id",
        "in": "query",
        "description": "Look up one variant directly. This is the fastest lookup. Accepts either a UUID or a legacy slug.",
        "schema": {
          "type": "string"
        }
      },
      "V2TcgplayerId": {
        "name": "tcgplayer_id",
        "in": "query",
        "description": "Look up one card by its TCGplayer product ID (v1's `tcgplayerId`).",
        "schema": {
          "type": "string"
        }
      },
      "V2TcgplayerSku": {
        "name": "tcgplayer_sku",
        "in": "query",
        "description": "Look up one variant by its TCGplayer SKU ID (v1's `tcgplayerSkuId`).",
        "schema": {
          "type": "string"
        }
      },
      "V2Regions": {
        "name": "regions",
        "in": "query",
        "description": "Comma-separated region codes in priority order (not case sensitive). The first region is your primary market (`markets[0]`), which `min_price` and `order_by` apply to. Market codes: `NA`, `EU`, `UK`, `JP`, `OCE`, `LATAM`, `OTHER`; ISO country codes (e.g. `MX`, `CA`) are also accepted. **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. Prices are never converted between currencies.",
        "style": "form",
        "explode": false,
        "schema": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "default": ["NA"]
        },
        "example": ["NA"]
      },
      "V2Graded": {
        "name": "graded",
        "in": "query",
        "description": "Which kinds of variants come back: `exclude` for raw only, `only` for graded only, `include` for both. `include` adds a surcharge.",
        "schema": {
          "type": "string",
          "enum": ["exclude", "only", "include"],
          "default": "exclude"
        }
      },
      "V2GradingCompany": {
        "name": "grading_company",
        "in": "query",
        "description": "Return every grade from one grading company. Sets `graded=only` automatically unless you also pass `graded=include`.",
        "schema": {
          "type": "string",
          "enum": ["PSA", "BGS", "CGC", "BCCG", "BVG", "SGC"]
        }
      },
      "V2Grade": {
        "name": "grade",
        "in": "query",
        "description": "Narrow results to specific grades within the chosen company, comma-separated (e.g. `9.5,10`).",
        "style": "form",
        "explode": false,
        "schema": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "example": ["9.5", "10"]
      },
      "V2Include": {
        "name": "include",
        "in": "query",
        "description": "Optional components to expand for each market, comma-separated. `periods[.<window>]` with windows `7d`, `30d`, `90d`, `1y`, `all_time`; `price_history[.<window>]` with windows `7d`, `30d`, `90d`, `180d`, `1y`. Default: all periods plus `price_history.7d`. Fields you do not request are omitted from the response rather than sent as null.",
        "style": "form",
        "explode": false,
        "schema": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "example": ["periods.7d", "price_history.30d"]
      },
      "V2Cursor": {
        "name": "cursor",
        "in": "query",
        "description": "Pagination cursor from the `Link` response header or `meta.cursor.next`. Replaces v1's `offset`.",
        "schema": {
          "type": "string"
        }
      },
      "V2Limit": {
        "name": "limit",
        "in": "query",
        "description": "Cards per page.",
        "schema": {
          "type": "integer",
          "default": 20
        }
      },
      "V2Q": {
        "name": "q",
        "in": "query",
        "description": "Free-text search matched against the card name.",
        "schema": {
          "type": "string"
        }
      },
      "V2Game": {
        "name": "game",
        "in": "query",
        "description": "Limit results to one game ID.",
        "schema": {
          "type": "string"
        }
      },
      "V2Set": {
        "name": "set",
        "in": "query",
        "description": "Limit results to one set ID.",
        "schema": {
          "type": "string"
        }
      },
      "V2Number": {
        "name": "number",
        "in": "query",
        "description": "Filter by a card's number within its set.",
        "schema": {
          "type": "string"
        }
      },
      "V2Language": {
        "name": "language",
        "in": "query",
        "description": "Filter variants by language, comma-separated and case-insensitive. Narrows each card's `variants` array but never removes cards, so `meta.total`, `meta.has_more`, `X-Total-Count` and cursors are unchanged. A card with no matching variant is returned with an empty `variants` array. Ignored for single-variant lookups (`variant_id`). Variants with `language: null` are English. An unrecognized value returns a 400 problem response.",
        "style": "form",
        "explode": false,
        "schema": {
          "type": "array",
          "items": {
            "type": "string",
            "enum": [
              "English",
              "Japanese",
              "French",
              "German",
              "Spanish",
              "Italian",
              "Chinese (S)",
              "Chinese (T)",
              "Portuguese",
              "Russian",
              "Korean"
            ]
          }
        }
      },
      "V2MinPrice": {
        "name": "min_price",
        "in": "query",
        "description": "Only return cards priced at or above this value, read from the primary market (`markets[0]`).",
        "schema": {
          "type": "number"
        }
      },
      "V2OrderBy": {
        "name": "order_by",
        "in": "query",
        "description": "Sort field. Sorting always uses the primary market only.",
        "schema": {
          "type": "string",
          "enum": ["price", "24h", "7d", "30d"]
        }
      },
      "V2CardPathId": {
        "name": "id",
        "in": "path",
        "description": "Card UUID (recommended) or legacy v1 slug.",
        "required": true,
        "schema": {
          "type": "string"
        },
        "example": "pokemon-base-set-charizard-holo-rare"
      }
    },
    "headers": {
      "RateLimit": {
        "description": "Usage for the current window: `limit`, `remaining`, and seconds until `reset`. Replaces v1's `_metadata` usage fields.",
        "schema": {
          "type": "string",
          "example": "limit=500000, remaining=499958, reset=42"
        }
      },
      "Link": {
        "description": "RFC 8288 link to the next page (`rel=\"next\"`). Present only while more results exist; always agrees with `meta.cursor`.",
        "schema": {
          "type": "string"
        }
      },
      "X-Total-Count": {
        "description": "Mirrors `meta.total` on browse/search responses. Omitted when the total could not be computed in time.",
        "schema": {
          "type": "integer"
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Bad Request",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Unauthorized - Missing or invalid API key",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "TooManyRequests": {
        "description": "Rate limit exceeded",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "ProblemBadRequest": {
        "description": "Bad Request (RFC 7807 problem details)",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "ProblemUnauthorized": {
        "description": "Missing or invalid API key (RFC 7807 problem details)",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "ProblemNotFound": {
        "description": "Card not found (RFC 7807 problem details)",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "ProblemTooManyRequests": {
        "description": "Rate or usage limit exceeded (RFC 7807 problem details)",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      }
    },
    "schemas": {
      "GamesResponse": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Game"
            }
          },
          "_metadata": {
            "$ref": "#/components/schemas/UsageMetadata"
          }
        }
      },
      "SetsResponse": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Set"
            }
          },
          "meta": {
            "$ref": "#/components/schemas/PaginationMeta"
          },
          "_metadata": {
            "$ref": "#/components/schemas/UsageMetadata"
          }
        }
      },
      "CardsResponse": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Card"
            }
          },
          "meta": {
            "$ref": "#/components/schemas/PaginationMeta"
          },
          "_metadata": {
            "$ref": "#/components/schemas/UsageMetadata"
          }
        }
      },
      "Game": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "magic-the-gathering"
          },
          "name": {
            "type": "string",
            "example": "Magic: The Gathering"
          },
          "cards_count": {
            "type": "integer"
          },
          "variants_count": {
            "type": "integer"
          },
          "sealed_count": {
            "type": "integer"
          },
          "sets_count": {
            "type": "integer"
          },
          "last_updated": {
            "type": "integer",
            "description": "Unix timestamp"
          },
          "game_value_index_cents": {
            "type": "integer"
          },
          "game_value_change_7d_pct": {
            "type": "number",
            "format": "float"
          }
        }
      },
      "Set": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "modern-horizons-2-magic-the-gathering"
          },
          "name": {
            "type": "string",
            "example": "Modern Horizons 2"
          },
          "game_id": {
            "type": "string"
          },
          "game": {
            "type": "string"
          },
          "release_date": {
            "type": "string",
            "format": "date"
          },
          "cards_count": {
            "type": "integer"
          },
          "set_value_usd": {
            "type": "number",
            "format": "float"
          }
        }
      },
      "Card": {
        "type": "object",
        "description": "Represents a trading card. Pricing is contained within the variants array.",
        "properties": {
          "id": {
            "type": "string",
            "description": "Slug-based ID (format: game-set-name-rarity)."
          },
          "uuid": {
            "type": "string",
            "description": "UUID of the card. Use this for unique identification"
          },
          "name": {
            "type": "string"
          },
          "game": {
            "type": "string"
          },
          "set": {
            "type": "string",
            "description": "Set ID"
          },
          "set_name": {
            "type": "string"
          },
          "number": {
            "type": "string",
            "description": "Card number in set"
          },
          "tcgplayerId": {
            "type": "string"
          },
          "mtgjsonId": {
            "type": "string"
          },
          "scryfallId": {
            "type": "string"
          },
          "rarity": {
            "type": "string"
          },
          "details": {
            "type": "string",
            "nullable": true
          },
          "variants": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Variant"
            }
          }
        }
      },
      "Variant": {
        "type": "object",
        "description": "A specific combination of condition and printing for a card.",
        "properties": {
          "id": {
            "type": "string",
            "description": "Slug-based ID (format: cardId_condition_printing)"
          },
          "uuid": {
            "type": "string",
            "description": "UUID of the variant. Use this for unique identification"
          },
          "condition": {
            "type": "string",
            "example": "Near Mint"
          },
          "printing": {
            "type": "string",
            "example": "Foil"
          },
          "language": {
            "type": "string",
            "example": "English"
          },
          "tcgplayerSkuId": {
            "type": "string"
          },
          "price": {
            "type": "number",
            "format": "float",
            "description": "Current price in USD"
          },
          "lastUpdated": {
            "type": "integer",
            "description": "Unix timestamp"
          },
          "priceChange24hr": {
            "type": "number",
            "nullable": true
          },
          "priceChange7d": {
            "type": "number",
            "nullable": true
          },
          "avgPrice": {
            "type": "number",
            "nullable": true
          },
          "minPrice7d": {
            "type": "number",
            "nullable": true
          },
          "maxPrice7d": {
            "type": "number",
            "nullable": true
          },
          "priceHistory": {
            "type": "array",
            "description": "Array of price points based on `priceHistoryDuration`.",
            "items": {
              "type": "object",
              "properties": {
                "p": {
                  "type": "number",
                  "description": "Price"
                },
                "t": {
                  "type": "integer",
                  "description": "Timestamp"
                }
              }
            }
          },
          "priceHistory30d": {
            "type": "array",
            "deprecated": true,
            "items": {
              "type": "object"
            }
          }
        }
      },
      "BatchCardRequestItem": {
        "type": "object",
        "description": "Object containing one identifier and optional filters for a card/variant lookup. Do not use multiple identifiers.",
        "properties": {
          "tcgplayerId": {
            "type": "string"
          },
          "mtgjsonId": {
            "type": "string"
          },
          "scryfallId": {
            "type": "string"
          },
          "tcgplayerSkuId": {
            "type": "string"
          },
          "cardId": {
            "type": "string",
            "description": "Slug-based ID or UUID of the card."
          },
          "variantId": {
            "type": "string",
            "description": "Slug-based ID or UUID of the variant."
          },
          "printing": {
            "type": "string"
          },
          "condition": {
            "type": "string"
          },
          "updated_after": {
            "type": "integer",
            "description": "Unix timestamp for delta sync"
          }
        }
      },
      "PaginationMeta": {
        "type": "object",
        "properties": {
          "total": {
            "type": "integer"
          },
          "limit": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          },
          "hasMore": {
            "type": "boolean"
          }
        }
      },
      "UsageMetadata": {
        "type": "object",
        "properties": {
          "apiPlan": {
            "type": "string"
          },
          "apiRequestLimit": {
            "type": "integer"
          },
          "apiRequestsUsed": {
            "type": "integer"
          },
          "apiRequestsRemaining": {
            "type": "integer"
          },
          "apiDailyLimit": {
            "type": "integer"
          },
          "apiDailyRequestsUsed": {
            "type": "integer"
          },
          "apiRateLimit": {
            "type": "integer"
          }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          },
          "code": {
            "type": "string",
            "example": "INVALID_REQUEST"
          }
        }
      },
      "CardV2": {
        "type": "object",
        "description": "v2 (beta) card. Pricing lives on each variant's `markets` array. Fields you did not request are omitted rather than sent as null.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Stable native identifier (v1's `uuid`). Store and key on this."
          },
          "slug": {
            "type": "string",
            "description": "Legacy v1 `id`. Still works for lookups.",
            "example": "pokemon-base-set-charizard-holo-rare"
          },
          "name": {
            "type": "string",
            "example": "Charizard"
          },
          "game": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "example": "pokemon"
              },
              "name": {
                "type": "string",
                "example": "Pokemon"
              }
            }
          },
          "set": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "example": "base-set-pokemon"
              },
              "name": {
                "type": "string",
                "nullable": true,
                "example": "Base Set"
              }
            }
          },
          "number": {
            "type": "string",
            "nullable": true,
            "description": "Card number within the set."
          },
          "rarity": {
            "type": "string",
            "nullable": true
          },
          "external_ids": {
            "type": "object",
            "properties": {
              "tcgplayer": {
                "type": "string",
                "nullable": true,
                "description": "TCGplayer product ID."
              },
              "scryfall": {
                "type": "string",
                "nullable": true,
                "description": "Scryfall ID."
              },
              "mtgjson": {
                "type": "string",
                "nullable": true,
                "description": "MTGJSON ID."
              }
            }
          },
          "details": {
            "type": "string",
            "nullable": true,
            "description": "Free-form extra detail, when available."
          },
          "variants": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/VariantV2"
            }
          }
        }
      },
      "VariantV2": {
        "type": "object",
        "description": "v2 (beta) variant: either raw (`condition` set, `grading` null) or graded (`condition` null, `grading` set).",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Stable native identifier (v1's variant `uuid`)."
          },
          "slug": {
            "type": "string",
            "description": "Legacy v1 variant `id`. Still works for lookups."
          },
          "type": {
            "type": "string",
            "enum": ["raw", "graded"]
          },
          "condition": {
            "type": "string",
            "nullable": true,
            "description": "Condition for raw variants, e.g. \"Near Mint\". Null for graded variants."
          },
          "printing": {
            "type": "string",
            "nullable": true,
            "example": "Holofoil",
            "description": "Print type only; v1's \" - <Language>\" suffix is removed."
          },
          "language": {
            "type": "string",
            "nullable": true,
            "description": "Set only for non-English printings; null means English."
          },
          "external_ids": {
            "type": "object",
            "properties": {
              "tcgplayer_sku": {
                "type": "string",
                "nullable": true,
                "description": "TCGplayer SKU ID."
              }
            }
          },
          "grading": {
            "allOf": [
              {
                "$ref": "#/components/schemas/GradingV2"
              }
            ],
            "type": "object",
            "nullable": true,
            "description": "Present for graded variants; null for raw."
          },
          "markets": {
            "type": "array",
            "description": "One entry per requested region, in request order. `markets[0]` is the primary market.",
            "items": {
              "$ref": "#/components/schemas/MarketV2"
            }
          }
        }
      },
      "GradingV2": {
        "type": "object",
        "properties": {
          "company": {
            "type": "string",
            "enum": ["PSA", "BGS", "CGC", "BCCG", "BVG", "SGC"]
          },
          "grade": {
            "type": "number",
            "nullable": true,
            "description": "Numeric grade, e.g. 9 or 10. Null only for cards graded \"Authentic\"."
          },
          "grade_label": {
            "type": "string",
            "nullable": true,
            "description": "Special designation such as \"Black Label\" or \"Pristine\"."
          },
          "qualifier": {
            "type": "string",
            "nullable": true,
            "description": "Grading qualifier such as \"OC\" (off-center). Qualified cards are priced separately."
          },
          "canonical": {
            "type": "string",
            "description": "Display-ready grade string.",
            "example": "PSA 10"
          }
        }
      },
      "MarketV2": {
        "type": "object",
        "properties": {
          "region": {
            "type": "string",
            "description": "The requested region code (market code like NA, or ISO country code).",
            "example": "NA"
          },
          "currency": {
            "type": "string",
            "description": "ISO 4217 currency for `price` and every `periods` value.",
            "example": "USD"
          },
          "price": {
            "type": "number",
            "nullable": true,
            "description": "Observed price in this region. Null when there is no local data; never converted or copied from another region."
          },
          "updated_at": {
            "type": "integer",
            "description": "Unix timestamp (seconds) when the price was last observed."
          },
          "change_24h_pct": {
            "type": "number",
            "nullable": true,
            "description": "Percent change over 24 hours (v1's `priceChange24hr`)."
          },
          "periods": {
            "type": "object",
            "description": "Statistics by time window. Only requested windows are included. `1y` and `all_time` carry min/max only (plus `min_date`/`max_date` for `all_time`).",
            "properties": {
              "7d": {
                "$ref": "#/components/schemas/PeriodStatsV2"
              },
              "30d": {
                "$ref": "#/components/schemas/PeriodStatsV2"
              },
              "90d": {
                "$ref": "#/components/schemas/PeriodStatsV2"
              },
              "1y": {
                "$ref": "#/components/schemas/PeriodStatsV2"
              },
              "all_time": {
                "$ref": "#/components/schemas/PeriodStatsV2"
              }
            }
          },
          "price_history": {
            "type": "array",
            "description": "Observed price series for this region, same as v1's `priceHistory`.",
            "items": {
              "$ref": "#/components/schemas/PricePoint"
            }
          }
        }
      },
      "PeriodStatsV2": {
        "type": "object",
        "description": "Aggregates for one window. Values match v1's flat stat fields.",
        "properties": {
          "change_pct": {
            "type": "number",
            "nullable": true,
            "description": "Percent change over the window."
          },
          "avg": {
            "type": "number",
            "nullable": true,
            "description": "Average price."
          },
          "min": {
            "type": "number",
            "nullable": true,
            "description": "Minimum price."
          },
          "max": {
            "type": "number",
            "nullable": true,
            "description": "Maximum price."
          },
          "stddev": {
            "type": "number",
            "nullable": true,
            "description": "Population standard deviation (v1's `stddevPopPrice*`)."
          },
          "cov": {
            "type": "number",
            "nullable": true,
            "description": "Coefficient of variation."
          },
          "iqr": {
            "type": "number",
            "nullable": true,
            "description": "Interquartile range."
          },
          "trend_slope": {
            "type": "number",
            "nullable": true,
            "description": "Fitted price trend, change per day."
          },
          "changes_count": {
            "type": "number",
            "nullable": true,
            "description": "Number of price changes observed."
          },
          "range_position": {
            "type": "number",
            "nullable": true,
            "description": "Current price's position in the window's low-high range, 0 to 1. 30d and 90d only."
          },
          "min_date": {
            "type": "string",
            "nullable": true,
            "description": "ISO date of the all-time low. all_time only."
          },
          "max_date": {
            "type": "string",
            "nullable": true,
            "description": "ISO date of the all-time high. all_time only."
          }
        }
      },
      "PricePoint": {
        "type": "object",
        "properties": {
          "t": {
            "type": "integer",
            "description": "Unix timestamp (seconds)"
          },
          "p": {
            "type": "number",
            "description": "Price"
          }
        }
      },
      "PaginationMetaV2": {
        "type": "object",
        "properties": {
          "count": {
            "type": "integer",
            "description": "Items in this response's `data`."
          },
          "total": {
            "type": "integer",
            "nullable": true,
            "description": "Total matches across pages. Null means unknown, not zero."
          },
          "limit": {
            "type": "integer"
          },
          "has_more": {
            "type": "boolean"
          },
          "cursor": {
            "type": "object",
            "properties": {
              "next": {
                "type": "string",
                "nullable": true,
                "description": "Cursor for the next page."
              },
              "prev": {
                "type": "string",
                "nullable": true,
                "description": "Cursor for the previous page."
              }
            }
          }
        }
      },
      "CardsV2Response": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CardV2"
            }
          },
          "meta": {
            "$ref": "#/components/schemas/PaginationMetaV2"
          }
        }
      },
      "CardV2Response": {
        "type": "object",
        "properties": {
          "data": {
            "$ref": "#/components/schemas/CardV2"
          }
        }
      },
      "Problem": {
        "type": "object",
        "description": "RFC 7807 problem details, served as application/problem+json. `code` carries the same error codes as v1.",
        "properties": {
          "type": {
            "type": "string",
            "format": "uri",
            "example": "https://justtcg.com/docs/errors#invalid-request"
          },
          "title": {
            "type": "string",
            "example": "Invalid request"
          },
          "status": {
            "type": "integer",
            "example": 400
          },
          "detail": {
            "type": "string"
          },
          "code": {
            "type": "string",
            "enum": [
              "MISSING_API_KEY",
              "INVALID_API_KEY",
              "INVALID_REQUEST",
              "RATE_LIMIT_EXCEEDED",
              "DAILY_LIMIT_EXCEEDED",
              "REQUEST_LIMIT_EXCEEDED"
            ],
            "example": "INVALID_REQUEST"
          }
        }
      }
    }
  }
}
