{
  "openapi": "3.1.0",
  "info": {
    "title": "Hobby Card Index Data API",
    "version": "1",
    "summary": "Real sold prices, photo identification, cert lookups, pop reports, sold sales and price history for sports cards and trading cards.",
    "description": "Real sold prices for sports cards and trading cards: a catalog of more than 16 million cards across 25-plus categories (baseball, basketball, football, hockey, soccer, Pokemon, Magic: The Gathering, Yu-Gi-Oh, One Piece and more) with more than 22 million raw and graded prices, refreshed daily, plus card identification from a photo, PSA, BGS, CGC and SGC cert lookups, population reports, verified sold-sale records, daily price history, set lists, market movers, a price watch list with a daily webhook, and bulk price files.\n\n## Keys and plans\n\nEvery endpoint needs an API key, sent as the `X-API-Key` header (or `Authorization: Bearer`). Keys come from Settings, API Access, after subscribing. An **Enterprise** plan ($49.99 a month) includes a key for your own business's internal use: 1 request a second, up to 100,000 a day. Apps, websites and products whose users see the data need a **Commercial plan** (a commercial data license, Terms of Service section 8):\n\n- Lite (plan id `lite`): $99 a month; 25,000 requests, 25,000 photo scans, 5,000 sold-sale lookups, 150 cert lookups, 10 lookups a second, price history.\n- Growth Lite (plan id `growth_lite`): $249 a month; 125,000 requests, 125,000 photo scans, 25,000 sold-sale lookups, 750 cert lookups, 15 lookups a second, price history.\n- Growth (plan id `growth`): $499 a month; 500,000 requests, 500,000 photo scans, 100,000 sold-sale lookups, 3,000 cert lookups, 20 lookups a second, price history.\n- Platform (plan id `platform`): from $1,500 a month; 2,000,000 requests, 1,500,000 photo scans, 500,000 sold-sale lookups, 50,000 cert lookups, 50 lookups a second, price history, bulk price files.\n\nA **free setup month** is by application in Settings, API Access (one per company): enough to build and test against real data, with daily pools. Plans, prices and how to buy, including how an AI agent can buy a plan for its user with no account (GET /api/v1/agent/plans, POST /api/v1/agent/checkout): https://hobbycardindex.com/developers#commercial-plans. Questions or a bigger deal: sean@hobbycardindex.com.\n\n## Credit the data (required)\n\nCommercial plans ask for one link back to https://hobbycardindex.com somewhere on your site, such as 'Prices and API supplied by Hobby Card Index'; it does not need to be beside the prices. Every successful JSON object response carries an `attribution` object (text, url, html, required, terms) and an `X-Attribution` header with the same credit, so it can be rendered straight from the response. Put the credit beside the prices or anywhere on your website, such as the footer. Snippet: `<a href=\"https://hobbycardindex.com\" rel=\"noopener\">Prices from Hobby Card Index</a>`.\n\n## Conventions\n\n- Base URL `https://hobbycardindex.com`; every path starts with `/api/v1/`. Prices are US dollars with two decimals; a grade with no price is `null`. Grade keys: `loose` (raw), `psa10`, `psa9`, `psa8`, `psa7`, `bgs10` (BGS 9.5), `cgc10`, `sgc10`.\n- Successful responses are `{ \"success\": true, \"data\": { ... }, \"attribution\": { ... } }`; errors are `{ \"success\": false, \"error\": { \"code\", \"message\", \"details\"? } }` with the HTTP status that fits (400, 401, 402, 403, 404, 409, 413, 429, 500, 503). Streamed downloads (CSV, bulk files, the watch-list prices) are not wrapped.\n- Card ids: pass the integer `id` every response carries, or `ext-` plus the card's `externalId`. Picture links (`imageUrl`) go through our image proxy and need no key; a link can answer 404 later if the picture is withdrawn, so show a placeholder on a 404.\n- Rate limits: 1 request a second on an Enterprise key, the plan's lookups a second on a commercial key, with `RateLimit-*` and `X-RateLimit-*` headers on the one-second window. Commercial plans also return `X-HCI-Plan` and `X-HCI-*-Used` / `X-HCI-*-Allowance` headers for the monthly counters.\n- The same data through an MCP server for AI assistants: POST https://hobbycardindex.com/api/v1/mcp, manifest https://hobbycardindex.com/.well-known/mcp.json.",
    "termsOfService": "https://hobbycardindex.com/terms/",
    "contact": {
      "name": "Sean Charman, Hobby Card Index",
      "email": "sean@hobbycardindex.com",
      "url": "https://hobbycardindex.com/developers"
    },
    "license": {
      "name": "Hobby Card Index Terms of Service (section 8, Commercial Data License)",
      "url": "https://hobbycardindex.com/terms/#data-license"
    },
    "x-logo": {
      "url": "https://hobbycardindex.com/hci-logo2.png",
      "altText": "Hobby Card Index",
      "href": "https://hobbycardindex.com"
    },
    "x-attribution": {
      "text": "Prices from Hobby Card Index",
      "url": "https://hobbycardindex.com",
      "html": "<a href=\"https://hobbycardindex.com\" rel=\"noopener\">Prices from Hobby Card Index</a>",
      "required": true,
      "terms": "https://hobbycardindex.com/terms/"
    }
  },
  "externalDocs": {
    "description": "Developer documentation with examples in curl, JavaScript and Python",
    "url": "https://hobbycardindex.com/developers"
  },
  "servers": [
    {
      "url": "https://hobbycardindex.com",
      "description": "Production"
    }
  ],
  "security": [
    {
      "ApiKeyAuth": []
    }
  ],
  "tags": [
    {
      "name": "prices",
      "description": "Current prices by grade, search, and daily price history."
    },
    {
      "name": "catalog",
      "description": "Sets and the ids the other endpoints take."
    },
    {
      "name": "market",
      "description": "Market movers, category statistics, the market indexes and the market summary."
    },
    {
      "name": "scan",
      "description": "Card identification from a photo (commercial plans)."
    },
    {
      "name": "slabs and certs",
      "description": "Cert lookups and population reports."
    },
    {
      "name": "sales",
      "description": "Verified sold-sale records and active listings."
    },
    {
      "name": "watch list and webhooks",
      "description": "Follow cards, sets or categories and get the daily price changes."
    },
    {
      "name": "bulk files",
      "description": "Daily per-category price files."
    },
    {
      "name": "collection",
      "description": "The key owner's own collection."
    },
    {
      "name": "usage",
      "description": "The key's plan, limits and usage."
    },
    {
      "name": "mcp",
      "description": "The Model Context Protocol server for AI assistants."
    }
  ],
  "paths": {
    "/api/v1/usage": {
      "get": {
        "tags": [
          "usage"
        ],
        "summary": "This key's plan, limits and month-to-date usage",
        "operationId": "getUsage",
        "description": "What the key can do and how much of it is used. Commercial plans see their monthly allowances (requests, scans, sold-sale, cert and pop lookups), per-second limits, overage terms and, on a free setup month, when it ends. An Enterprise key sees the self-serve model: 1 request a second.",
        "responses": {
          "200": {
            "description": "Usage for the key",
            "headers": {
              "X-Attribution": {
                "$ref": "#/components/headers/X-Attribution"
              },
              "X-HCI-Plan": {
                "$ref": "#/components/headers/X-HCI-Plan"
              },
              "X-HCI-Requests-Used": {
                "$ref": "#/components/headers/X-HCI-Requests-Used"
              },
              "X-HCI-Requests-Allowance": {
                "$ref": "#/components/headers/X-HCI-Requests-Allowance"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/Usage"
                    },
                    "attribution": {
                      "$ref": "#/components/schemas/Attribution"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      }
    },
    "/api/v1/prices/search": {
      "get": {
        "tags": [
          "prices"
        ],
        "summary": "Search cards and their current prices by grade",
        "operationId": "searchPrices",
        "description": "A plain `q` search returns the 10 best matches with every grade price: base cards first, then parallels, the most traded first within each. Any filter turns it into a paged, filtered search (25 a page by default) with `hasMore`. To list every card in one set, send `category` and `set` (a set id from GET /api/v1/sets) with no `q`; to get only cards added since a date, send `added_since`. Prices are real sold prices in US dollars. Every plan; one request.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Search text, 2 to 200 characters, for example `Mike Trout 2011`. Required unless `set` or `added_since` is given.",
            "schema": {
              "type": "string",
              "minLength": 2,
              "maxLength": 200
            }
          },
          {
            "name": "category",
            "in": "query",
            "required": false,
            "description": "A category id from GET /api/v1/market/categories, for example `pokemon` or `baseball`.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9-]{0,39}$"
            }
          },
          {
            "name": "set",
            "in": "query",
            "required": false,
            "description": "A set id from GET /api/v1/sets. Needs `category`. Results come in card-number order.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9-]{0,79}$"
            }
          },
          {
            "name": "language",
            "in": "query",
            "required": false,
            "description": "The language printed on the card.",
            "schema": {
              "type": "string",
              "enum": [
                "english",
                "japanese",
                "chinese",
                "korean",
                "german",
                "french",
                "spanish",
                "italian",
                "portuguese",
                "dutch",
                "thai",
                "indonesian",
                "russian",
                "polish"
              ]
            }
          },
          {
            "name": "year",
            "in": "query",
            "required": false,
            "description": "Card year, four digits.",
            "schema": {
              "type": "integer",
              "minimum": 1850,
              "maximum": 2100
            }
          },
          {
            "name": "added_since",
            "in": "query",
            "required": false,
            "description": "Only cards added to our catalog since this date or time (UTC), for example `2026-10-01` or `2026-10-01T12:00:00Z`. Results come in the order they were added. It can reach back up to 90 days.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "min_price",
            "in": "query",
            "required": false,
            "description": "Lowest raw (ungraded) price, US dollars.",
            "schema": {
              "type": "number",
              "minimum": 0,
              "maximum": 10000000
            }
          },
          {
            "name": "max_price",
            "in": "query",
            "required": false,
            "description": "Highest raw (ungraded) price, US dollars.",
            "schema": {
              "type": "number",
              "minimum": 0,
              "maximum": 10000000
            }
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "description": "Order of a filtered search.",
            "schema": {
              "type": "string",
              "enum": [
                "price_asc",
                "price_desc",
                "volume_desc",
                "name_asc",
                "added_asc",
                "added_desc"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size, 1 to 25. Default 25.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 25,
              "default": 25
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "How many to skip. A `q` search pages through its best matches (up to the search ceiling); a set, or `added_since` without `q`, pages through every card, up to 100,000.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Matching cards",
            "headers": {
              "X-Attribution": {
                "$ref": "#/components/headers/X-Attribution"
              },
              "X-HCI-Plan": {
                "$ref": "#/components/headers/X-HCI-Plan"
              },
              "X-HCI-Requests-Used": {
                "$ref": "#/components/headers/X-HCI-Requests-Used"
              },
              "X-HCI-Requests-Allowance": {
                "$ref": "#/components/headers/X-HCI-Requests-Allowance"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/SearchResponse"
                    },
                    "attribution": {
                      "$ref": "#/components/schemas/Attribution"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      }
    },
    "/api/v1/sets": {
      "get": {
        "tags": [
          "catalog"
        ],
        "summary": "The sets in a category",
        "operationId": "listSets",
        "description": "Every set in a category with the set id that search (`set=`) and the watch list take, the language printed on its cards, its year and how many cards it has. Every plan; one request.",
        "parameters": [
          {
            "name": "category",
            "in": "query",
            "required": true,
            "description": "The category id, for example `pokemon` (see GET /api/v1/market/categories).",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9-]{0,39}$"
            }
          },
          {
            "name": "language",
            "in": "query",
            "required": false,
            "description": "Only sets in this language.",
            "schema": {
              "type": "string",
              "enum": [
                "english",
                "japanese",
                "chinese",
                "korean",
                "german",
                "french",
                "spanish",
                "italian",
                "portuguese",
                "dutch",
                "thai",
                "indonesian",
                "russian",
                "polish"
              ]
            }
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Part of the set name or id, up to 100 characters.",
            "schema": {
              "type": "string",
              "maxLength": 100
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size, 1 to 5,000. Default 1,000.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 5000,
              "default": 1000
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "How many to skip.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Sets in the category",
            "headers": {
              "X-Attribution": {
                "$ref": "#/components/headers/X-Attribution"
              },
              "X-HCI-Plan": {
                "$ref": "#/components/headers/X-HCI-Plan"
              },
              "X-HCI-Requests-Used": {
                "$ref": "#/components/headers/X-HCI-Requests-Used"
              },
              "X-HCI-Requests-Allowance": {
                "$ref": "#/components/headers/X-HCI-Requests-Allowance"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/SetsResponse"
                    },
                    "attribution": {
                      "$ref": "#/components/schemas/Attribution"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      }
    },
    "/api/v1/prices/{cardId}": {
      "get": {
        "tags": [
          "prices"
        ],
        "summary": "One card's prices at every grade",
        "operationId": "getCardPrices",
        "description": "The card and its current prices by grade (raw, PSA 10, 9, 8 and 7, BGS 9.5, CGC 10, SGC 10) in US dollars, with recent sales volume and when the prices were last refreshed. Numbered parallels carry `printRun`. `imageUrl` is the card's picture through our image link (no key needed). Every plan; one request.",
        "parameters": [
          {
            "$ref": "#/components/parameters/cardId"
          }
        ],
        "responses": {
          "200": {
            "description": "The card and its prices",
            "headers": {
              "X-Attribution": {
                "$ref": "#/components/headers/X-Attribution"
              },
              "X-HCI-Plan": {
                "$ref": "#/components/headers/X-HCI-Plan"
              },
              "X-HCI-Requests-Used": {
                "$ref": "#/components/headers/X-HCI-Requests-Used"
              },
              "X-HCI-Requests-Allowance": {
                "$ref": "#/components/headers/X-HCI-Requests-Allowance"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/CardPricesResponse"
                    },
                    "attribution": {
                      "$ref": "#/components/schemas/Attribution"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      }
    },
    "/api/v1/prices/{cardId}/sales": {
      "get": {
        "tags": [
          "sales"
        ],
        "summary": "Verified sold sales for one card",
        "operationId": "getCardSales",
        "description": "Individual verified sales: price, date, grade, how it sold (auction, buy_it_now, best_offer), the marketplace, the listing link and the listing photo's link, plus summary stats for the window. Commercial plans with sold sales (every size; the free setup month includes lookups to test with). Each call returns up to 100 sales in each grade, picked by `sample` (the newest by default), and counts as one sold-sale lookup as well as one request. Other keys get 403 REQUIRES_COMMERCIAL_LICENSE.",
        "parameters": [
          {
            "$ref": "#/components/parameters/cardId"
          },
          {
            "$ref": "#/components/parameters/days365"
          },
          {
            "name": "sample",
            "in": "query",
            "required": false,
            "description": "How the 100 sales in each grade are picked: `latest` (the newest), `daily` (one sale per day, the middle-priced sale of that day, newest days first) or `spread` (evenly spaced through the window). Default latest.",
            "schema": {
              "type": "string",
              "enum": [
                "latest",
                "daily",
                "spread"
              ],
              "default": "latest"
            }
          },
          {
            "name": "everyDays",
            "in": "query",
            "required": false,
            "description": "With sample=daily: one sale per this many days, 1 to 30. Default 1.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 30,
              "default": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Sales in the window, newest first",
            "headers": {
              "X-Attribution": {
                "$ref": "#/components/headers/X-Attribution"
              },
              "X-HCI-Plan": {
                "$ref": "#/components/headers/X-HCI-Plan"
              },
              "X-HCI-Requests-Used": {
                "$ref": "#/components/headers/X-HCI-Requests-Used"
              },
              "X-HCI-Requests-Allowance": {
                "$ref": "#/components/headers/X-HCI-Requests-Allowance"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/SalesResponse"
                    },
                    "attribution": {
                      "$ref": "#/components/schemas/Attribution"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      }
    },
    "/api/v1/prices/{cardId}/listings": {
      "get": {
        "tags": [
          "sales"
        ],
        "summary": "Active Buy It Now listings for one card",
        "operationId": "getCardListings",
        "description": "Listings for sale right now that are this card, deals first (priced under our value at the listing's grade), then the cheapest others, with the seller's feedback. Checked at most every 15 minutes per card. Switched on per account (403 LISTINGS_NOT_ENABLED otherwise), 100 lookups a day (429 LISTINGS_LIMIT_REACHED). Email sean@hobbycardindex.com to have it turned on.",
        "parameters": [
          {
            "$ref": "#/components/parameters/cardId"
          },
          {
            "name": "grade",
            "in": "query",
            "required": false,
            "description": "Only listings at this grade, for example `raw` or `PSA 10`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Listings to return, 1 to 25. Default 10.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 25,
              "default": 10
            }
          },
          {
            "name": "minFeedbackPercent",
            "in": "query",
            "required": false,
            "description": "Drop sellers under this feedback percentage. Default 98.",
            "schema": {
              "type": "number",
              "minimum": 0,
              "maximum": 100,
              "default": 98
            }
          },
          {
            "name": "minFeedbackScore",
            "in": "query",
            "required": false,
            "description": "Drop sellers under this feedback score. Default 50.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Listings that are this card",
            "headers": {
              "X-Attribution": {
                "$ref": "#/components/headers/X-Attribution"
              },
              "X-HCI-Plan": {
                "$ref": "#/components/headers/X-HCI-Plan"
              },
              "X-HCI-Requests-Used": {
                "$ref": "#/components/headers/X-HCI-Requests-Used"
              },
              "X-HCI-Requests-Allowance": {
                "$ref": "#/components/headers/X-HCI-Requests-Allowance"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/ListingsResponse"
                    },
                    "attribution": {
                      "$ref": "#/components/schemas/Attribution"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      }
    },
    "/api/v1/prices/{cardId}/history": {
      "get": {
        "tags": [
          "prices"
        ],
        "summary": "Daily price history at every grade",
        "operationId": "getCardPriceHistory",
        "description": "The card's price at every grade, day by day, for charts. A grade with no price on a day is null, never 0; days we hold no prices for are not in the list. Individual sales are in /sales. Commercial plans with price history (every size), signed deals and admins; other keys get 403 REQUIRES_GROWTH. One request.",
        "parameters": [
          {
            "$ref": "#/components/parameters/cardId"
          },
          {
            "name": "days",
            "in": "query",
            "required": false,
            "description": "How far back, 1 to 3,650 days. Default 365.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 3650,
              "default": 365
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Daily prices",
            "headers": {
              "X-Attribution": {
                "$ref": "#/components/headers/X-Attribution"
              },
              "X-HCI-Plan": {
                "$ref": "#/components/headers/X-HCI-Plan"
              },
              "X-HCI-Requests-Used": {
                "$ref": "#/components/headers/X-HCI-Requests-Used"
              },
              "X-HCI-Requests-Allowance": {
                "$ref": "#/components/headers/X-HCI-Requests-Allowance"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/HistoryResponse"
                    },
                    "attribution": {
                      "$ref": "#/components/schemas/Attribution"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      }
    },
    "/api/v1/prices/{cardId}/population": {
      "get": {
        "tags": [
          "slabs and certs"
        ],
        "summary": "Population report for one card",
        "operationId": "getCardPopulation",
        "description": "How many copies PSA, BGS, SGC and CGC have graded, grade by grade, with gem counts and gem rate. We hold a stored report for many cards; a card with none, or one over 30 days old, is fetched live within a daily budget. Platform, or Lite, Growth Lite and Growth with the pop + cert add-on. Pop reports have their own monthly number and stop there (429 POP_LIMIT_REACHED); each call also counts as a request.",
        "parameters": [
          {
            "$ref": "#/components/parameters/cardId"
          }
        ],
        "responses": {
          "200": {
            "description": "The population report, or null when we hold none",
            "headers": {
              "X-Attribution": {
                "$ref": "#/components/headers/X-Attribution"
              },
              "X-HCI-Plan": {
                "$ref": "#/components/headers/X-HCI-Plan"
              },
              "X-HCI-Requests-Used": {
                "$ref": "#/components/headers/X-HCI-Requests-Used"
              },
              "X-HCI-Requests-Allowance": {
                "$ref": "#/components/headers/X-HCI-Requests-Allowance"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "X-HCI-Pop-Used": {
                "description": "Pop reports used this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-HCI-Pop-Allowance": {
                "description": "The plan's monthly pop reports.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/PopulationResponse"
                    },
                    "attribution": {
                      "$ref": "#/components/schemas/Attribution"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      }
    },
    "/api/v1/certs/{grader}/{certNumber}": {
      "get": {
        "tags": [
          "slabs and certs"
        ],
        "summary": "Look up a graded card by cert number",
        "operationId": "lookupCert",
        "description": "The grading company's record for the cert (grade, autograph grade, player, year, set, card number and population when the grader reports it), the matching card in our catalog with its prices at every grade, and the `slab` block: the price at that exact grade, whether the cert verified, the grader's own cert page (`verifyUrl`) and their slab photos when they have them. Every commercial size has a monthly number of cert lookups (hard stop, 429 CERT_LIMIT_REACHED); one lookup a second per key. A bad request or a busy day is not counted.",
        "parameters": [
          {
            "name": "grader",
            "in": "path",
            "required": true,
            "description": "psa, bgs, cgc or sgc.",
            "schema": {
              "type": "string",
              "enum": [
                "psa",
                "bgs",
                "cgc",
                "sgc"
              ]
            }
          },
          {
            "name": "certNumber",
            "in": "path",
            "required": true,
            "description": "The cert number printed on the slab label.",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The cert, the card and the slab block",
            "headers": {
              "X-Attribution": {
                "$ref": "#/components/headers/X-Attribution"
              },
              "X-HCI-Plan": {
                "$ref": "#/components/headers/X-HCI-Plan"
              },
              "X-HCI-Requests-Used": {
                "$ref": "#/components/headers/X-HCI-Requests-Used"
              },
              "X-HCI-Requests-Allowance": {
                "$ref": "#/components/headers/X-HCI-Requests-Allowance"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "X-HCI-Certs-Used": {
                "description": "Cert lookups used this month.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-HCI-Certs-Allowance": {
                "description": "The plan's monthly cert lookups.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/CertResponse"
                    },
                    "attribution": {
                      "$ref": "#/components/schemas/Attribution"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      }
    },
    "/api/v1/scan": {
      "post": {
        "tags": [
          "scan"
        ],
        "summary": "Identify a card from a photo",
        "operationId": "scanCard",
        "description": "Send one photo as base64 and get the card: the best match with its prices and a confidence from 0 to 1, up to 7 alternatives, and on a graded card the label read into `slab` (grader, grade, cert number, whether the cert verified, the price at that grade). Our own image index answers first; a miss falls through to a vision model within the plan's share. Commercial plans only (403 REQUIRES_PARTNER_PLAN on an Enterprise key), counted as a scan against the monthly scans, with its own per-second limit. A request without a usable photo is not charged; a bad photo is. 429 SCAN_ENGINE_BUSY carries Retry-After.",
        "requestBody": {
          "required": true,
          "description": "The photo and optional hints. Up to about 14 MB of JSON.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ScanRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The identification",
            "headers": {
              "X-Attribution": {
                "$ref": "#/components/headers/X-Attribution"
              },
              "X-HCI-Plan": {
                "$ref": "#/components/headers/X-HCI-Plan"
              },
              "X-HCI-Requests-Used": {
                "$ref": "#/components/headers/X-HCI-Requests-Used"
              },
              "X-HCI-Requests-Allowance": {
                "$ref": "#/components/headers/X-HCI-Requests-Allowance"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "X-HCI-Scans-Used": {
                "description": "Scans used this month, after this one.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-HCI-Scans-Allowance": {
                "description": "The plan's monthly scans.",
                "schema": {
                  "type": "integer"
                }
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/ScanResponse"
                    },
                    "attribution": {
                      "$ref": "#/components/schemas/Attribution"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "413": {
            "description": "IMAGE_TOO_LARGE: the photo is over 10 MB.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      }
    },
    "/api/v1/scan/{scanId}/feedback": {
      "post": {
        "tags": [
          "scan"
        ],
        "summary": "Tell us what the user said about a scan",
        "operationId": "scanFeedback",
        "description": "The user's answer to a scan: `correct` (our match was the card), `picked` (they chose another card, `cardId`) or `none` (none of our cards was it). A later answer replaces an earlier one, up to 30 days after the scan. Counts as one request, not a scan. 404 NOT_FOUND when the scan is not this account's or the card does not exist; 409 FEEDBACK_WINDOW_CLOSED after 30 days.",
        "parameters": [
          {
            "name": "scanId",
            "in": "path",
            "required": true,
            "description": "The scanId the scan returned.",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "description": "The answer.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "result": {
                    "type": "string",
                    "enum": [
                      "correct",
                      "picked",
                      "none"
                    ],
                    "description": "What the user said."
                  },
                  "cardId": {
                    "type": [
                      "integer",
                      "string"
                    ],
                    "description": "With picked: the card they chose, usually an `id` from `alternatives`."
                  }
                },
                "required": [
                  "result"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Saved",
            "headers": {
              "X-Attribution": {
                "$ref": "#/components/headers/X-Attribution"
              },
              "X-HCI-Plan": {
                "$ref": "#/components/headers/X-HCI-Plan"
              },
              "X-HCI-Requests-Used": {
                "$ref": "#/components/headers/X-HCI-Requests-Used"
              },
              "X-HCI-Requests-Allowance": {
                "$ref": "#/components/headers/X-HCI-Requests-Allowance"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "scanId": {
                          "type": "integer"
                        },
                        "result": {
                          "type": "string",
                          "enum": [
                            "correct",
                            "picked",
                            "none"
                          ]
                        },
                        "cardId": {
                          "type": [
                            "integer",
                            "null"
                          ],
                          "description": "The chosen card, with picked."
                        },
                        "recordedAt": {
                          "type": "string",
                          "format": "date-time"
                        }
                      },
                      "required": [
                        "scanId",
                        "result"
                      ]
                    },
                    "attribution": {
                      "$ref": "#/components/schemas/Attribution"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "description": "FEEDBACK_WINDOW_CLOSED: the scan is more than 30 days old.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      }
    },
    "/api/v1/collection": {
      "get": {
        "tags": [
          "collection"
        ],
        "summary": "The account's own collection",
        "operationId": "getCollection",
        "description": "Every card in the key owner's HobbyCardIndex collection with the grade, quantity, purchase price and notes they recorded. Dealer keys reach this endpoint too. One request.",
        "responses": {
          "200": {
            "description": "The collection",
            "headers": {
              "X-Attribution": {
                "$ref": "#/components/headers/X-Attribution"
              },
              "X-HCI-Plan": {
                "$ref": "#/components/headers/X-HCI-Plan"
              },
              "X-HCI-Requests-Used": {
                "$ref": "#/components/headers/X-HCI-Requests-Used"
              },
              "X-HCI-Requests-Allowance": {
                "$ref": "#/components/headers/X-HCI-Requests-Allowance"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/CollectionResponse"
                    },
                    "attribution": {
                      "$ref": "#/components/schemas/Attribution"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      }
    },
    "/api/v1/export/csv": {
      "get": {
        "tags": [
          "collection"
        ],
        "summary": "The account's collection as a CSV file",
        "operationId": "exportCollectionCsv",
        "description": "The same collection as a price-guide style CSV (columns id, console-name, product-name, loose-price, cib-price, new-price, graded-price, box-only-price, manual-only-price; raw and PSA 10 prices filled). Streamed as a download. One request.",
        "parameters": [
          {
            "name": "category",
            "in": "query",
            "required": false,
            "description": "Only cards in this category.",
            "schema": {
              "type": "string",
              "maxLength": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The CSV file",
            "headers": {
              "Content-Disposition": {
                "description": "attachment; filename=\"hci-...-YYYY-MM-DD.csv\"",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "text/csv": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      }
    },
    "/api/v1/market/movers": {
      "get": {
        "tags": [
          "market"
        ],
        "summary": "The biggest risers and fallers in one category",
        "operationId": "getMarketMovers",
        "description": "The same list as our Market Movers page: the 10 biggest risers, then the 10 biggest fallers, by the change in average raw price over the period against the period before. Rebuilt every 6 hours (`updatedAt`); 503 MOVERS_REFRESHING with Retry-After while a rebuild runs. An unknown category answers 400 with the full list. One request.",
        "parameters": [
          {
            "name": "category",
            "in": "query",
            "required": false,
            "description": "Category id: baseball, football, basketball, hockey, soccer, pokemon, magic-tcg, yu-gi-oh, one-piece and more. Default baseball.",
            "schema": {
              "type": "string",
              "maxLength": 50,
              "default": "baseball"
            }
          },
          {
            "name": "period",
            "in": "query",
            "required": false,
            "description": "Days: 1 for the last day, 2 to 7 for the last week, 8 or more for the last 30 days. Default 7.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 365,
              "default": 7
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Movers each way, 1 to 100 (commercial plans; other keys up to 10). Up to 10 each way is one request; every riser-and-faller pair past 10 counts one more (limit=100 is 91 requests), charged on what comes back.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 10
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Movers in the category",
            "headers": {
              "X-Attribution": {
                "$ref": "#/components/headers/X-Attribution"
              },
              "X-HCI-Plan": {
                "$ref": "#/components/headers/X-HCI-Plan"
              },
              "X-HCI-Requests-Used": {
                "$ref": "#/components/headers/X-HCI-Requests-Used"
              },
              "X-HCI-Requests-Allowance": {
                "$ref": "#/components/headers/X-HCI-Requests-Allowance"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/MoversResponse"
                    },
                    "attribution": {
                      "$ref": "#/components/schemas/Attribution"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      }
    },
    "/api/v1/market/categories": {
      "get": {
        "tags": [
          "market"
        ],
        "summary": "Every category with its card count and average prices",
        "operationId": "getCategoryStats",
        "description": "The categories we cover (the ids the other endpoints take), each with its card count, average raw and PSA 10 price and the newest sale we hold. One request.",
        "responses": {
          "200": {
            "description": "Category statistics",
            "headers": {
              "X-Attribution": {
                "$ref": "#/components/headers/X-Attribution"
              },
              "X-HCI-Plan": {
                "$ref": "#/components/headers/X-HCI-Plan"
              },
              "X-HCI-Requests-Used": {
                "$ref": "#/components/headers/X-HCI-Requests-Used"
              },
              "X-HCI-Requests-Allowance": {
                "$ref": "#/components/headers/X-HCI-Requests-Allowance"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/CategoriesResponse"
                    },
                    "attribution": {
                      "$ref": "#/components/schemas/Attribution"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      }
    },
    "/api/v1/market/indexes": {
      "get": {
        "tags": [
          "market"
        ],
        "summary": "Every market index with its latest value and 1, 7 and 30-day change",
        "operationId": "listMarketIndexes",
        "description": "The HCI market indexes. Category indexes (the default, 25): the average price of a fixed basket of the category's most-traded cards, up to 500, recomputed daily. Player indexes (type=player, about 7,800): a chain-linked, equal-weighted basket of up to 50 of the player's most-traded cards. Every commercial size (Lite, Growth Lite, Growth, Platform) and the free setup month; other keys get 403 REQUIRES_GROWTH. 503 INDEXES_REFRESHING with Retry-After before the first read lands. One request.",
        "parameters": [
          {
            "name": "type",
            "in": "query",
            "required": false,
            "description": "`category` (default) or `player`.",
            "schema": {
              "type": "string",
              "enum": [
                "category",
                "player"
              ],
              "default": "category"
            }
          },
          {
            "name": "category",
            "in": "query",
            "required": false,
            "description": "Only indexes in one category, for example `pokemon`.",
            "schema": {
              "type": "string",
              "maxLength": 40
            }
          },
          {
            "name": "search",
            "in": "query",
            "required": false,
            "description": "Part of a name, for example `ohtani`.",
            "schema": {
              "type": "string",
              "maxLength": 100
            }
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "description": "Default dollarVolume (30-day sales in dollars).",
            "schema": {
              "type": "string",
              "enum": [
                "dollarVolume",
                "sales",
                "value",
                "change1d",
                "change7d",
                "change30d",
                "name"
              ],
              "default": "dollarVolume"
            }
          },
          {
            "name": "order",
            "in": "query",
            "required": false,
            "description": "Default desc (asc for name).",
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Up to 100 categories or 200 players. Default 100 / 50.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "Default 0.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The indexes",
            "headers": {
              "X-Attribution": {
                "$ref": "#/components/headers/X-Attribution"
              },
              "X-HCI-Plan": {
                "$ref": "#/components/headers/X-HCI-Plan"
              },
              "X-HCI-Requests-Used": {
                "$ref": "#/components/headers/X-HCI-Requests-Used"
              },
              "X-HCI-Requests-Allowance": {
                "$ref": "#/components/headers/X-HCI-Requests-Allowance"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/MarketIndexList"
                    },
                    "attribution": {
                      "$ref": "#/components/schemas/Attribution"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      }
    },
    "/api/v1/market/indexes/{id}": {
      "get": {
        "tags": [
          "market"
        ],
        "summary": "One market index",
        "operationId": "getMarketIndex",
        "description": "One index with its latest value and change. Every commercial size (Lite, Growth Lite, Growth, Platform) and the free setup month; other keys get 403 REQUIRES_GROWTH. 503 INDEXES_REFRESHING with Retry-After before the first read lands. One request.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "An index id from GET /api/v1/market/indexes: a category (`pokemon`) or a player (`player-shohei-ohtani`).",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9-]{0,99}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The index",
            "headers": {
              "X-Attribution": {
                "$ref": "#/components/headers/X-Attribution"
              },
              "X-HCI-Plan": {
                "$ref": "#/components/headers/X-HCI-Plan"
              },
              "X-HCI-Requests-Used": {
                "$ref": "#/components/headers/X-HCI-Requests-Used"
              },
              "X-HCI-Requests-Allowance": {
                "$ref": "#/components/headers/X-HCI-Requests-Allowance"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "index": {
                          "$ref": "#/components/schemas/MarketIndex"
                        },
                        "updatedAt": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "format": "date-time",
                          "description": "When the indexes were last read."
                        }
                      },
                      "required": [
                        "index"
                      ]
                    },
                    "attribution": {
                      "$ref": "#/components/schemas/Attribution"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      }
    },
    "/api/v1/market/indexes/{id}/history": {
      "get": {
        "tags": [
          "market"
        ],
        "summary": "A market index day by day",
        "operationId": "getMarketIndexHistory",
        "description": "Daily values, counted back from the index's latest day. Category history from March 2026 (June 2026 for the newer categories), player history from September 2026. Every commercial size (Lite, Growth Lite, Growth, Platform) and the free setup month; other keys get 403 REQUIRES_GROWTH. 503 INDEXES_REFRESHING with Retry-After before the first read lands. One request.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "An index id from GET /api/v1/market/indexes: a category (`pokemon`) or a player (`player-shohei-ohtani`).",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9-]{0,99}$"
            }
          },
          {
            "name": "days",
            "in": "query",
            "required": false,
            "description": "1 to 365. Default 90.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 365,
              "default": 90
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The daily values",
            "headers": {
              "X-Attribution": {
                "$ref": "#/components/headers/X-Attribution"
              },
              "X-HCI-Plan": {
                "$ref": "#/components/headers/X-HCI-Plan"
              },
              "X-HCI-Requests-Used": {
                "$ref": "#/components/headers/X-HCI-Requests-Used"
              },
              "X-HCI-Requests-Allowance": {
                "$ref": "#/components/headers/X-HCI-Requests-Allowance"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/MarketIndexHistory"
                    },
                    "attribution": {
                      "$ref": "#/components/schemas/Attribution"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      }
    },
    "/api/v1/market/indexes/{id}/cards": {
      "get": {
        "tags": [
          "market"
        ],
        "summary": "The cards in a market index basket",
        "operationId": "getMarketIndexCards",
        "description": "The basket in order, with the price each card counts at and its card id for the card and sales endpoints. Every commercial size (Lite, Growth Lite, Growth, Platform) and the free setup month; other keys get 403 REQUIRES_GROWTH. 503 INDEXES_REFRESHING with Retry-After before the first read lands. One request.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "An index id from GET /api/v1/market/indexes: a category (`pokemon`) or a player (`player-shohei-ohtani`).",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9-]{0,99}$"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Up to 500. Default 100.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500,
              "default": 100
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "Default 0.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "maximum": 500,
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The basket",
            "headers": {
              "X-Attribution": {
                "$ref": "#/components/headers/X-Attribution"
              },
              "X-HCI-Plan": {
                "$ref": "#/components/headers/X-HCI-Plan"
              },
              "X-HCI-Requests-Used": {
                "$ref": "#/components/headers/X-HCI-Requests-Used"
              },
              "X-HCI-Requests-Allowance": {
                "$ref": "#/components/headers/X-HCI-Requests-Allowance"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/MarketIndexCards"
                    },
                    "attribution": {
                      "$ref": "#/components/schemas/Attribution"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      }
    },
    "/api/v1/market/summary": {
      "get": {
        "tags": [
          "market"
        ],
        "summary": "Index change, sales change and the top movers in one call",
        "operationId": "getMarketSummary",
        "description": "With category: its index and change, its sales over 7 and 30 days, the 5 biggest rising and falling cards over 7 days and the 5 biggest player index moves in the category. Without: every category index, the biggest 7-day gainers and losers, and the biggest player index moves. Every commercial size (Lite, Growth Lite, Growth, Platform) and the free setup month; other keys get 403 REQUIRES_GROWTH. 503 INDEXES_REFRESHING with Retry-After before the first read lands. One request.",
        "parameters": [
          {
            "name": "category",
            "in": "query",
            "required": false,
            "description": "A category id, for example `pokemon`. Leave out for the whole market.",
            "schema": {
              "type": "string",
              "maxLength": 40
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The summary",
            "headers": {
              "X-Attribution": {
                "$ref": "#/components/headers/X-Attribution"
              },
              "X-HCI-Plan": {
                "$ref": "#/components/headers/X-HCI-Plan"
              },
              "X-HCI-Requests-Used": {
                "$ref": "#/components/headers/X-HCI-Requests-Used"
              },
              "X-HCI-Requests-Allowance": {
                "$ref": "#/components/headers/X-HCI-Requests-Allowance"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/MarketSummary"
                    },
                    "attribution": {
                      "$ref": "#/components/schemas/Attribution"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      }
    },
    "/api/v1/bulk/prices": {
      "get": {
        "tags": [
          "bulk files"
        ],
        "summary": "The daily price files: categories, parts and when they were built",
        "operationId": "getBulkManifest",
        "description": "The manifest of the gzipped per-category price CSVs (every grade plus volume), rebuilt daily. Platform, the price-files add-on on Growth Lite or Growth, or an Enterprise key for internal use; never the free setup month (403 REQUIRES_PLATFORM_PLAN). One request.",
        "responses": {
          "200": {
            "description": "The manifest",
            "headers": {
              "X-Attribution": {
                "$ref": "#/components/headers/X-Attribution"
              },
              "X-HCI-Plan": {
                "$ref": "#/components/headers/X-HCI-Plan"
              },
              "X-HCI-Requests-Used": {
                "$ref": "#/components/headers/X-HCI-Requests-Used"
              },
              "X-HCI-Requests-Allowance": {
                "$ref": "#/components/headers/X-HCI-Requests-Allowance"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/BulkManifest"
                    },
                    "attribution": {
                      "$ref": "#/components/schemas/Attribution"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      }
    },
    "/api/v1/bulk/prices/{category}": {
      "get": {
        "tags": [
          "bulk files"
        ],
        "summary": "Download one category's price file",
        "operationId": "downloadBulkPrices",
        "description": "The full category as CSV (decompressed on the way out) or, with `format=gz`, the raw gzipped file. Big categories are split into parts of about 500,000 rows; the manifest lists each category's part count. Same plans as the manifest. One request.",
        "parameters": [
          {
            "name": "category",
            "in": "path",
            "required": true,
            "description": "The category slug from the manifest.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9-]{1,40}$"
            }
          },
          {
            "name": "part",
            "in": "query",
            "required": false,
            "description": "Which part, from 1. Default 1.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          },
          {
            "name": "format",
            "in": "query",
            "required": false,
            "description": "`gz` for the gzipped file; anything else streams plain CSV.",
            "schema": {
              "type": "string",
              "enum": [
                "csv",
                "gz"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The file",
            "headers": {
              "Content-Disposition": {
                "description": "attachment; filename=\"hci-prices-<slug>-<part>-YYYY-MM-DD.csv[.gz]\"",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "text/csv": {
                "schema": {
                  "type": "string"
                }
              },
              "application/gzip": {
                "schema": {
                  "type": "string",
                  "contentEncoding": "binary"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      }
    },
    "/api/v1/webhook": {
      "get": {
        "tags": [
          "watch list and webhooks"
        ],
        "summary": "Your price webhook, its hour and last delivery",
        "operationId": "getWebhook",
        "description": "The endpoint you set, the hour (UTC) it is called, the last delivery, how many cards you watch and your watch limit. Every commercial size; other keys get 403 REQUIRES_GROWTH. One request.",
        "responses": {
          "200": {
            "description": "The webhook and the watch count",
            "headers": {
              "X-Attribution": {
                "$ref": "#/components/headers/X-Attribution"
              },
              "X-HCI-Plan": {
                "$ref": "#/components/headers/X-HCI-Plan"
              },
              "X-HCI-Requests-Used": {
                "$ref": "#/components/headers/X-HCI-Requests-Used"
              },
              "X-HCI-Requests-Allowance": {
                "$ref": "#/components/headers/X-HCI-Requests-Allowance"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/WebhookStatus"
                    },
                    "attribution": {
                      "$ref": "#/components/schemas/Attribution"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      },
      "put": {
        "tags": [
          "watch list and webhooks"
        ],
        "summary": "Set the daily price webhook",
        "operationId": "setWebhook",
        "description": "Register an https URL and the hour (UTC) you want it called. Once a day we compare every watched card with the prices we last sent you and POST the changes (event `prices.changed`, up to 1,000 cards a request), and any cards newly added to the sets you follow (`cards.added`). Deliveries are signed: `HCI-Signature: t=<unix time>,v1=<hex>`, the HMAC-SHA256 of `<t>.<raw body>` with your secret. The secret is returned once, on the call that creates or rotates it. Failed deliveries retry after 5 and 30 minutes and 2, 6 and 12 hours; after 3 deliveries fail every retry the webhook pauses, and setting the URL again turns it back on. Deliveries do not count against your requests.",
        "requestBody": {
          "required": true,
          "description": "The endpoint and hour.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookPut"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The webhook as stored (with `secret` when new or rotated)",
            "headers": {
              "X-Attribution": {
                "$ref": "#/components/headers/X-Attribution"
              },
              "X-HCI-Plan": {
                "$ref": "#/components/headers/X-HCI-Plan"
              },
              "X-HCI-Requests-Used": {
                "$ref": "#/components/headers/X-HCI-Requests-Used"
              },
              "X-HCI-Requests-Allowance": {
                "$ref": "#/components/headers/X-HCI-Requests-Allowance"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "webhook": {
                          "$ref": "#/components/schemas/WebhookEndpoint"
                        }
                      },
                      "required": [
                        "webhook"
                      ]
                    },
                    "attribution": {
                      "$ref": "#/components/schemas/Attribution"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      },
      "delete": {
        "tags": [
          "watch list and webhooks"
        ],
        "summary": "Turn the webhook off",
        "operationId": "deleteWebhook",
        "description": "Stop the daily call. The watch list stays. One request.",
        "responses": {
          "200": {
            "description": "The webhook after it was turned off",
            "headers": {
              "X-Attribution": {
                "$ref": "#/components/headers/X-Attribution"
              },
              "X-HCI-Plan": {
                "$ref": "#/components/headers/X-HCI-Plan"
              },
              "X-HCI-Requests-Used": {
                "$ref": "#/components/headers/X-HCI-Requests-Used"
              },
              "X-HCI-Requests-Allowance": {
                "$ref": "#/components/headers/X-HCI-Requests-Allowance"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "type": "object",
                      "additionalProperties": true
                    },
                    "attribution": {
                      "$ref": "#/components/schemas/Attribution"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      }
    },
    "/api/v1/webhook/test": {
      "post": {
        "tags": [
          "watch list and webhooks"
        ],
        "summary": "Send a signed ping to your webhook now",
        "operationId": "testWebhook",
        "description": "A signed `ping` event to the URL you set, right away, so you can check your signature code. One request.",
        "responses": {
          "200": {
            "description": "The delivery result",
            "headers": {
              "X-Attribution": {
                "$ref": "#/components/headers/X-Attribution"
              },
              "X-HCI-Plan": {
                "$ref": "#/components/headers/X-HCI-Plan"
              },
              "X-HCI-Requests-Used": {
                "$ref": "#/components/headers/X-HCI-Requests-Used"
              },
              "X-HCI-Requests-Allowance": {
                "$ref": "#/components/headers/X-HCI-Requests-Allowance"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "type": "object",
                      "additionalProperties": true
                    },
                    "attribution": {
                      "$ref": "#/components/schemas/Attribution"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      }
    },
    "/api/v1/watchlist": {
      "get": {
        "tags": [
          "watch list and webhooks"
        ],
        "summary": "The cards you watch and the sets you follow",
        "operationId": "getWatchlist",
        "description": "The watched cards (no prices) and the sets or categories you follow. Every commercial size; one request.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size, 1 to 5,000. Default 1,000.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 5000,
              "default": 1000
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "How many to skip.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of the watch list",
            "headers": {
              "X-Attribution": {
                "$ref": "#/components/headers/X-Attribution"
              },
              "X-HCI-Plan": {
                "$ref": "#/components/headers/X-HCI-Plan"
              },
              "X-HCI-Requests-Used": {
                "$ref": "#/components/headers/X-HCI-Requests-Used"
              },
              "X-HCI-Requests-Allowance": {
                "$ref": "#/components/headers/X-HCI-Requests-Allowance"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/WatchlistPage"
                    },
                    "attribution": {
                      "$ref": "#/components/schemas/Attribution"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      },
      "post": {
        "tags": [
          "watch list and webhooks"
        ],
        "summary": "Watch cards, a whole set or a whole category",
        "operationId": "addToWatchlist",
        "description": "Add cards by id (up to 1,000 a call), or a whole set or category. A set or category stays followed: cards we add to it later join your list on their own and come in the daily webhook as `cards.added`. Watch limits by plan: Lite 5,000 cards, Growth Lite 25,000, Growth 100,000, Platform 500,000, the free setup month 1,000 (409 WATCH_LIMIT_REACHED past it). One request.",
        "requestBody": {
          "required": true,
          "description": "What to watch.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WatchlistAdd"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "What changed: how many were added, already watched, not found, and your total",
            "headers": {
              "X-Attribution": {
                "$ref": "#/components/headers/X-Attribution"
              },
              "X-HCI-Plan": {
                "$ref": "#/components/headers/X-HCI-Plan"
              },
              "X-HCI-Requests-Used": {
                "$ref": "#/components/headers/X-HCI-Requests-Used"
              },
              "X-HCI-Requests-Allowance": {
                "$ref": "#/components/headers/X-HCI-Requests-Allowance"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/WatchlistChange"
                    },
                    "attribution": {
                      "$ref": "#/components/schemas/Attribution"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "WATCH_LIMIT_REACHED: the list is full for your plan (`details.total`, `details.limit`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      },
      "delete": {
        "tags": [
          "watch list and webhooks"
        ],
        "summary": "Take cards off the watch list (same body as POST /watchlist/remove)",
        "operationId": "removeFromWatchlistDelete",
        "description": "Remove cards by id, a set you follow (send the fields you added it with) or everything with `{\"all\": true}`. One request.",
        "requestBody": {
          "required": true,
          "description": "What to remove.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WatchlistRemove"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "What changed",
            "headers": {
              "X-Attribution": {
                "$ref": "#/components/headers/X-Attribution"
              },
              "X-HCI-Plan": {
                "$ref": "#/components/headers/X-HCI-Plan"
              },
              "X-HCI-Requests-Used": {
                "$ref": "#/components/headers/X-HCI-Requests-Used"
              },
              "X-HCI-Requests-Allowance": {
                "$ref": "#/components/headers/X-HCI-Requests-Allowance"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/WatchlistChange"
                    },
                    "attribution": {
                      "$ref": "#/components/schemas/Attribution"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      }
    },
    "/api/v1/watchlist/remove": {
      "post": {
        "tags": [
          "watch list and webhooks"
        ],
        "summary": "Take cards off the watch list",
        "operationId": "removeFromWatchlist",
        "description": "Remove cards by id, a set you follow (that set's cards come off and it stops being followed) or everything with `{\"all\": true}`. For clients that cannot send a body with DELETE. One request.",
        "requestBody": {
          "required": true,
          "description": "What to remove.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WatchlistRemove"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "What changed",
            "headers": {
              "X-Attribution": {
                "$ref": "#/components/headers/X-Attribution"
              },
              "X-HCI-Plan": {
                "$ref": "#/components/headers/X-HCI-Plan"
              },
              "X-HCI-Requests-Used": {
                "$ref": "#/components/headers/X-HCI-Requests-Used"
              },
              "X-HCI-Requests-Allowance": {
                "$ref": "#/components/headers/X-HCI-Requests-Allowance"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/WatchlistChange"
                    },
                    "attribution": {
                      "$ref": "#/components/schemas/Attribution"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      }
    },
    "/api/v1/watchlist/prices": {
      "get": {
        "tags": [
          "watch list and webhooks"
        ],
        "summary": "Current prices for every watched card, in one call",
        "operationId": "downloadWatchlistPrices",
        "description": "Every watched card with its prices at every grade, its last verified sale, its 90-day sale count and a last sale per grade lane, streamed as JSON or, with `format=csv`, as a CSV download. Prices change once a day, so once a day is plenty: one download a minute per account (429 RATE_LIMITED). The response is streamed, so it carries no attribution object; the credit rule still applies. One request.",
        "parameters": [
          {
            "name": "format",
            "in": "query",
            "required": false,
            "description": "`csv` for a CSV download; otherwise JSON.",
            "schema": {
              "type": "string",
              "enum": [
                "json",
                "csv"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Prices for the watched cards",
            "headers": {
              "Content-Disposition": {
                "description": "CSV only: attachment; filename=\"hci-watchlist-prices.csv\"",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WatchlistPricesResponse"
                }
              },
              "text/csv": {
                "schema": {
                  "type": "string",
                  "description": "Columns: id, externalId, name, setName, cardNumber, category, year, loose, psa10, psa9, psa8, psa7, bgs10, cgc10, sgc10, volume, updated, lastSalePrice, lastSaleAt, lastSaleGrade, sales90d, then lastSale_<grade>_price, _soldAt and _url per grade lane."
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      }
    },
    "/api/v1/mcp": {
      "post": {
        "tags": [
          "mcp"
        ],
        "summary": "MCP server for AI assistants (JSON-RPC 2.0 over Streamable HTTP)",
        "operationId": "mcp",
        "description": "The Model Context Protocol server. POST one JSON-RPC 2.0 message, or a batch of up to 10, as application/json; the answer is JSON (no SSE stream, and GET answers 405). Stateless: no Mcp-Session-Id. Protocol versions 2024-11-05 through 2025-11-25. The manifest is at https://hobbycardindex.com/.well-known/mcp.json; the registry name is com.hobbycardindex/card-prices.\n\n`initialize`, `ping` and `tools/list` work without a key, capped per address. Every data `tools/call` needs the same X-API-Key (or Authorization: Bearer) as the REST API and counts toward the same plan and limits; a call without a key gets a tool error that says where to get one. The buy tools work without a key.\n\nTools:\n- `search_cards`: Find a sports or trading card and its current sold price. Describe the card the way a collector would, for example \"1989 Upper Deck Ken Griffey Jr 1\", \"2018 Topps Update Acuna US250\" or \"Charizard Base Set\". Returns up to 5 matching cards with the raw (ungraded) and PSA 10 price, a card_id, and the link to the card page. Use get_card_prices for every grade of one card. Needs a HobbyCardIndex API key.\n- `get_card_prices`: Current sold prices for one card at every grade: raw (ungraded), PSA 7, PSA 8, PSA 9, PSA 10, BGS 9.5, CGC 10 and SGC 10, in US dollars, with the link to the card page for recent sales and price history. Take the card_id from search_cards. Needs a HobbyCardIndex API key.\n- `list_plans`: The HobbyCardIndex plans a user can buy right now: the Commercial API sizes (requests, photo scans, sold-sale lookups, cert lookups) and the app plans Pro, Dealer and Enterprise (scans a month, dealer tools, API key on Enterprise), with monthly prices in US dollars, the terms and how to buy. Pass use_case to get the size and add-ons that fit what the user wants to do. Works without an API key.\n- `start_checkout`: Start buying a Commercial API plan for the user. Returns a Stripe checkout_url where the user pays by card (HobbyCardIndex never sees the card) and a status_url that returns the API key once, after payment. Only call this after the user has picked a plan from list_plans and agreed to the terms (accept_terms: true). Use the user's own email: the receipt and the account sign-in go there. Works without an API key.\n\nA successful data lookup's result ends with a text item: \"Prices from Hobby Card Index. Credit https://hobbycardindex.com when you show this data.\"",
        "requestBody": {
          "required": true,
          "description": "A JSON-RPC 2.0 message or batch.",
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/JsonRpcRequest"
                  },
                  {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/JsonRpcRequest"
                    },
                    "minItems": 1,
                    "maxItems": 10
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The JSON-RPC response, or an array of them for a batch",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/JsonRpcResponse"
                    },
                    {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/JsonRpcResponse"
                      }
                    }
                  ]
                }
              }
            }
          },
          "202": {
            "description": "The message was a notification (no id): accepted, no body."
          },
          "400": {
            "description": "Parse error (-32700) or Invalid Request (-32600), as a JSON-RPC error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JsonRpcResponse"
                }
              }
            }
          },
          "405": {
            "description": "Any method other than POST. The Allow header says POST.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JsonRpcResponse"
                }
              }
            }
          }
        },
        "security": [
          {},
          {
            "ApiKeyAuth": []
          }
        ],
        "x-mcp": {
          "transport": "streamable-http",
          "manifest": "https://hobbycardindex.com/.well-known/mcp.json",
          "registryName": "com.hobbycardindex/card-prices",
          "tools": [
            "search_cards",
            "get_card_prices",
            "list_plans",
            "start_checkout"
          ]
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-Key",
        "description": "Your HobbyCardIndex API key (hci_live_...). `Authorization: Bearer <key>` is accepted too. Keys: https://hobbycardindex.com/developers."
      }
    },
    "schemas": {
      "Attribution": {
        "type": "object",
        "properties": {
          "text": {
            "type": "string",
            "const": "Prices from Hobby Card Index"
          },
          "url": {
            "type": "string",
            "format": "uri",
            "const": "https://hobbycardindex.com"
          },
          "html": {
            "type": "string",
            "const": "<a href=\"https://hobbycardindex.com\" rel=\"noopener\">Prices from Hobby Card Index</a>",
            "description": "Ready to paste: the credit as a link."
          },
          "required": {
            "type": "boolean",
            "const": true
          },
          "terms": {
            "type": "string",
            "format": "uri",
            "const": "https://hobbycardindex.com/terms/"
          }
        },
        "required": [
          "text",
          "url",
          "html",
          "required",
          "terms"
        ],
        "description": "The credit this data must carry wherever it is shown: a visible \"Prices from Hobby Card Index\" link to https://hobbycardindex.com. Added to every successful JSON object response (also sent as the X-Attribution header)."
      },
      "Error": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "const": false,
            "description": "false on every error the API itself sends. The per-second rate limiter's 429 body has only `error`."
          },
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "description": "Machine-readable code, for example VALIDATION_ERROR, INVALID_PARAM, MISSING_API_KEY, INVALID_API_KEY, KEY_REVOKED, PLAN_REQUIRED, TRIAL_ENDED, NOT_ON_PLAN, REQUIRES_COMMERCIAL_LICENSE, REQUIRES_GROWTH, REQUIRES_PARTNER_PLAN, RATE_LIMITED, MONTHLY_LIMIT_REACHED, DAILY_LIMIT_REACHED, NOT_FOUND, SERVER_ERROR."
              },
              "message": {
                "type": "string",
                "description": "What went wrong and, where there is one, what to do about it."
              },
              "details": {
                "type": "object",
                "additionalProperties": true,
                "description": "Extra fields on some errors: plans_url, upgrade_url, limit, allowance, plan, upgrade."
              }
            },
            "required": [
              "code",
              "message"
            ]
          }
        },
        "required": [
          "error"
        ],
        "description": "The error envelope from every endpoint."
      },
      "PriceGrid": {
        "type": "object",
        "properties": {
          "loose": {
            "type": [
              "number",
              "null"
            ],
            "description": "raw (ungraded) price in US dollars; null when there is no price at that grade."
          },
          "psa10": {
            "type": [
              "number",
              "null"
            ],
            "description": "PSA 10 price in US dollars; null when there is no price at that grade."
          },
          "psa9": {
            "type": [
              "number",
              "null"
            ],
            "description": "PSA 9 price in US dollars; null when there is no price at that grade."
          },
          "psa8": {
            "type": [
              "number",
              "null"
            ],
            "description": "PSA 8 price in US dollars; null when there is no price at that grade."
          },
          "psa7": {
            "type": [
              "number",
              "null"
            ],
            "description": "PSA 7 price in US dollars; null when there is no price at that grade."
          },
          "bgs10": {
            "type": [
              "number",
              "null"
            ],
            "description": "BGS 9.5 (BGS 10 when there is no 9.5 price) price in US dollars; null when there is no price at that grade."
          },
          "cgc10": {
            "type": [
              "number",
              "null"
            ],
            "description": "CGC 10 price in US dollars; null when there is no price at that grade."
          },
          "sgc10": {
            "type": [
              "number",
              "null"
            ],
            "description": "SGC 10 price in US dollars; null when there is no price at that grade."
          }
        },
        "description": "Current prices by grade, in US dollars with two decimals. A grade with no price is null; `bgs10` holds the BGS 9.5 price."
      },
      "Prices": {
        "allOf": [
          {
            "$ref": "#/components/schemas/PriceGrid"
          },
          {
            "type": "object",
            "properties": {
              "volume": {
                "type": [
                  "integer",
                  "null"
                ],
                "description": "Recent sales volume, a count."
              },
              "updated": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time",
                "description": "When this card's prices were last refreshed."
              },
              "fromSales": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Grades (keys of this object) whose price came from our own recent sold sales because no other price was on file for that grade."
              }
            }
          }
        ],
        "description": "The price block of GET /api/v1/prices/{cardId}: the grade grid plus volume and the last update."
      },
      "SearchPrices": {
        "allOf": [
          {
            "$ref": "#/components/schemas/PriceGrid"
          },
          {
            "type": "object",
            "properties": {
              "volume": {
                "type": [
                  "integer",
                  "null"
                ],
                "description": "Recent sales volume, a count."
              },
              "fromSales": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Grades whose price came from our own recent sold sales."
              }
            }
          }
        ],
        "description": "The price block of a search result. null on the whole block when the card has no price at any grade, and on every result for keys on the pop + certs plan."
      },
      "SearchResult": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "description": "The catalog id. Pass it to GET /api/v1/prices/{cardId} and the other card endpoints."
          },
          "externalId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Secondary id; the card endpoints also accept `ext-<externalId>`."
          },
          "name": {
            "type": "string",
            "description": "Card name; a parallel is named in brackets."
          },
          "year": {
            "type": [
              "integer",
              "string",
              "null"
            ]
          },
          "setName": {
            "type": [
              "string",
              "null"
            ],
            "description": "Set name."
          },
          "setId": {
            "type": [
              "string",
              "null"
            ],
            "description": "The set id GET /api/v1/sets lists and `set=` takes."
          },
          "cardNumber": {
            "type": [
              "string",
              "null"
            ],
            "description": "The number printed on the card."
          },
          "printRun": {
            "type": [
              "integer",
              "null"
            ],
            "description": "For numbered cards, the print run (50 for a card numbered to /50); null when not numbered or unknown."
          },
          "category": {
            "type": [
              "string",
              "null"
            ],
            "description": "Category."
          },
          "playerName": {
            "type": [
              "string",
              "null"
            ],
            "description": "Player or character."
          },
          "imageUrl": {
            "type": [
              "string",
              "null"
            ],
            "description": "The card's picture through our image link, or null when we have no usable picture. The link needs no key; it can answer 404 later if the picture is withdrawn.",
            "format": "uri"
          },
          "addedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the card joined our catalog."
          },
          "prices": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/SearchPrices"
              },
              {
                "type": "null"
              }
            ]
          },
          "hciUrl": {
            "type": [
              "string",
              "null"
            ],
            "description": "The card's page on hobbycardindex.com, with recent sales and price history.",
            "format": "uri"
          }
        },
        "required": [
          "id",
          "name"
        ]
      },
      "SearchResponse": {
        "type": "object",
        "properties": {
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SearchResult"
            }
          },
          "count": {
            "type": "integer",
            "description": "How many results are in this response."
          },
          "limit": {
            "type": "integer",
            "description": "Filtered searches only: the page size."
          },
          "offset": {
            "type": "integer",
            "description": "Filtered searches only: how many were skipped."
          },
          "hasMore": {
            "type": "boolean",
            "description": "Filtered searches only: whether another page exists."
          }
        },
        "required": [
          "results",
          "count"
        ]
      },
      "Card": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "externalId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Secondary id; also accepted as `ext-<externalId>`."
          },
          "name": {
            "type": "string",
            "description": "Card name; a parallel is named in brackets."
          },
          "year": {
            "type": [
              "integer",
              "string",
              "null"
            ]
          },
          "setName": {
            "type": [
              "string",
              "null"
            ],
            "description": "Set name."
          },
          "cardNumber": {
            "type": [
              "string",
              "null"
            ],
            "description": "The number printed on the card."
          },
          "printRun": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Print run for numbered cards; null otherwise."
          },
          "category": {
            "type": [
              "string",
              "null"
            ],
            "description": "Category."
          },
          "playerName": {
            "type": [
              "string",
              "null"
            ],
            "description": "Player or character."
          },
          "imageUrl": {
            "type": [
              "string",
              "null"
            ],
            "description": "Picture through our image link, or null.",
            "format": "uri"
          },
          "hciUrl": {
            "type": [
              "string",
              "null"
            ],
            "description": "The card's page on hobbycardindex.com.",
            "format": "uri"
          }
        },
        "required": [
          "id",
          "name"
        ]
      },
      "CardPricesResponse": {
        "type": "object",
        "properties": {
          "card": {
            "$ref": "#/components/schemas/Card"
          },
          "prices": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Prices"
              },
              {
                "type": "null"
              }
            ],
            "description": "null when we hold no price for this card."
          }
        },
        "required": [
          "card",
          "prices"
        ]
      },
      "CardRef": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "description": "The catalog id. Pass it to the other card endpoints."
          },
          "externalId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Our catalog's secondary id for the card. Accepted by the card endpoints as `ext-<externalId>`."
          },
          "name": {
            "type": "string",
            "description": "The card name, with the parallel in brackets when it is not the base card, for example `Mike Trout [Gold] #US175`."
          },
          "year": {
            "type": [
              "integer",
              "string",
              "null"
            ],
            "description": "The year the card came out (for Pokemon, the year its set was released); null when unknown."
          },
          "setName": {
            "type": [
              "string",
              "null"
            ],
            "description": "The set the card belongs to."
          },
          "category": {
            "type": [
              "string",
              "null"
            ],
            "description": "The category, for example Baseball or Pokemon."
          },
          "playerName": {
            "type": [
              "string",
              "null"
            ],
            "description": "The player or character on the card."
          }
        },
        "required": [
          "id",
          "name"
        ],
        "description": "The card block the sales, listings, history and population endpoints return."
      },
      "Sale": {
        "type": "object",
        "properties": {
          "soldAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the sale closed."
          },
          "price": {
            "type": "number",
            "description": "What was paid, in US dollars."
          },
          "grade": {
            "type": [
              "string",
              "null"
            ],
            "description": "The grade as listed, for example `PSA 10`; null for a raw card."
          },
          "gradingCompany": {
            "type": [
              "string",
              "null"
            ],
            "description": "PSA, BGS, SGC, CGC or null."
          },
          "title": {
            "type": [
              "string",
              "null"
            ],
            "description": "The listing title."
          },
          "auction": {
            "type": "boolean",
            "description": "true when the sale was an auction. `saleType` carries the full answer."
          },
          "saleType": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "auction",
              "buy_it_now",
              "best_offer",
              null
            ],
            "description": "How the item sold; null when the marketplace did not say."
          },
          "askingPrice": {
            "type": [
              "number",
              "null"
            ],
            "description": "On best_offer sales, the asking price; `price` is the accepted offer."
          },
          "bids": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Bid count on an auction, when reported."
          },
          "source": {
            "type": "string",
            "description": "The marketplace it sold on, for example ebay, tcgplayer, fanatics-collect, goldin, heritage, or marketplace."
          },
          "url": {
            "type": [
              "string",
              "null"
            ],
            "description": "The listing page.",
            "format": "uri"
          },
          "imageUrl": {
            "type": [
              "string",
              "null"
            ],
            "description": "The listing photo's link (not a copy of the photo).",
            "format": "uri"
          }
        },
        "required": [
          "price",
          "auction",
          "source"
        ]
      },
      "SalesResponse": {
        "type": "object",
        "properties": {
          "card": {
            "$ref": "#/components/schemas/CardRef"
          },
          "periodDays": {
            "type": "integer",
            "description": "The lookback window that was applied."
          },
          "sample": {
            "type": "string",
            "enum": [
              "latest",
              "daily",
              "spread"
            ],
            "description": "How the sales were picked."
          },
          "everyDays": {
            "type": "integer",
            "description": "With sample=daily: the days each sale stands for."
          },
          "summary": {
            "type": "object",
            "properties": {
              "count": {
                "type": "integer"
              },
              "average": {
                "type": [
                  "number",
                  "null"
                ],
                "description": "Average sale price in the window."
              },
              "low": {
                "type": [
                  "number",
                  "null"
                ],
                "description": "Lowest sale price in the window."
              },
              "high": {
                "type": [
                  "number",
                  "null"
                ],
                "description": "Highest sale price in the window."
              }
            },
            "required": [
              "count"
            ]
          },
          "sales": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Sale"
            },
            "description": "Verified sales, newest first: the newest 100 in each grade for metered plans."
          }
        },
        "required": [
          "card",
          "periodDays",
          "summary",
          "sales"
        ]
      },
      "Listing": {
        "type": "object",
        "properties": {
          "itemId": {
            "type": [
              "string",
              "null"
            ],
            "description": "The marketplace item id."
          },
          "title": {
            "type": "string",
            "description": "Listing title."
          },
          "price": {
            "type": "number",
            "description": "Item price in US dollars."
          },
          "shipping": {
            "type": [
              "number",
              "null"
            ],
            "description": "Shipping cost when the marketplace reported it."
          },
          "total": {
            "type": [
              "number",
              "null"
            ],
            "description": "price + shipping, when shipping is known."
          },
          "currency": {
            "type": "string",
            "const": "USD"
          },
          "grade": {
            "type": "string",
            "description": "`raw`, `graded`, or the grader and grade, for example `PSA 10`."
          },
          "condition": {
            "type": [
              "string",
              "null"
            ],
            "description": "The listing's condition text."
          },
          "marketValue": {
            "type": [
              "number",
              "null"
            ],
            "description": "Our price for the card at the listing's grade."
          },
          "belowMarketPct": {
            "type": [
              "number",
              "null"
            ],
            "description": "How far under marketValue the listing is, as a percentage."
          },
          "seller": {
            "type": "object",
            "properties": {
              "username": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Seller name."
              },
              "feedbackPercent": {
                "type": [
                  "number",
                  "null"
                ],
                "description": "Seller feedback percentage."
              },
              "feedbackScore": {
                "type": [
                  "integer",
                  "null"
                ],
                "description": "Seller feedback score."
              },
              "topRated": {
                "type": "boolean",
                "description": "Top-rated seller."
              }
            }
          },
          "imageUrl": {
            "type": [
              "string",
              "null"
            ],
            "description": "The listing photo's link.",
            "format": "uri"
          },
          "url": {
            "type": [
              "string",
              "null"
            ],
            "description": "The listing, with our affiliate tag.",
            "format": "uri"
          }
        },
        "required": [
          "title",
          "price",
          "grade",
          "url"
        ]
      },
      "ListingsResponse": {
        "type": "object",
        "properties": {
          "card": {
            "$ref": "#/components/schemas/CardRef"
          },
          "status": {
            "type": "string",
            "enum": [
              "ok",
              "no-listings",
              "parallel",
              "no-price",
              "budget"
            ],
            "description": "ok; no-listings; parallel (parallel cards are not matched yet); no-price (no market value to check against); budget (marketplace lookups paused for today, try later)."
          },
          "checkedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the listings were last checked (cached up to 15 minutes)."
          },
          "count": {
            "type": "integer"
          },
          "listings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Listing"
            },
            "description": "Buy It Now listings that are this card: deals first, then the cheapest others."
          },
          "usage": {
            "type": "object",
            "properties": {
              "usedToday": {
                "type": "integer"
              },
              "limitPerDay": {
                "type": "integer"
              }
            }
          }
        },
        "required": [
          "card",
          "status",
          "count",
          "listings"
        ]
      },
      "HistoryPoint": {
        "allOf": [
          {
            "$ref": "#/components/schemas/PriceGrid"
          },
          {
            "type": "object",
            "properties": {
              "date": {
                "type": "string",
                "format": "date",
                "description": "The day, UTC."
              },
              "volume": {
                "type": [
                  "integer",
                  "null"
                ],
                "description": "Sales volume that day, a count (0 is a real 0)."
              }
            },
            "required": [
              "date"
            ]
          }
        ],
        "description": "One day's prices. A grade with no price that day is null, never 0."
      },
      "HistoryResponse": {
        "type": "object",
        "properties": {
          "card": {
            "$ref": "#/components/schemas/CardRef"
          },
          "days": {
            "type": "integer",
            "description": "The window that was applied."
          },
          "history": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HistoryPoint"
            },
            "description": "Days we hold prices for, so dates can skip."
          },
          "soldByGrade": {
            "type": "object",
            "additionalProperties": true,
            "description": "Only with soldByGrade=1. Sold prices at every grade we have sales for, half grades and every grading company included, from our own verified sales. Keyed by grade (\"PSA 8.5\", \"BGS 9\", \"Raw\"), newest first: [{ date, price (the middle sale that day), sales }]. A day with no sale has no point."
          },
          "count": {
            "type": "integer"
          }
        },
        "required": [
          "card",
          "days",
          "history",
          "count"
        ]
      },
      "GraderPopulation": {
        "type": "object",
        "properties": {
          "totalGraded": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Copies this grader has graded."
          },
          "gems": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Copies at the gem grade."
          },
          "gemGrade": {
            "type": [
              "string",
              "null"
            ],
            "description": "The grade counted as gem for this grader, for example 10 or 9.5."
          },
          "gemRate": {
            "type": [
              "number",
              "null"
            ],
            "description": "gems / totalGraded as a percentage."
          },
          "ladder": {
            "type": [
              "object",
              "array",
              "null"
            ],
            "description": "Counts grade by grade, as the grader reports them."
          },
          "autoTotal": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Autograph copies graded."
          },
          "nonAutoTotal": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Non-autograph copies graded."
          },
          "lastChange": {
            "type": [
              "object",
              "integer",
              "string",
              "null"
            ],
            "description": "The most recent change we recorded for this grader."
          }
        },
        "additionalProperties": true
      },
      "PopulationResponse": {
        "type": "object",
        "properties": {
          "card": {
            "$ref": "#/components/schemas/CardRef"
          },
          "population": {
            "oneOf": [
              {
                "type": "object",
                "properties": {
                  "totalPopulation": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "description": "All graders together."
                  },
                  "totalGems": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "description": "Gem copies across graders."
                  },
                  "asOf": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "date-time",
                    "description": "When the report was taken."
                  },
                  "lastChange": {
                    "type": [
                      "object",
                      "integer",
                      "string",
                      "null"
                    ]
                  },
                  "graders": {
                    "type": "object",
                    "additionalProperties": {
                      "$ref": "#/components/schemas/GraderPopulation"
                    },
                    "description": "Keyed by grader: PSA, BGS, SGC, CGC."
                  }
                }
              },
              {
                "type": "null"
              }
            ],
            "description": "null when we do not hold a report for this card yet."
          }
        },
        "required": [
          "card",
          "population"
        ]
      },
      "Slab": {
        "type": "object",
        "properties": {
          "grader": {
            "type": [
              "string",
              "null"
            ],
            "description": "PSA, BGS, CGC, SGC, TGA or null."
          },
          "grade": {
            "type": [
              "number",
              "string",
              "null"
            ],
            "description": "The numeric grade, `Authentic`, or null when unread."
          },
          "autoGrade": {
            "type": [
              "string",
              "null"
            ],
            "description": "Autograph grade when the label carries one."
          },
          "certNumber": {
            "type": [
              "string",
              "null"
            ],
            "description": "The cert number read from the label or the grader's record."
          },
          "certVerified": {
            "type": "boolean",
            "description": "true when the grader's record for that cert was found and agrees with the label."
          },
          "priceAtGrade": {
            "type": [
              "number",
              "null"
            ],
            "description": "The card's price at exactly this grade when we price that grade; null otherwise (never a neighbouring grade's price)."
          },
          "verifyUrl": {
            "type": [
              "string",
              "null"
            ],
            "description": "The grader's own page for the cert (PSA, CGC and TGA open the cert; Beckett and SGC open their lookup page).",
            "format": "uri"
          },
          "frontImageUrl": {
            "type": [
              "string",
              "null"
            ],
            "description": "The grader's front slab photo through our image link, when they have one.",
            "format": "uri"
          },
          "backImageUrl": {
            "type": [
              "string",
              "null"
            ],
            "description": "The grader's back slab photo through our image link, when they have one.",
            "format": "uri"
          }
        },
        "required": [
          "certVerified"
        ]
      },
      "ScanCard": {
        "type": "object",
        "properties": {
          "id": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The catalog id, or null when the match is not in our catalog yet."
          },
          "externalId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Secondary id."
          },
          "name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Card name; a parallel is named in brackets."
          },
          "playerName": {
            "type": [
              "string",
              "null"
            ],
            "description": "Player or character."
          },
          "year": {
            "type": [
              "integer",
              "string",
              "null"
            ]
          },
          "setName": {
            "type": [
              "string",
              "null"
            ],
            "description": "Set name."
          },
          "cardNumber": {
            "type": [
              "string",
              "null"
            ],
            "description": "Card number."
          },
          "printRun": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Print run for numbered cards."
          },
          "parallel": {
            "type": "string",
            "description": "The parallel, or `Base`."
          },
          "category": {
            "type": [
              "string",
              "null"
            ],
            "description": "Category."
          },
          "imageUrl": {
            "type": [
              "string",
              "null"
            ],
            "description": "Picture through our image link, or null.",
            "format": "uri"
          },
          "confidence": {
            "type": [
              "number",
              "null"
            ],
            "description": "0 to 1, three decimals."
          }
        }
      },
      "ScanResponse": {
        "type": "object",
        "properties": {
          "match": {
            "oneOf": [
              {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/ScanCard"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "prices": {
                        "oneOf": [
                          {
                            "$ref": "#/components/schemas/Prices"
                          },
                          {
                            "type": "null"
                          }
                        ]
                      }
                    }
                  }
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "The best card, with its prices; null when nothing is close enough."
          },
          "alternatives": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ScanCard"
            },
            "description": "Up to 7 more cards, without prices. Show them and let the user choose."
          },
          "engine": {
            "type": "string",
            "description": "`image` when our own image index answered, `vision` when the fallback model did."
          },
          "slab": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Slab"
              },
              {
                "type": "null"
              }
            ],
            "description": "The graded-card label, or null on a raw card."
          },
          "scanId": {
            "type": [
              "string",
              "integer",
              "null"
            ],
            "description": "Our id for this scan."
          },
          "uncertain": {
            "type": "boolean",
            "description": "True when the user should confirm the card, or its printing, before you show a price."
          },
          "printingUncertain": {
            "type": "boolean",
            "description": "True when the card is right but its printing (colour, parallel) is not certain. The alternatives usually hold the other printings."
          },
          "reason": {
            "type": "string",
            "enum": [
              "low_confidence",
              "no_card",
              "name_not_on_card"
            ],
            "description": "Why `match` is null: `low_confidence` (no card scored 0.75 or more), `no_card` (no card in the photo), `name_not_on_card` (the closest card's name is not printed on the card in the photo). The alternatives still come back."
          },
          "nameCheck": {
            "type": "string",
            "enum": [
              "match",
              "switched",
              "not_on_card",
              "no_match",
              "no_text",
              "skipped",
              "unavailable"
            ],
            "description": "On a match under 0.85: how the name printed on the card compared with the pick."
          },
          "usage": {
            "type": "object",
            "properties": {
              "scans": {
                "type": "object",
                "properties": {
                  "used": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "description": "Scans used this month, after this one."
                  },
                  "allowance": {
                    "type": "integer"
                  }
                }
              }
            }
          }
        },
        "required": [
          "match",
          "alternatives",
          "engine"
        ],
        "additionalProperties": true
      },
      "ScanRequest": {
        "type": "object",
        "properties": {
          "image": {
            "type": "string",
            "description": "The photo as base64 (JPEG, PNG or WebP, under 10 MB). A `data:` URL prefix is fine. One card per photo.",
            "contentEncoding": "base64"
          },
          "mimeType": {
            "type": "string",
            "description": "The photo's media type. Default image/jpeg.",
            "maxLength": 40,
            "default": "image/jpeg"
          },
          "category": {
            "type": "string",
            "description": "Narrow the match to one category, for example football, pokemon or marvel.",
            "maxLength": 40
          },
          "slab": {
            "type": "boolean",
            "description": "true for a graded slab (the label is read in full), false for a raw card (no label read). Leave it out and a quick check decides."
          }
        },
        "required": [
          "image"
        ]
      },
      "Set": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The set id that search (`set=`) and the watch list take, for example pokemon-scarlet-violet-151."
          },
          "name": {
            "type": "string",
            "description": "Set name."
          },
          "language": {
            "type": [
              "string",
              "null"
            ],
            "description": "The language printed on its cards."
          },
          "year": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Release year, when known."
          },
          "cards": {
            "type": [
              "integer",
              "null"
            ],
            "description": "How many cards it has."
          }
        },
        "required": [
          "id",
          "name"
        ],
        "additionalProperties": true
      },
      "SetsResponse": {
        "type": "object",
        "properties": {
          "category": {
            "type": "string",
            "description": "The category that was asked for."
          },
          "language": {
            "type": [
              "string",
              "null"
            ],
            "description": "The language filter, or null."
          },
          "sets": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Set"
            }
          },
          "total": {
            "type": "integer",
            "description": "Sets in the category after the filters, before paging."
          },
          "limit": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          }
        },
        "required": [
          "category",
          "sets",
          "total",
          "limit",
          "offset"
        ]
      },
      "Cert": {
        "type": "object",
        "properties": {
          "grader": {
            "type": [
              "string",
              "null"
            ],
            "description": "PSA, BGS, CGC or SGC."
          },
          "certNumber": {
            "type": [
              "string",
              "null"
            ],
            "description": "The cert number."
          },
          "grade": {
            "type": [
              "string",
              "null"
            ],
            "description": "The grade on the grader's record."
          },
          "player": {
            "type": [
              "string",
              "null"
            ],
            "description": "Player or character on the record."
          },
          "year": {
            "type": [
              "string",
              "null"
            ],
            "description": "Year on the record."
          },
          "cardNumber": {
            "type": [
              "string",
              "null"
            ],
            "description": "Card number on the record."
          }
        },
        "additionalProperties": true,
        "description": "The grading company's record for the cert, with whatever fields the grader reports (set, autograph grade, population)."
      },
      "CertResponse": {
        "type": "object",
        "properties": {
          "cert": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Cert"
              },
              {
                "type": "null"
              }
            ]
          },
          "card": {
            "oneOf": [
              {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/ScanCard"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "prices": {
                        "oneOf": [
                          {
                            "$ref": "#/components/schemas/Prices"
                          },
                          {
                            "type": "null"
                          }
                        ],
                        "description": "null for keys on the pop + certs plan."
                      }
                    }
                  }
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "The matching card in our catalog with its prices at every grade, or null when the record did not match a card."
          },
          "slab": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Slab"
              },
              {
                "type": "null"
              }
            ],
            "description": "The slab block: grade, cert, whether it verified, price at that grade, the grader's own cert page and slab photos."
          }
        },
        "additionalProperties": true
      },
      "CollectionItem": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "description": "The collection row id."
          },
          "cardId": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The catalog id."
          },
          "name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Card name."
          },
          "year": {
            "type": [
              "integer",
              "string",
              "null"
            ]
          },
          "setName": {
            "type": [
              "string",
              "null"
            ],
            "description": "Set name."
          },
          "category": {
            "type": [
              "string",
              "null"
            ],
            "description": "Category."
          },
          "playerName": {
            "type": [
              "string",
              "null"
            ],
            "description": "Player or character."
          },
          "imageUrl": {
            "type": [
              "string",
              "null"
            ],
            "description": "Picture through our image link, or null.",
            "format": "uri"
          },
          "grade": {
            "type": [
              "string",
              "null"
            ],
            "description": "The grade you recorded."
          },
          "quantity": {
            "type": "integer",
            "default": 1
          },
          "purchasePrice": {
            "type": "number",
            "description": "What you paid, in US dollars (0 when not recorded)."
          },
          "purchaseDate": {
            "type": [
              "string",
              "null"
            ],
            "format": "date",
            "description": "When you bought it."
          },
          "notes": {
            "type": [
              "string",
              "null"
            ],
            "description": "Your notes."
          },
          "addedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When it joined your collection."
          }
        },
        "required": [
          "id"
        ]
      },
      "CollectionResponse": {
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CollectionItem"
            }
          },
          "total": {
            "type": "integer"
          }
        },
        "required": [
          "items",
          "total"
        ]
      },
      "Mover": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "externalId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Secondary id."
          },
          "name": {
            "type": "string",
            "description": "Card name."
          },
          "playerName": {
            "type": [
              "string",
              "null"
            ],
            "description": "Player or character."
          },
          "year": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Year."
          },
          "setName": {
            "type": [
              "string",
              "null"
            ],
            "description": "Set name."
          },
          "cardNumber": {
            "type": [
              "string",
              "null"
            ],
            "description": "Card number."
          },
          "parallel": {
            "type": "string",
            "description": "The parallel, or `Base`."
          },
          "category": {
            "type": "string",
            "description": "The category."
          },
          "imageUrl": {
            "type": [
              "string",
              "null"
            ],
            "description": "Picture through our image link, or null.",
            "format": "uri"
          },
          "direction": {
            "type": "string",
            "enum": [
              "up",
              "down"
            ]
          },
          "priceChangePct": {
            "type": "number",
            "description": "How far the average raw price moved over the period, as a percentage, one decimal."
          },
          "price": {
            "type": [
              "number",
              "null"
            ],
            "description": "The current raw price."
          },
          "averagePrice": {
            "type": [
              "number",
              "null"
            ],
            "description": "The average raw price over the period."
          },
          "prices": {
            "type": "object",
            "properties": {
              "loose": {
                "type": [
                  "number",
                  "null"
                ],
                "description": "Raw"
              },
              "psa10": {
                "type": [
                  "number",
                  "null"
                ],
                "description": "PSA 10"
              },
              "psa9": {
                "type": [
                  "number",
                  "null"
                ],
                "description": "PSA 9"
              }
            }
          },
          "hciUrl": {
            "type": [
              "string",
              "null"
            ],
            "description": "The card's page on hobbycardindex.com.",
            "format": "uri"
          }
        },
        "required": [
          "id",
          "name",
          "direction",
          "priceChangePct"
        ]
      },
      "MoversResponse": {
        "type": "object",
        "properties": {
          "movers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Mover"
            },
            "description": "The biggest risers, then the biggest fallers (10 each way, or `limit`)."
          },
          "category": {
            "type": "string",
            "description": "The category as we name it."
          },
          "period": {
            "type": "string",
            "description": "`1 day`, `7 days` or `30 days`."
          },
          "updatedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the list was last rebuilt (every 6 hours)."
          },
          "limit": {
            "type": "integer",
            "description": "Movers each way asked for."
          },
          "requestsCharged": {
            "type": "integer",
            "description": "Requests this call counted: 1, plus 1 per pair past 10."
          }
        },
        "required": [
          "movers",
          "category",
          "period"
        ]
      },
      "CategoryStats": {
        "type": "object",
        "properties": {
          "category": {
            "type": "string",
            "description": "Category name."
          },
          "cardCount": {
            "type": [
              "integer",
              "string"
            ],
            "description": "Cards in the category."
          },
          "averagePriceLoose": {
            "type": "number",
            "description": "Average raw price, US dollars."
          },
          "averagePricePsa10": {
            "type": "number",
            "description": "Average PSA 10 price, US dollars."
          },
          "lastSaleAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "The newest sale we hold in the category."
          }
        },
        "required": [
          "category"
        ]
      },
      "CategoriesResponse": {
        "type": "object",
        "properties": {
          "categories": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CategoryStats"
            }
          },
          "total": {
            "type": "integer"
          }
        },
        "required": [
          "categories",
          "total"
        ]
      },
      "IndexChange": {
        "type": "object",
        "properties": {
          "1d": {
            "type": [
              "number",
              "null"
            ],
            "description": "Percent change from the newest value on or before 1 day before `date`; null when the index is younger."
          },
          "7d": {
            "type": [
              "number",
              "null"
            ],
            "description": "Percent change over 7 days."
          },
          "30d": {
            "type": [
              "number",
              "null"
            ],
            "description": "Percent change over 30 days."
          }
        }
      },
      "IndexSales": {
        "type": "object",
        "properties": {
          "last7d": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Sales in the 7 days through `throughDate`."
          },
          "last30d": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Sales in the 30 days through `throughDate`."
          },
          "dollarVolume7d": {
            "type": [
              "number",
              "null"
            ],
            "description": "Those 7 days' sales in US dollars."
          },
          "dollarVolume30d": {
            "type": [
              "number",
              "null"
            ],
            "description": "Those 30 days' sales in US dollars."
          },
          "throughDate": {
            "type": [
              "string",
              "null"
            ],
            "description": "The last day counted (YYYY-MM-DD). Verified sold sales by the day they sold; the counts grow as we add marketplaces."
          }
        }
      },
      "MarketIndex": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The index id the other index endpoints take: a category id (`pokemon`) or `player-` plus the player (`player-shohei-ohtani`)."
          },
          "type": {
            "type": "string",
            "description": "`category` or `player`.",
            "enum": [
              "category",
              "player"
            ]
          },
          "name": {
            "type": "string",
            "description": "The category or player name."
          },
          "category": {
            "type": [
              "string",
              "null"
            ],
            "description": "The category id; a player's is the category of the top card in the basket."
          },
          "date": {
            "type": "string",
            "description": "The day of the latest value (YYYY-MM-DD)."
          },
          "value": {
            "type": "number",
            "description": "The latest index value."
          },
          "change": {
            "$ref": "#/components/schemas/IndexChange"
          },
          "basketSize": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Cards in the basket."
          },
          "sales": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/IndexSales"
              },
              {
                "type": "null"
              }
            ]
          },
          "methodology": {
            "type": [
              "string",
              "null"
            ],
            "description": "How the index is built, in one sentence."
          }
        },
        "required": [
          "id",
          "type",
          "name",
          "date",
          "value",
          "change"
        ]
      },
      "MarketIndexList": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "description": "`category` or `player`."
          },
          "indexes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MarketIndex"
            }
          },
          "total": {
            "type": "integer",
            "description": "Indexes matching the filters."
          },
          "limit": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          },
          "updatedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the indexes were last read (they move once a day)."
          }
        },
        "required": [
          "type",
          "indexes",
          "total"
        ]
      },
      "MarketIndexHistory": {
        "type": "object",
        "properties": {
          "index": {
            "$ref": "#/components/schemas/MarketIndex"
          },
          "days": {
            "type": "integer"
          },
          "history": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "date": {
                  "type": "string",
                  "description": "YYYY-MM-DD."
                },
                "value": {
                  "type": [
                    "number",
                    "null"
                  ],
                  "description": "The index value that day."
                },
                "cardsPriced": {
                  "type": [
                    "integer",
                    "null"
                  ],
                  "description": "Basket cards with a price that day."
                },
                "basketSize": {
                  "type": [
                    "integer",
                    "null"
                  ],
                  "description": "Player indexes: cards in the basket."
                },
                "averageRawPrice": {
                  "type": [
                    "number",
                    "null"
                  ],
                  "description": "Category indexes: average raw price across the basket."
                },
                "averagePsa10Price": {
                  "type": [
                    "number",
                    "null"
                  ],
                  "description": "Category indexes: average PSA 10 price across the basket."
                },
                "sales": {
                  "type": [
                    "integer",
                    "null"
                  ],
                  "description": "Sales recorded that day (category: every card in the category; player: verified sales of the basket cards)."
                },
                "salesDollarVolume": {
                  "type": [
                    "number",
                    "null"
                  ],
                  "description": "Those sales in US dollars."
                }
              },
              "required": [
                "date"
              ]
            }
          },
          "updatedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the indexes were last read."
          }
        },
        "required": [
          "index",
          "days",
          "history"
        ]
      },
      "MarketIndexCards": {
        "type": "object",
        "properties": {
          "index": {
            "$ref": "#/components/schemas/MarketIndex"
          },
          "cards": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "rank": {
                  "type": [
                    "integer",
                    "null"
                  ],
                  "description": "Position in the basket."
                },
                "id": {
                  "type": "integer",
                  "description": "The card's id, the one the card and sales endpoints take."
                },
                "externalId": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "The card's external id (`ext-` plus this also works as a cardId)."
                },
                "name": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "Card name."
                },
                "playerName": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "Player or character."
                },
                "setName": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "Set."
                },
                "year": {
                  "type": [
                    "integer",
                    "null"
                  ],
                  "description": "Year."
                },
                "cardNumber": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "Card number."
                },
                "parallel": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "Parallel, `Base` for the base card."
                },
                "category": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "Category id."
                },
                "price": {
                  "type": [
                    "number",
                    "null"
                  ],
                  "description": "The price the index counts this card at, US dollars."
                },
                "prices": {
                  "type": "object",
                  "properties": {
                    "loose": {
                      "type": [
                        "number",
                        "null"
                      ],
                      "description": "Raw."
                    },
                    "psa10": {
                      "type": [
                        "number",
                        "null"
                      ],
                      "description": "PSA 10."
                    }
                  },
                  "description": "Category baskets only."
                },
                "priceDate": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "Player baskets: the day of that price."
                },
                "imageUrl": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "Card image.",
                  "format": "uri"
                },
                "hciUrl": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "The card's page on hobbycardindex.com.",
                  "format": "uri"
                }
              },
              "required": [
                "id"
              ]
            }
          },
          "total": {
            "type": "integer"
          },
          "limit": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          },
          "updatedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the indexes were last read."
          }
        },
        "required": [
          "index",
          "cards",
          "total"
        ]
      },
      "MarketSummary": {
        "type": "object",
        "properties": {
          "category": {
            "type": [
              "string",
              "null"
            ],
            "description": "The category asked for, or null for the whole market."
          },
          "index": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/MarketIndex"
              },
              {
                "type": "null"
              }
            ],
            "description": "With a category: its index."
          },
          "sales": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/IndexSales"
              },
              {
                "type": "null"
              }
            ],
            "description": "With a category: its sales over 7 and 30 days."
          },
          "movers": {
            "anyOf": [
              {
                "type": "object",
                "properties": {
                  "period": {
                    "type": "string",
                    "description": "`7 days`."
                  },
                  "up": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/Mover"
                    }
                  },
                  "down": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/Mover"
                    }
                  },
                  "updatedAt": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "date-time",
                    "description": "When the movers list was rebuilt."
                  }
                }
              },
              {
                "type": "null"
              }
            ],
            "description": "With a category: the 5 biggest rising and falling cards (GET /api/v1/market/movers)."
          },
          "indexes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MarketIndex"
            },
            "description": "Without a category: every category index."
          },
          "gainers7d": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MarketIndex"
            },
            "description": "Without a category: the 3 biggest 7-day gainers."
          },
          "losers7d": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MarketIndex"
            },
            "description": "Without a category: the 3 biggest 7-day losers."
          },
          "playerIndexes": {
            "type": "object",
            "properties": {
              "up": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/MarketIndex"
                }
              },
              "down": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/MarketIndex"
                }
              }
            },
            "required": [
              "up",
              "down"
            ],
            "description": "The 5 biggest 7-day rises and falls among player indexes with at least 25 sales in 30 days and 10 basket cards."
          },
          "updatedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the indexes were last read."
          }
        },
        "required": [
          "category",
          "playerIndexes"
        ]
      },
      "BulkManifest": {
        "type": "object",
        "properties": {
          "generatedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the files were last rebuilt; null with `pending: true` before the first build."
          },
          "durationMs": {
            "type": [
              "integer",
              "null"
            ],
            "description": "How long the build took."
          },
          "totalRows": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Rows across every category."
          },
          "categories": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "category": {
                  "type": "string",
                  "description": "Category name."
                },
                "slug": {
                  "type": "string",
                  "description": "The slug GET /api/v1/bulk/prices/{category} takes."
                },
                "rows": {
                  "type": "integer"
                },
                "parts": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "part": {
                        "type": "integer",
                        "description": "Pass as `?part=`."
                      },
                      "rows": {
                        "type": "integer"
                      },
                      "bytes": {
                        "type": "integer",
                        "description": "Gzipped size."
                      },
                      "filename": {
                        "type": "string",
                        "description": "The file's name."
                      }
                    }
                  },
                  "description": "Big categories are split into parts of about 500,000 rows."
                }
              },
              "required": [
                "category",
                "slug",
                "parts"
              ],
              "additionalProperties": true
            }
          },
          "pending": {
            "type": "boolean",
            "description": "true when no build has finished yet."
          }
        },
        "required": [
          "categories"
        ],
        "additionalProperties": true
      },
      "UsageCounter": {
        "type": "object",
        "properties": {
          "used": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Used this month (this setup month, on a free setup month with its daily pool lifted)."
          },
          "allowance": {
            "type": "integer",
            "description": "The plan's monthly number."
          },
          "usedToday": {
            "type": "integer",
            "description": "Plans with a daily pool only."
          },
          "allowancePerDay": {
            "type": "integer",
            "description": "Plans with a daily pool only."
          }
        },
        "required": [
          "used",
          "allowance"
        ]
      },
      "Usage": {
        "oneOf": [
          {
            "type": "object",
            "properties": {
              "plan": {
                "type": "string",
                "const": "self-serve",
                "description": "An Enterprise (or Dealer) key: 1 request a second, licensed for your own business."
              },
              "rateLimitPerSecond": {
                "type": "integer"
              },
              "dailyCeiling": {
                "type": "integer",
                "description": "The runaway backstop, requests a day."
              }
            },
            "required": [
              "plan",
              "rateLimitPerSecond",
              "dailyCeiling"
            ]
          },
          {
            "type": "object",
            "properties": {
              "plan": {
                "type": "string",
                "description": "The commercial plan id: trial, lite, growth_lite, growth, platform, or a custom plan."
              },
              "label": {
                "type": "string",
                "description": "The plan as we name it."
              },
              "month": {
                "type": "string",
                "description": "The month the counters cover, YYYY-MM (UTC)."
              },
              "requests": {
                "$ref": "#/components/schemas/UsageCounter"
              },
              "scans": {
                "$ref": "#/components/schemas/UsageCounter"
              },
              "saleLookups": {
                "type": "object",
                "properties": {
                  "used": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "description": ""
                  },
                  "allowance": {
                    "type": "integer"
                  }
                },
                "description": "Plans with sold sales."
              },
              "certLookups": {
                "type": "object",
                "properties": {
                  "used": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "description": ""
                  },
                  "allowance": {
                    "type": "integer"
                  }
                },
                "description": "Plans with cert lookups."
              },
              "popReports": {
                "type": "object",
                "properties": {
                  "used": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "description": ""
                  },
                  "allowance": {
                    "type": "integer"
                  }
                },
                "description": "Plans with pop reports."
              },
              "features": {
                "type": "object",
                "properties": {
                  "soldSales": {
                    "type": "boolean",
                    "description": ""
                  },
                  "certLookups": {
                    "type": "boolean",
                    "description": ""
                  },
                  "priceHistory": {
                    "type": "boolean",
                    "description": ""
                  },
                  "population": {
                    "type": "boolean",
                    "description": ""
                  },
                  "bulkFiles": {
                    "type": "boolean",
                    "description": ""
                  }
                }
              },
              "perSecond": {
                "type": "object",
                "properties": {
                  "lookups": {
                    "type": "integer"
                  },
                  "scans": {
                    "type": "integer"
                  },
                  "certs": {
                    "type": "integer"
                  }
                }
              },
              "overage": {
                "oneOf": [
                  {
                    "type": "object",
                    "properties": {
                      "usdPer1000": {
                        "type": "number"
                      },
                      "price": {
                        "type": "string",
                        "description": "The rate in words, for example $1 per 1,000."
                      },
                      "stopsAt": {
                        "type": "string",
                        "description": "For example 3x allowance."
                      },
                      "billed": {
                        "type": "string",
                        "description": "When and how overage is billed."
                      }
                    }
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "null on plans that stop at the allowance."
              },
              "overageOffer": {
                "type": "object",
                "properties": {
                  "usdPer1000": {
                    "type": "number"
                  },
                  "price": {
                    "type": "string"
                  },
                  "stopsAt": {
                    "type": "string"
                  },
                  "acceptAt": {
                    "type": "string",
                    "format": "uri"
                  }
                },
                "description": "Present while overage waits on your acceptance of the updated terms."
              },
              "trialEndsAt": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time",
                "description": "When a free setup month ends; null on paid plans."
              },
              "bulkFiles": {
                "type": "boolean",
                "description": "Whether the daily price files are included."
              }
            },
            "required": [
              "plan",
              "label",
              "month",
              "requests",
              "scans",
              "features",
              "perSecond"
            ]
          }
        ]
      },
      "WebhookEndpoint": {
        "type": "object",
        "properties": {
          "url": {
            "type": [
              "string",
              "null"
            ],
            "description": "Your https endpoint.",
            "format": "uri"
          },
          "hourUtc": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The hour (UTC, 0 to 23) the daily call is sent."
          },
          "secret": {
            "type": "string",
            "description": "The signing secret. Returned once, on the PUT that created or rotated it."
          },
          "active": {
            "type": "boolean",
            "description": "false after the webhook paused or was turned off."
          },
          "lastDelivery": {
            "type": [
              "object",
              "string",
              "null"
            ],
            "description": "The last delivery attempt and its result."
          }
        },
        "additionalProperties": true,
        "description": "Your webhook as stored. Field names beyond url, hour and secret follow the live service."
      },
      "WebhookStatus": {
        "type": "object",
        "properties": {
          "webhook": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/WebhookEndpoint"
              },
              {
                "type": "null"
              }
            ],
            "description": "null when no endpoint is set."
          },
          "watched": {
            "type": "integer",
            "description": "Cards on your watch list."
          },
          "watchLimit": {
            "type": "integer",
            "description": "The most cards your plan can watch."
          }
        },
        "required": [
          "webhook",
          "watched",
          "watchLimit"
        ]
      },
      "WebhookPut": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "description": "Your https endpoint.",
            "format": "uri"
          },
          "hour": {
            "type": "integer",
            "minimum": 0,
            "maximum": 23,
            "description": "The hour (UTC) you want the daily call. `hourUtc` is accepted too."
          },
          "rotateSecret": {
            "type": "boolean",
            "description": "true for a new signing secret (returned once)."
          }
        }
      },
      "WatchlistAdd": {
        "description": "Cards by id (up to 1,000 a call), a whole set, or a whole category. A set or category stays followed: cards we add to it later join your list and come in the daily webhook as cards.added.",
        "oneOf": [
          {
            "type": "object",
            "properties": {
              "cardIds": {
                "type": "array",
                "items": {
                  "type": [
                    "integer",
                    "string"
                  ],
                  "description": "A card id, or `ext-<externalId>`."
                },
                "description": "Up to 1,000 ids a call."
              }
            },
            "required": [
              "cardIds"
            ]
          },
          {
            "type": "object",
            "properties": {
              "category": {
                "type": "string",
                "description": "Category id, for example pokemon."
              },
              "set": {
                "type": "string",
                "description": "A set id from GET /api/v1/sets."
              },
              "language": {
                "type": "string",
                "description": "Optional language filter."
              }
            },
            "required": [
              "category",
              "set"
            ]
          },
          {
            "type": "object",
            "properties": {
              "category": {
                "type": "string",
                "description": "Category id, for example pokemon."
              },
              "language": {
                "type": "string",
                "description": "Optional language filter."
              }
            },
            "required": [
              "category"
            ]
          }
        ]
      },
      "WatchlistRemove": {
        "description": "Cards by id, a set you follow (send the same fields you added it with) or everything.",
        "oneOf": [
          {
            "type": "object",
            "properties": {
              "cardIds": {
                "type": "array",
                "items": {
                  "type": [
                    "integer",
                    "string"
                  ]
                }
              }
            },
            "required": [
              "cardIds"
            ]
          },
          {
            "type": "object",
            "properties": {
              "category": {
                "type": "string"
              },
              "set": {
                "type": "string"
              },
              "language": {
                "type": "string"
              }
            },
            "required": [
              "category"
            ]
          },
          {
            "type": "object",
            "properties": {
              "all": {
                "type": "boolean",
                "const": true
              }
            },
            "required": [
              "all"
            ]
          }
        ]
      },
      "WatchlistChange": {
        "type": "object",
        "properties": {
          "added": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Cards added this call."
          },
          "alreadyWatched": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Ids already on the list."
          },
          "notFound": {
            "type": "array",
            "items": {
              "type": [
                "integer",
                "string"
              ]
            },
            "description": "Ids we could not find."
          },
          "removed": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Cards taken off (remove calls)."
          },
          "total": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Cards on the list after this call."
          },
          "limit": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The most your plan can watch."
          }
        },
        "additionalProperties": true,
        "description": "What changed. Field names follow the live service; the counts above are the ones the documentation describes."
      },
      "WatchlistPage": {
        "type": "object",
        "properties": {
          "cards": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true,
              "description": "A watched card: id, externalId, name, setName, cardNumber, category, year (no prices)."
            }
          },
          "following": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true,
              "description": "A set or category you follow: category, set, language."
            }
          },
          "total": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Cards on the list."
          },
          "limit": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          },
          "watchLimit": {
            "type": "integer",
            "description": "The most cards your plan can watch."
          }
        },
        "required": [
          "watchLimit"
        ],
        "additionalProperties": true
      },
      "WatchedCardPrices": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "externalId": {
            "type": [
              "string",
              "null"
            ],
            "description": ""
          },
          "name": {
            "type": [
              "string",
              "null"
            ],
            "description": ""
          },
          "setName": {
            "type": [
              "string",
              "null"
            ],
            "description": ""
          },
          "cardNumber": {
            "type": [
              "string",
              "null"
            ],
            "description": ""
          },
          "category": {
            "type": [
              "string",
              "null"
            ],
            "description": ""
          },
          "year": {
            "type": [
              "integer",
              "string",
              "null"
            ]
          },
          "prices": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Prices"
              },
              {
                "type": "null"
              }
            ]
          },
          "lastSale": {
            "oneOf": [
              {
                "type": "object",
                "properties": {
                  "price": {
                    "type": "number"
                  },
                  "soldAt": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "date-time",
                    "description": ""
                  },
                  "grade": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": ""
                  }
                }
              },
              {
                "type": "null"
              }
            ],
            "description": "The newest verified sale we hold."
          },
          "sales90d": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Verified sales in the last 90 days."
          }
        },
        "required": [
          "id"
        ],
        "additionalProperties": true,
        "description": "Plus one `lastSale_<grade>` object per grade lane (price, soldAt, url) or null, for example lastSale_PSA10, lastSale_Raw."
      },
      "WatchlistPricesResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "const": true
          },
          "data": {
            "type": "object",
            "properties": {
              "generatedAt": {
                "type": "string",
                "format": "date-time"
              },
              "cards": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/WatchedCardPrices"
                }
              },
              "count": {
                "type": "integer"
              }
            },
            "required": [
              "generatedAt",
              "cards",
              "count"
            ]
          }
        },
        "required": [
          "success",
          "data"
        ],
        "description": "Streamed, so this response carries no attribution object; the credit rule still applies to the prices in it."
      },
      "JsonRpcRequest": {
        "type": "object",
        "properties": {
          "jsonrpc": {
            "type": "string",
            "const": "2.0"
          },
          "id": {
            "type": [
              "string",
              "integer",
              "null"
            ],
            "description": "Leave it out for a notification (notifications/initialized); the server then answers 202 with no body."
          },
          "method": {
            "type": "string",
            "enum": [
              "initialize",
              "ping",
              "tools/list",
              "tools/call"
            ],
            "description": "initialize, ping and tools/list need no key. tools/call runs a tool."
          },
          "params": {
            "type": "object",
            "additionalProperties": true,
            "description": "For tools/call: { name, arguments }."
          }
        },
        "required": [
          "jsonrpc",
          "method"
        ]
      },
      "JsonRpcResponse": {
        "type": "object",
        "properties": {
          "jsonrpc": {
            "type": "string",
            "const": "2.0"
          },
          "id": {
            "type": [
              "string",
              "integer",
              "null"
            ]
          },
          "result": {
            "type": "object",
            "additionalProperties": true,
            "description": "For tools/call: { content: [{ type: \"text\", text }], structuredContent?, isError }. A successful data lookup ends with a text item carrying the credit line."
          },
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "integer"
              },
              "message": {
                "type": "string"
              }
            },
            "required": [
              "code",
              "message"
            ]
          }
        },
        "required": [
          "jsonrpc"
        ]
      }
    },
    "parameters": {
      "cardId": {
        "name": "cardId",
        "in": "path",
        "required": true,
        "description": "The card's `id` from search, scan, movers or watch-list results (an integer), or `ext-` followed by its `externalId` (for example `ext-67890`). Letters, digits, dashes, underscores and dots, up to 150 characters.",
        "schema": {
          "type": "string",
          "maxLength": 150,
          "pattern": "^[a-zA-Z0-9\\-_.]+$"
        }
      },
      "days365": {
        "name": "days",
        "in": "query",
        "required": false,
        "description": "Lookback window in days, 1 to 365. Default 90. A longer window is trimmed to 365 (the reply's periodDays says which window was used).",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 365,
          "default": 90
        }
      },
      "limit": {
        "name": "limit",
        "in": "query",
        "required": false,
        "description": "Page size.",
        "schema": {
          "type": "integer",
          "minimum": 1
        }
      },
      "offset": {
        "name": "offset",
        "in": "query",
        "required": false,
        "description": "How many to skip.",
        "schema": {
          "type": "integer",
          "minimum": 0,
          "default": 0
        }
      }
    },
    "headers": {
      "X-Attribution": {
        "description": "Always \"Prices from Hobby Card Index (https://hobbycardindex.com)\" on a successful JSON object response: the credit the data must carry.",
        "schema": {
          "type": "string"
        }
      },
      "X-HCI-Plan": {
        "description": "Commercial plans only: the plan id behind this key.",
        "schema": {
          "type": "string"
        }
      },
      "X-HCI-Requests-Used": {
        "description": "Commercial plans only: requests used this month, after this one.",
        "schema": {
          "type": "integer"
        }
      },
      "X-HCI-Requests-Allowance": {
        "description": "Commercial plans only: the plan's monthly requests.",
        "schema": {
          "type": "integer"
        }
      },
      "RateLimit-Limit": {
        "description": "Requests allowed in the current one-second window (X-RateLimit-* are sent too).",
        "schema": {
          "type": "integer"
        }
      },
      "RateLimit-Remaining": {
        "description": "Requests left in the current one-second window.",
        "schema": {
          "type": "integer"
        }
      },
      "Retry-After": {
        "description": "Seconds to wait before retrying (busy scanner, movers rebuilding).",
        "schema": {
          "type": "integer"
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "The request is not valid: VALIDATION_ERROR, INVALID_PARAM, BAD_REQUEST, SEARCH_TOO_BROAD, NO_IMAGE, INVALID_IMAGE.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "No key (MISSING_API_KEY), a key we do not know (INVALID_API_KEY) or one that was revoked (KEY_REVOKED).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "PaymentRequired": {
        "description": "TRIAL_ENDED: the free setup month is over and the first month of the plan is not paid yet.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Forbidden": {
        "description": "The key's plan does not include this: PLAN_REQUIRED, DEALER_KEY_SCOPE, NOT_ON_PLAN, REQUIRES_COMMERCIAL_LICENSE, REQUIRES_GROWTH, REQUIRES_PARTNER_PLAN, REQUIRES_PLATFORM_PLAN, REQUIRES_PRICE_FILES, REQUIRES_ENTERPRISE, LISTINGS_NOT_ENABLED. The message says which plan has it and `details.plans_url` where to get it.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "NotFound": {
        "description": "NOT_FOUND: no card (or file) matches.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "TooManyRequests": {
        "description": "RATE_LIMITED (the per-second window; slow down and retry), RATE_LIMIT_EXCEEDED (the daily ceiling), MONTHLY_LIMIT_REACHED, DAILY_LIMIT_REACHED, SALES_LIMIT_REACHED, CERT_LIMIT_REACHED, POP_LIMIT_REACHED, LISTINGS_LIMIT_REACHED, SCAN_ENGINE_BUSY (Retry-After says when).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "ServerError": {
        "description": "SERVER_ERROR (500 or 503): something failed on our side; retry in a minute.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    }
  }
}