{
  "openapi": "3.1.0",
  "info": {
    "title": "BGT Public Collectible Catalog",
    "version": "1.2.0",
    "description": "Read-only first-party catalog. Source-reported optional attributes do not establish finish, price or authenticity. Coverage is measured against registered scopes, not all collectibles. Images are source URLs, not a redistribution license."
  },
  "servers": [{ "url": "https://api.bestgradingtools.com" }],
  "paths": {
    "/v1/catalog/health": {
      "get": {
        "summary": "Check service health",
        "security": [],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Own stored public data. Respect Cache-Control. Missing attributes remain absent.",
            "content": {
              "application/json": { "schema": { "$ref": "#/components/schemas/Health" } }
            }
          },
          "400": { "description": "Invalid input or unsupported set language." },
          "429": { "description": "Rate limited. Respect Retry-After." },
          "503": {
            "description": "Our catalog is temporarily unavailable. No interactive fallback to providers."
          }
        }
      }
    },
    "/v1/catalog/status": {
      "get": {
        "summary": "Read import history and overall coverage",
        "security": [],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Own stored public data. Respect Cache-Control. Missing attributes remain absent.",
            "content": {
              "application/json": { "schema": { "$ref": "#/components/schemas/Status" } }
            }
          },
          "400": { "description": "Invalid input or unsupported set language." },
          "429": { "description": "Rate limited. Respect Retry-After." },
          "503": {
            "description": "Our catalog is temporarily unavailable. No interactive fallback to providers."
          }
        }
      }
    },
    "/v1/catalog/coverage": {
      "get": {
        "summary": "Read available cards by game and printed language",
        "security": [],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Own stored public data. Respect Cache-Control. Missing attributes remain absent.",
            "content": {
              "application/json": { "schema": { "$ref": "#/components/schemas/Coverage" } }
            }
          },
          "400": { "description": "Invalid input or unsupported set language." },
          "429": { "description": "Rate limited. Respect Retry-After." },
          "503": {
            "description": "Our catalog is temporarily unavailable. No interactive fallback to providers."
          }
        }
      }
    },
    "/v1/catalog/sets": {
      "get": {
        "summary": "Read registered sets and source snapshot dates",
        "security": [],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Own stored public data. Respect Cache-Control. Missing attributes remain absent.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Sets" } } }
          },
          "400": { "description": "Invalid input or unsupported set language." },
          "429": { "description": "Rate limited. Respect Retry-After." },
          "503": {
            "description": "Our catalog is temporarily unavailable. No interactive fallback to providers."
          }
        }
      }
    },
    "/v1/catalog/sets/{setId}/cards": {
      "get": {
        "summary": "List stored cards in a physical set",
        "security": [],
        "parameters": [
          {
            "name": "setId",
            "in": "path",
            "required": true,
            "schema": { "type": "string" },
            "example": "pokemon:base1"
          },
          {
            "name": "locale",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "en",
                "es",
                "de",
                "ja",
                "ko",
                "zh-cn",
                "zh-tw",
                "fr",
                "it",
                "pt",
                "nl",
                "pl",
                "ru",
                "id",
                "th"
              ],
              "default": "en"
            },
            "description": "Printed language, never the interface language. und is rejected for catalog reads."
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 1 }
          },
          {
            "name": "pageSize",
            "in": "query",
            "required": false,
            "schema": { "type": "integer", "minimum": 1, "maximum": 20, "default": 20 }
          }
        ],
        "responses": {
          "200": {
            "description": "Own stored public data. Respect Cache-Control. Missing attributes remain absent.",
            "content": {
              "application/json": { "schema": { "$ref": "#/components/schemas/Search" } }
            }
          },
          "400": { "description": "Invalid input or unsupported set language." },
          "429": { "description": "Rate limited. Respect Retry-After." },
          "503": {
            "description": "Our catalog is temporarily unavailable. No interactive fallback to providers."
          }
        }
      }
    },
    "/v1/catalog/cards/search": {
      "get": {
        "summary": "Search stored printings by name or number",
        "security": [],
        "parameters": [
          {
            "name": "game",
            "in": "query",
            "required": true,
            "schema": { "type": "string", "enum": ["pokemon", "magic", "one_piece", "lorcana"] }
          },
          {
            "name": "q",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 80,
              "description": "A card number (one digit allowed) or 2–80 text characters."
            }
          },
          {
            "name": "locale",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "en",
                "es",
                "de",
                "ja",
                "ko",
                "zh-cn",
                "zh-tw",
                "fr",
                "it",
                "pt",
                "nl",
                "pl",
                "ru",
                "id",
                "th"
              ],
              "default": "en"
            },
            "description": "Printed language, never the interface language. und is rejected for catalog reads."
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 1 }
          },
          {
            "name": "pageSize",
            "in": "query",
            "required": false,
            "schema": { "type": "integer", "minimum": 1, "maximum": 20, "default": 5 }
          }
        ],
        "responses": {
          "200": {
            "description": "Own stored public data. Respect Cache-Control. Missing attributes remain absent.",
            "content": {
              "application/json": { "schema": { "$ref": "#/components/schemas/Search" } }
            }
          },
          "400": { "description": "Invalid input or unsupported set language." },
          "429": { "description": "Rate limited. Respect Retry-After." },
          "503": {
            "description": "Our catalog is temporarily unavailable. No interactive fallback to providers."
          }
        }
      }
    },
    "/v1/catalog/cards/{id}": {
      "get": {
        "summary": "Read a printing, finishes, attributes and sources",
        "security": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": { "type": "string", "minLength": 1, "maxLength": 160 }
          },
          {
            "name": "game",
            "in": "query",
            "required": true,
            "schema": { "type": "string", "enum": ["pokemon", "magic", "one_piece", "lorcana"] }
          },
          {
            "name": "locale",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "en",
                "es",
                "de",
                "ja",
                "ko",
                "zh-cn",
                "zh-tw",
                "fr",
                "it",
                "pt",
                "nl",
                "pl",
                "ru",
                "id",
                "th"
              ],
              "default": "en"
            },
            "description": "Printed language, never the interface language. und is rejected for catalog reads."
          }
        ],
        "responses": {
          "200": {
            "description": "Own stored public data. Respect Cache-Control. Missing attributes remain absent.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Card" } } }
          },
          "400": { "description": "Invalid input or unsupported set language." },
          "404": {
            "description": "CARD_NOT_IMPORTED: this printing is not stored. This does not prove nonexistence."
          },
          "429": { "description": "Rate limited. Respect Retry-After." },
          "503": {
            "description": "Our catalog is temporarily unavailable. No interactive fallback to providers."
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Health": {
        "type": "object",
        "properties": {
          "data": {
            "type": "object",
            "properties": {
              "status": { "type": "string", "const": "ok" },
              "provider": { "type": "string", "enum": ["pokemon-tcg-api", "bgt-catalog"] },
              "cache": { "type": "string", "enum": ["cloudflare-kv", "cloudflare-d1"] }
            },
            "required": ["status", "provider", "cache"],
            "additionalProperties": false
          },
          "meta": {
            "type": "object",
            "properties": {
              "schemaVersion": { "type": "string", "const": "1.0.0" },
              "requestId": { "type": "string", "minLength": 8, "maxLength": 128 },
              "generatedAt": { "type": "string", "format": "date-time" }
            },
            "required": ["schemaVersion", "requestId", "generatedAt"],
            "additionalProperties": false
          }
        },
        "required": ["data", "meta"],
        "additionalProperties": false
      },
      "Search": {
        "type": "object",
        "properties": {
          "data": {
            "type": "object",
            "properties": {
              "items": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "id": { "type": "string", "minLength": 1, "maxLength": 160 },
                    "name": { "type": "string", "minLength": 1, "maxLength": 200 },
                    "setId": { "type": "string", "minLength": 1, "maxLength": 80 },
                    "setName": { "type": "string", "minLength": 1, "maxLength": 200 },
                    "number": { "type": "string", "minLength": 1, "maxLength": 40 },
                    "printedTotal": {
                      "anyOf": [{ "type": "integer", "exclusiveMinimum": 0 }, { "type": "null" }]
                    },
                    "language": {
                      "type": "string",
                      "enum": [
                        "en",
                        "es",
                        "de",
                        "ja",
                        "ko",
                        "zh-cn",
                        "zh-tw",
                        "fr",
                        "it",
                        "pt",
                        "nl",
                        "pl",
                        "ru",
                        "id",
                        "th",
                        "und"
                      ]
                    },
                    "imageSmallUrl": {
                      "anyOf": [{ "type": "string", "format": "uri" }, { "type": "null" }]
                    },
                    "imageLargeUrl": {
                      "anyOf": [{ "type": "string", "format": "uri" }, { "type": "null" }]
                    },
                    "releaseDate": {
                      "anyOf": [
                        { "type": "string", "pattern": "^\\d{4}-\\d{2}-\\d{2}$" },
                        { "type": "null" }
                      ]
                    },
                    "provider": {
                      "type": "string",
                      "enum": ["pokemon-tcg-api", "tcgdex", "scryfall", "lorcast", "bandai"]
                    },
                    "game": {
                      "type": "string",
                      "enum": ["pokemon", "magic", "one_piece", "lorcana"]
                    },
                    "attributes": {
                      "type": "object",
                      "properties": {
                        "sourceUrl": { "type": "string", "format": "uri" },
                        "rarity": { "type": "string", "minLength": 1, "maxLength": 120 },
                        "illustrators": {
                          "type": "array",
                          "items": { "type": "string", "minLength": 1, "maxLength": 120 },
                          "minItems": 1,
                          "maxItems": 16
                        },
                        "category": { "type": "string", "minLength": 1, "maxLength": 120 },
                        "types": {
                          "type": "array",
                          "items": { "type": "string", "minLength": 1, "maxLength": 120 },
                          "minItems": 1,
                          "maxItems": 16
                        }
                      },
                      "required": ["sourceUrl"],
                      "additionalProperties": false
                    },
                    "provenance": {
                      "type": "object",
                      "properties": {
                        "sourceUrl": { "type": "string", "format": "uri" },
                        "importedAt": { "type": "string", "format": "date-time" },
                        "runId": { "type": "string", "minLength": 1, "maxLength": 80 },
                        "detailsStatus": { "type": "string", "enum": ["basic", "complete"] }
                      },
                      "required": ["sourceUrl", "importedAt", "runId", "detailsStatus"],
                      "additionalProperties": false
                    },
                    "variantOptions": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": { "type": "string", "minLength": 1, "maxLength": 160 },
                          "label": { "type": "string", "minLength": 1, "maxLength": 120 }
                        },
                        "required": ["id", "label"],
                        "additionalProperties": false
                      },
                      "maxItems": 100
                    },
                    "variantSourceUrl": { "type": "string", "format": "uri" }
                  },
                  "required": [
                    "id",
                    "name",
                    "setId",
                    "setName",
                    "number",
                    "printedTotal",
                    "language",
                    "imageSmallUrl",
                    "imageLargeUrl",
                    "releaseDate",
                    "provider"
                  ],
                  "additionalProperties": false
                },
                "maxItems": 20
              },
              "nextCursor": {
                "anyOf": [{ "type": "string", "maxLength": 240 }, { "type": "null" }]
              },
              "truncated": { "type": "boolean", "const": true },
              "cache": { "type": "string", "enum": ["hit", "miss", "stale"] }
            },
            "required": ["items", "nextCursor", "cache"],
            "additionalProperties": false
          },
          "meta": {
            "type": "object",
            "properties": {
              "schemaVersion": { "type": "string", "const": "1.0.0" },
              "requestId": { "type": "string", "minLength": 8, "maxLength": 128 },
              "generatedAt": { "type": "string", "format": "date-time" }
            },
            "required": ["schemaVersion", "requestId", "generatedAt"],
            "additionalProperties": false
          }
        },
        "required": ["data", "meta"],
        "additionalProperties": false
      },
      "Card": {
        "type": "object",
        "properties": {
          "data": {
            "type": "object",
            "properties": {
              "id": { "type": "string", "minLength": 1, "maxLength": 160 },
              "name": { "type": "string", "minLength": 1, "maxLength": 200 },
              "setId": { "type": "string", "minLength": 1, "maxLength": 80 },
              "setName": { "type": "string", "minLength": 1, "maxLength": 200 },
              "number": { "type": "string", "minLength": 1, "maxLength": 40 },
              "printedTotal": {
                "anyOf": [{ "type": "integer", "exclusiveMinimum": 0 }, { "type": "null" }]
              },
              "language": {
                "type": "string",
                "enum": [
                  "en",
                  "es",
                  "de",
                  "ja",
                  "ko",
                  "zh-cn",
                  "zh-tw",
                  "fr",
                  "it",
                  "pt",
                  "nl",
                  "pl",
                  "ru",
                  "id",
                  "th",
                  "und"
                ]
              },
              "imageSmallUrl": {
                "anyOf": [{ "type": "string", "format": "uri" }, { "type": "null" }]
              },
              "imageLargeUrl": {
                "anyOf": [{ "type": "string", "format": "uri" }, { "type": "null" }]
              },
              "releaseDate": {
                "anyOf": [
                  { "type": "string", "pattern": "^\\d{4}-\\d{2}-\\d{2}$" },
                  { "type": "null" }
                ]
              },
              "provider": {
                "type": "string",
                "enum": ["pokemon-tcg-api", "tcgdex", "scryfall", "lorcast", "bandai"]
              },
              "game": { "type": "string", "enum": ["pokemon", "magic", "one_piece", "lorcana"] },
              "attributes": {
                "type": "object",
                "properties": {
                  "sourceUrl": { "type": "string", "format": "uri" },
                  "rarity": { "type": "string", "minLength": 1, "maxLength": 120 },
                  "illustrators": {
                    "type": "array",
                    "items": { "type": "string", "minLength": 1, "maxLength": 120 },
                    "minItems": 1,
                    "maxItems": 16
                  },
                  "category": { "type": "string", "minLength": 1, "maxLength": 120 },
                  "types": {
                    "type": "array",
                    "items": { "type": "string", "minLength": 1, "maxLength": 120 },
                    "minItems": 1,
                    "maxItems": 16
                  }
                },
                "required": ["sourceUrl"],
                "additionalProperties": false
              },
              "provenance": {
                "type": "object",
                "properties": {
                  "sourceUrl": { "type": "string", "format": "uri" },
                  "importedAt": { "type": "string", "format": "date-time" },
                  "runId": { "type": "string", "minLength": 1, "maxLength": 80 },
                  "detailsStatus": { "type": "string", "enum": ["basic", "complete"] }
                },
                "required": ["sourceUrl", "importedAt", "runId", "detailsStatus"],
                "additionalProperties": false
              },
              "variantOptions": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "id": { "type": "string", "minLength": 1, "maxLength": 160 },
                    "label": { "type": "string", "minLength": 1, "maxLength": 120 }
                  },
                  "required": ["id", "label"],
                  "additionalProperties": false
                },
                "maxItems": 100
              },
              "variantSourceUrl": { "type": "string", "format": "uri" }
            },
            "required": [
              "id",
              "name",
              "setId",
              "setName",
              "number",
              "printedTotal",
              "language",
              "imageSmallUrl",
              "imageLargeUrl",
              "releaseDate",
              "provider"
            ],
            "additionalProperties": false
          },
          "meta": {
            "type": "object",
            "properties": {
              "schemaVersion": { "type": "string", "const": "1.0.0" },
              "requestId": { "type": "string", "minLength": 8, "maxLength": 128 },
              "generatedAt": { "type": "string", "format": "date-time" }
            },
            "required": ["schemaVersion", "requestId", "generatedAt"],
            "additionalProperties": false
          }
        },
        "required": ["data", "meta"],
        "additionalProperties": false
      },
      "Sets": {
        "type": "object",
        "properties": {
          "data": {
            "type": "object",
            "properties": {
              "items": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "pattern": "^(pokemon|magic|one_piece|lorcana):[A-Za-z0-9][A-Za-z0-9._+-]{0,119}$"
                    },
                    "game": {
                      "type": "string",
                      "enum": ["pokemon", "magic", "one_piece", "lorcana"]
                    },
                    "code": { "type": "string", "minLength": 1, "maxLength": 80 },
                    "name": { "type": "string", "minLength": 1, "maxLength": 200 },
                    "names": {
                      "type": "object",
                      "additionalProperties": {
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 200
                      },
                      "propertyNames": {
                        "enum": [
                          "en",
                          "es",
                          "de",
                          "ja",
                          "ko",
                          "zh-cn",
                          "zh-tw",
                          "fr",
                          "it",
                          "pt",
                          "nl",
                          "pl",
                          "ru",
                          "id",
                          "th",
                          "und"
                        ]
                      }
                    },
                    "languages": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "enum": [
                          "en",
                          "es",
                          "de",
                          "ja",
                          "ko",
                          "zh-cn",
                          "zh-tw",
                          "fr",
                          "it",
                          "pt",
                          "nl",
                          "pl",
                          "ru",
                          "id",
                          "th",
                          "und"
                        ]
                      },
                      "minItems": 1,
                      "maxItems": 20
                    },
                    "cardCounts": {
                      "type": "object",
                      "additionalProperties": {
                        "type": "integer",
                        "exclusiveMinimum": 0,
                        "maximum": 100000
                      },
                      "propertyNames": {
                        "enum": [
                          "en",
                          "es",
                          "de",
                          "ja",
                          "ko",
                          "zh-cn",
                          "zh-tw",
                          "fr",
                          "it",
                          "pt",
                          "nl",
                          "pl",
                          "ru",
                          "id",
                          "th",
                          "und"
                        ]
                      }
                    },
                    "cardCount": { "type": "integer", "exclusiveMinimum": 0, "maximum": 100000 },
                    "logoUrl": { "type": "string", "format": "uri" },
                    "sourceUrl": { "type": "string", "format": "uri" }
                  },
                  "required": ["id", "game", "code", "name", "languages"],
                  "additionalProperties": false
                },
                "maxItems": 5000
              },
              "sources": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "game": {
                      "type": "string",
                      "enum": ["pokemon", "magic", "one_piece", "lorcana"]
                    },
                    "updatedAt": { "type": "string", "format": "date-time" },
                    "status": { "type": "string", "enum": ["bundled", "fresh", "stale"] },
                    "count": { "type": "integer", "minimum": 0 }
                  },
                  "required": ["game", "updatedAt", "status", "count"],
                  "additionalProperties": false
                },
                "maxItems": 4
              }
            },
            "required": ["items", "sources"],
            "additionalProperties": false
          },
          "meta": {
            "type": "object",
            "properties": {
              "schemaVersion": { "type": "string", "const": "1.0.0" },
              "requestId": { "type": "string", "minLength": 8, "maxLength": 128 },
              "generatedAt": { "type": "string", "format": "date-time" }
            },
            "required": ["schemaVersion", "requestId", "generatedAt"],
            "additionalProperties": false
          }
        },
        "required": ["data", "meta"],
        "additionalProperties": false
      },
      "Status": {
        "type": "object",
        "properties": {
          "data": {
            "type": "object",
            "properties": {
              "updatedAt": {
                "anyOf": [{ "type": "string", "format": "date-time" }, { "type": "null" }]
              },
              "cards": { "type": "integer", "minimum": 0 },
              "scopes": { "type": "integer", "minimum": 0 },
              "readyScopes": { "type": "integer", "minimum": 0 },
              "pendingScopes": { "type": "integer", "minimum": 0 },
              "failedScopes": { "type": "integer", "minimum": 0 },
              "complete": { "type": "boolean" },
              "runs": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "runId": { "type": "string" },
                    "scope": { "type": "string" },
                    "sourceUrl": { "type": "string", "format": "uri" },
                    "startedAt": { "type": "string", "format": "date-time" },
                    "finishedAt": {
                      "anyOf": [{ "type": "string", "format": "date-time" }, { "type": "null" }]
                    },
                    "status": { "type": "string", "enum": ["running", "updated", "failed"] },
                    "count": { "type": "integer", "minimum": 0 },
                    "error": {
                      "anyOf": [{ "type": "string", "maxLength": 240 }, { "type": "null" }]
                    }
                  },
                  "required": [
                    "runId",
                    "scope",
                    "sourceUrl",
                    "startedAt",
                    "finishedAt",
                    "status",
                    "count",
                    "error"
                  ],
                  "additionalProperties": false
                },
                "maxItems": 20
              }
            },
            "required": [
              "updatedAt",
              "cards",
              "scopes",
              "readyScopes",
              "pendingScopes",
              "failedScopes",
              "complete",
              "runs"
            ],
            "additionalProperties": false
          },
          "meta": {
            "type": "object",
            "properties": {
              "schemaVersion": { "type": "string", "const": "1.0.0" },
              "requestId": { "type": "string", "minLength": 8, "maxLength": 128 },
              "generatedAt": { "type": "string", "format": "date-time" }
            },
            "required": ["schemaVersion", "requestId", "generatedAt"],
            "additionalProperties": false
          }
        },
        "required": ["data", "meta"],
        "additionalProperties": false
      },
      "Coverage": {
        "type": "object",
        "properties": {
          "data": {
            "type": "object",
            "properties": {
              "items": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "updatedAt": {
                      "anyOf": [{ "type": "string", "format": "date-time" }, { "type": "null" }]
                    },
                    "cards": { "type": "integer", "minimum": 0 },
                    "scopes": { "type": "integer", "minimum": 0 },
                    "readyScopes": { "type": "integer", "minimum": 0 },
                    "pendingScopes": { "type": "integer", "minimum": 0 },
                    "failedScopes": { "type": "integer", "minimum": 0 },
                    "complete": { "type": "boolean" },
                    "game": {
                      "type": "string",
                      "enum": ["pokemon", "magic", "one_piece", "lorcana"]
                    },
                    "language": {
                      "type": "string",
                      "enum": [
                        "en",
                        "es",
                        "de",
                        "ja",
                        "ko",
                        "zh-cn",
                        "zh-tw",
                        "fr",
                        "it",
                        "pt",
                        "nl",
                        "pl",
                        "ru",
                        "id",
                        "th",
                        "und"
                      ]
                    }
                  },
                  "required": [
                    "updatedAt",
                    "cards",
                    "scopes",
                    "readyScopes",
                    "pendingScopes",
                    "failedScopes",
                    "complete",
                    "game",
                    "language"
                  ],
                  "additionalProperties": false
                },
                "maxItems": 80
              }
            },
            "required": ["items"],
            "additionalProperties": false
          },
          "meta": {
            "type": "object",
            "properties": {
              "schemaVersion": { "type": "string", "const": "1.0.0" },
              "requestId": { "type": "string", "minLength": 8, "maxLength": 128 },
              "generatedAt": { "type": "string", "format": "date-time" }
            },
            "required": ["schemaVersion", "requestId", "generatedAt"],
            "additionalProperties": false
          }
        },
        "required": ["data", "meta"],
        "additionalProperties": false
      }
    }
  }
}
