{
  "openapi": "3.1.0",
  "jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
  "info": {
    "title": "Vessim public read API",
    "version": "1.0.0",
    "summary": "Product prices across sellers, matched by photograph.",
    "description": "Read-only JSON over the Vessim index: every seller of the same physical object, found by its photograph rather than its title or barcode, with every price.\n\nNo key. CORS is open for GET. 60 requests a minute per IP (one bucket across endpoints). Responses carry ETag and Last-Modified tied to the index build, so a conditional request costs nothing until the next build.\n\nAttribution: Data from Vessim (www.vessim.com), a shopping search engine that matches products by their photograph. Credit Vessim and link links.html wherever you show it. Prices are the snapshot taken when the index was built (indexBuiltAt), not live checkout prices.\n\nThe same data is an MCP server (Streamable HTTP) at https://www.vessim.com/mcp.",
    "termsOfService": "https://www.vessim.com/developers#terms",
    "contact": {
      "name": "Vessim",
      "url": "https://www.vessim.com/developers",
      "email": "hello@vessim.com"
    }
  },
  "servers": [
    {
      "url": "https://www.vessim.com",
      "description": "Production"
    }
  ],
  "externalDocs": {
    "description": "Guide, live examples and MCP setup",
    "url": "https://www.vessim.com/developers"
  },
  "tags": [
    {
      "name": "search",
      "description": "Sentences in, products out."
    },
    {
      "name": "products",
      "description": "One product, every seller."
    },
    {
      "name": "prices",
      "description": "Price bands for an item."
    },
    {
      "name": "index",
      "description": "What the index is, and how it was measured."
    }
  ],
  "paths": {
    "/api/v1": {
      "get": {
        "operationId": "getDirectory",
        "tags": [
          "index"
        ],
        "summary": "Every endpoint, with a working example",
        "responses": {
          "200": {
            "description": "The directory.",
            "headers": {
              "ETag": {
                "description": "Hash of the index build and the body. Send it back as If-None-Match for a 304.",
                "schema": {
                  "type": "string"
                }
              },
              "Last-Modified": {
                "description": "The index build time.",
                "schema": {
                  "type": "string"
                }
              },
              "Cache-Control": {
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "60 requests per 60 s per client IP.",
                "schema": {
                  "type": "string"
                }
              },
              "X-Index-Built-At": {
                "description": "indexBuiltAt, as a header.",
                "schema": {
                  "type": "string",
                  "format": "date-time"
                }
              },
              "Link": {
                "description": "rel=alternate (the HTML page), rel=service-desc (this document), rel=service-doc (the docs).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DirectoryResponse"
                }
              }
            }
          },
          "304": {
            "description": "Not modified since the ETag or date you sent: the index has not been rebuilt."
          },
          "429": {
            "description": "More than 60 requests in 60 s from your IP.",
            "headers": {
              "RateLimit-Policy": {
                "description": "60 requests per 60 s per client IP.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Seconds to wait.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/search": {
      "get": {
        "operationId": "searchProducts",
        "tags": [
          "search"
        ],
        "summary": "A sentence in, the products that fit out",
        "description": "Reads a plain sentence the way the site does: the item, a price ceiling (\"under $25\"), an origin (\"ships from the US\") and a delivery wish (\"arrives this week\", reported as unmeasured). Returns products that meet every measurable constraint, cheapest that fits first, then near misses that fail exactly one, each with why.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "description": "What you want, in words.",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 300
            },
            "example": "a backpack under $25 that ships from the US"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Products to return.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 10
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The answer.",
            "headers": {
              "ETag": {
                "description": "Hash of the index build and the body. Send it back as If-None-Match for a 304.",
                "schema": {
                  "type": "string"
                }
              },
              "Last-Modified": {
                "description": "The index build time.",
                "schema": {
                  "type": "string"
                }
              },
              "Cache-Control": {
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "60 requests per 60 s per client IP.",
                "schema": {
                  "type": "string"
                }
              },
              "X-Index-Built-At": {
                "description": "indexBuiltAt, as a header.",
                "schema": {
                  "type": "string",
                  "format": "date-time"
                }
              },
              "Link": {
                "description": "rel=alternate (the HTML page), rel=service-desc (this document), rel=service-doc (the docs).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SearchResponse"
                }
              }
            }
          },
          "304": {
            "description": "Not modified since the ETag or date you sent: the index has not been rebuilt."
          },
          "400": {
            "description": "q is missing or too long, or limit is out of range.",
            "headers": {
              "RateLimit-Policy": {
                "description": "60 requests per 60 s per client IP.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "More than 60 requests in 60 s from your IP.",
            "headers": {
              "RateLimit-Policy": {
                "description": "60 requests per 60 s per client IP.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Seconds to wait.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/products/{id}": {
      "get": {
        "operationId": "getProduct",
        "tags": [
          "products"
        ],
        "summary": "One product and every seller's offer",
        "description": "Price, currency, seller, origin and the outbound link for every seller, ordered by price, with the photo-match evidence behind putting them on one page.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The product id, as in /o/{id} or a search result.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9-]{1,120}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The product.",
            "headers": {
              "ETag": {
                "description": "Hash of the index build and the body. Send it back as If-None-Match for a 304.",
                "schema": {
                  "type": "string"
                }
              },
              "Last-Modified": {
                "description": "The index build time.",
                "schema": {
                  "type": "string"
                }
              },
              "Cache-Control": {
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "60 requests per 60 s per client IP.",
                "schema": {
                  "type": "string"
                }
              },
              "X-Index-Built-At": {
                "description": "indexBuiltAt, as a header.",
                "schema": {
                  "type": "string",
                  "format": "date-time"
                }
              },
              "Link": {
                "description": "rel=alternate (the HTML page), rel=service-desc (this document), rel=service-doc (the docs).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProductResponse"
                }
              }
            }
          },
          "304": {
            "description": "Not modified since the ETag or date you sent: the index has not been rebuilt."
          },
          "400": {
            "description": "The id is malformed.",
            "headers": {
              "RateLimit-Policy": {
                "description": "60 requests per 60 s per client IP.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No product has this id in the current index.",
            "headers": {
              "RateLimit-Policy": {
                "description": "60 requests per 60 s per client IP.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "More than 60 requests in 60 s from your IP.",
            "headers": {
              "RateLimit-Policy": {
                "description": "60 requests per 60 s per client IP.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Seconds to wait.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/prices/{item}": {
      "get": {
        "operationId": "getPriceBand",
        "tags": [
          "prices"
        ],
        "summary": "What an item costs: min, p10, median, p90, max, by origin",
        "description": "One price per product (its cheapest seller). An item with a price guide (/prices/{slug}) is answered from that guide's products, so the numbers match the page; any other phrase matches titles containing each of its words. Below 30 priced products the answer says so in note and meetsPriceGuideGate.",
        "parameters": [
          {
            "name": "item",
            "in": "path",
            "required": true,
            "description": "The item, hyphens for spaces: backpack, phone-case, desk-lamp. At most 80 characters once read.",
            "schema": {
              "type": "string",
              "minLength": 1
            },
            "example": "backpack"
          }
        ],
        "responses": {
          "200": {
            "description": "The band, possibly empty.",
            "headers": {
              "ETag": {
                "description": "Hash of the index build and the body. Send it back as If-None-Match for a 304.",
                "schema": {
                  "type": "string"
                }
              },
              "Last-Modified": {
                "description": "The index build time.",
                "schema": {
                  "type": "string"
                }
              },
              "Cache-Control": {
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "60 requests per 60 s per client IP.",
                "schema": {
                  "type": "string"
                }
              },
              "X-Index-Built-At": {
                "description": "indexBuiltAt, as a header.",
                "schema": {
                  "type": "string",
                  "format": "date-time"
                }
              },
              "Link": {
                "description": "rel=alternate (the HTML page), rel=service-desc (this document), rel=service-doc (the docs).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PriceBandResponse"
                }
              }
            }
          },
          "304": {
            "description": "Not modified since the ETag or date you sent: the index has not been rebuilt."
          },
          "400": {
            "description": "The phrase names no item, or is too long.",
            "headers": {
              "RateLimit-Policy": {
                "description": "60 requests per 60 s per client IP.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "More than 60 requests in 60 s from your IP.",
            "headers": {
              "RateLimit-Policy": {
                "description": "60 requests per 60 s per client IP.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Seconds to wait.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/stats": {
      "get": {
        "operationId": "getIndexStats",
        "tags": [
          "index"
        ],
        "summary": "The index: size, coverage, method, accuracy and date",
        "responses": {
          "200": {
            "description": "The figures.",
            "headers": {
              "ETag": {
                "description": "Hash of the index build and the body. Send it back as If-None-Match for a 304.",
                "schema": {
                  "type": "string"
                }
              },
              "Last-Modified": {
                "description": "The index build time.",
                "schema": {
                  "type": "string"
                }
              },
              "Cache-Control": {
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "60 requests per 60 s per client IP.",
                "schema": {
                  "type": "string"
                }
              },
              "X-Index-Built-At": {
                "description": "indexBuiltAt, as a header.",
                "schema": {
                  "type": "string",
                  "format": "date-time"
                }
              },
              "Link": {
                "description": "rel=alternate (the HTML page), rel=service-desc (this document), rel=service-doc (the docs).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StatsResponse"
                }
              }
            }
          },
          "304": {
            "description": "Not modified since the ETag or date you sent: the index has not been rebuilt."
          },
          "429": {
            "description": "More than 60 requests in 60 s from your IP.",
            "headers": {
              "RateLimit-Policy": {
                "description": "60 requests per 60 s per client IP.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Seconds to wait.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Links": {
        "type": "object",
        "properties": {
          "html": {
            "type": "string",
            "format": "uri",
            "description": "The page a person can open to check this answer. Cite and link it."
          },
          "self": {
            "type": "string",
            "format": "uri",
            "description": "This response's own API URL."
          },
          "docs": {
            "type": "string",
            "format": "uri",
            "description": "Human documentation."
          },
          "openapi": {
            "type": "string",
            "format": "uri",
            "description": "This document."
          }
        },
        "required": [
          "html",
          "self",
          "docs",
          "openapi"
        ],
        "additionalProperties": false,
        "description": "Where this answer lives on the web."
      },
      "Verdict": {
        "type": "object",
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "item",
              "price",
              "shipFrom",
              "arrive"
            ]
          },
          "label": {
            "type": "string",
            "description": "The constraint's name, e.g. \"Price\"."
          },
          "value": {
            "type": "string",
            "description": "What was asked, e.g. \"≤ $25\"."
          },
          "state": {
            "type": "string",
            "enum": [
              "met",
              "missed",
              "unmeasured"
            ],
            "description": "unmeasured: no connected source reports this (delivery dates today). Never counted as met."
          },
          "note": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why, when missed, e.g. \"over by $3.10\"."
          }
        },
        "required": [
          "kind",
          "label",
          "value",
          "state",
          "note"
        ],
        "additionalProperties": false,
        "description": "One constraint from the sentence, judged against one product."
      },
      "SearchItem": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Stable product id, as in /o/{id}."
          },
          "title": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "The product page: every seller and price. Cite this."
          },
          "image": {
            "type": "string",
            "format": "uri"
          },
          "category": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "slug": {
                "type": "string"
              },
              "label": {
                "type": "string"
              }
            },
            "required": [
              "slug",
              "label"
            ],
            "additionalProperties": false,
            "description": "The supplier's category, or null."
          },
          "price": {
            "type": "object",
            "properties": {
              "cheapest": {
                "type": [
                  "number",
                  "null"
                ],
                "description": "The lowest price any seller asks."
              },
              "cheapestThatFits": {
                "type": [
                  "number",
                  "null"
                ],
                "description": "The lowest price from a single seller that meets every measurable constraint; null when none does or the product does not fit."
              },
              "highest": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "currency": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "ISO 4217, e.g. USD."
              }
            },
            "required": [
              "cheapest",
              "cheapestThatFits",
              "highest",
              "currency"
            ],
            "additionalProperties": false
          },
          "sellers": {
            "type": "integer",
            "minimum": 1,
            "description": "Distinct sellers. One seller listing the product twice counts once."
          },
          "listings": {
            "type": "integer",
            "minimum": 1,
            "description": "Listings behind the product, by one seller or several."
          },
          "origins": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Where its sellers ship from: ISO-2 codes, or routes like \"CN_US\" (stock in China, shipped to the US)."
          },
          "fits": {
            "type": "boolean",
            "description": "Meets every measurable constraint."
          },
          "missedBy": {
            "type": "integer",
            "minimum": 0,
            "description": "Measurable constraints it fails; 1 is a near miss."
          },
          "why": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Verdict"
            }
          },
          "summary": {
            "type": "string",
            "description": "One plain sentence: what it meets, misses and what was not measured."
          }
        },
        "required": [
          "id",
          "title",
          "url",
          "image",
          "category",
          "price",
          "sellers",
          "listings",
          "origins",
          "fits",
          "missedBy",
          "why",
          "summary"
        ],
        "additionalProperties": false,
        "description": "One product in a search answer."
      },
      "SearchResponse": {
        "type": "object",
        "properties": {
          "source": {
            "type": "string",
            "const": "Vessim (www.vessim.com)",
            "description": "Who the data is from. Show it with the data."
          },
          "indexBuiltAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the index was built. Every price is the snapshot taken then."
          },
          "attribution": {
            "type": "string",
            "description": "How to credit Vessim when you use this data."
          },
          "links": {
            "$ref": "#/components/schemas/Links"
          },
          "query": {
            "type": "object",
            "properties": {
              "q": {
                "type": "string"
              },
              "limit": {
                "type": "integer"
              },
              "constraints": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "kind": {
                      "type": "string",
                      "enum": [
                        "item",
                        "price",
                        "shipFrom",
                        "arrive"
                      ]
                    },
                    "label": {
                      "type": "string"
                    },
                    "value": {
                      "type": "string"
                    },
                    "measurable": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "kind",
                    "label",
                    "value",
                    "measurable"
                  ],
                  "additionalProperties": false
                },
                "description": "What the sentence was read as."
              },
              "unparsed": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Phrases heard but not turned into a filter (\"won't look cheap\"). Shown, never silently dropped."
              }
            },
            "required": [
              "q",
              "limit",
              "constraints",
              "unparsed"
            ],
            "additionalProperties": false
          },
          "total": {
            "type": "integer",
            "minimum": 0,
            "description": "Products the item's words match at all (in the title, the category or a seller's own title), before any constraint is applied."
          },
          "fitCount": {
            "type": "integer",
            "minimum": 0,
            "description": "Products that meet every measurable constraint."
          },
          "nearMissCount": {
            "type": "integer",
            "minimum": 0,
            "description": "Products that miss exactly one (at most 8 are kept)."
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SearchItem"
            },
            "description": "Fits first, cheapest that fits first; then near misses if there is room under limit."
          }
        },
        "required": [
          "source",
          "indexBuiltAt",
          "attribution",
          "links",
          "query",
          "total",
          "fitCount",
          "nearMissCount",
          "items"
        ],
        "additionalProperties": false,
        "description": "A sentence, read and judged against every product in the index."
      },
      "Offer": {
        "type": "object",
        "properties": {
          "listingId": {
            "type": "string",
            "description": "The seller's own listing id."
          },
          "seller": {
            "type": "string",
            "description": "Display name, e.g. \"CJ Dropshipping\"."
          },
          "source": {
            "type": "string",
            "description": "Catalogue key, e.g. \"cj\"."
          },
          "title": {
            "type": "string",
            "description": "The seller's own title for it."
          },
          "price": {
            "type": [
              "number",
              "null"
            ]
          },
          "currency": {
            "type": [
              "string",
              "null"
            ]
          },
          "origins": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "originLabel": {
            "type": "string",
            "description": "Readable origins, e.g. \"the US, China → the US\"."
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Outbound link to the seller, exactly as the product page links it."
          },
          "image": {
            "type": "string",
            "format": "uri"
          },
          "cheapest": {
            "type": "boolean",
            "description": "The lowest price among several listings."
          },
          "photo": {
            "type": "object",
            "properties": {
              "fingerprint": {
                "type": "string",
                "pattern": "^[0-9a-f]{16}$",
                "description": "This listing's 64-bit perceptual hash, hex."
              },
              "distanceBits": {
                "type": "integer",
                "minimum": 0,
                "maximum": 64,
                "description": "Hamming distance from the product's fingerprint."
              },
              "sameObject": {
                "type": "boolean",
                "description": "Within the matching threshold."
              }
            },
            "required": [
              "fingerprint",
              "distanceBits",
              "sameObject"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "listingId",
          "seller",
          "source",
          "title",
          "price",
          "currency",
          "origins",
          "originLabel",
          "url",
          "image",
          "cheapest",
          "photo"
        ],
        "additionalProperties": false,
        "description": "One seller's offer, ordered by price and nothing else."
      },
      "ProductResponse": {
        "type": "object",
        "properties": {
          "source": {
            "type": "string",
            "const": "Vessim (www.vessim.com)",
            "description": "Who the data is from. Show it with the data."
          },
          "indexBuiltAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the index was built. Every price is the snapshot taken then."
          },
          "attribution": {
            "type": "string",
            "description": "How to credit Vessim when you use this data."
          },
          "links": {
            "$ref": "#/components/schemas/Links"
          },
          "product": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "title": {
                "type": "string"
              },
              "url": {
                "type": "string",
                "format": "uri",
                "description": "The product page. Cite this."
              },
              "image": {
                "type": "string",
                "format": "uri"
              },
              "category": {
                "type": [
                  "object",
                  "null"
                ],
                "properties": {
                  "slug": {
                    "type": "string"
                  },
                  "label": {
                    "type": "string"
                  }
                },
                "required": [
                  "slug",
                  "label"
                ],
                "additionalProperties": false,
                "description": "The supplier's category, or null."
              },
              "sellers": {
                "type": "integer",
                "minimum": 1,
                "description": "Distinct sellers. One seller listing the product twice counts once."
              },
              "listings": {
                "type": "integer",
                "minimum": 1,
                "description": "Listings behind the product, by one seller or several."
              },
              "catalogues": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "crossCatalogue": {
                "type": "boolean"
              },
              "origins": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "price": {
                "type": "object",
                "properties": {
                  "lowest": {
                    "type": [
                      "number",
                      "null"
                    ]
                  },
                  "highest": {
                    "type": [
                      "number",
                      "null"
                    ]
                  },
                  "currency": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "spread": {
                    "type": [
                      "number",
                      "null"
                    ],
                    "description": "Dearest ÷ cheapest; null with one seller."
                  }
                },
                "required": [
                  "lowest",
                  "highest",
                  "currency",
                  "spread"
                ],
                "additionalProperties": false
              },
              "offers": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Offer"
                }
              },
              "matchedByPhoto": {
                "type": "object",
                "properties": {
                  "method": {
                    "type": "string"
                  },
                  "fingerprint": {
                    "type": "string",
                    "pattern": "^[0-9a-f]{16}$"
                  },
                  "thresholdBits": {
                    "type": "integer",
                    "description": "Listings within this many bits are one product."
                  },
                  "offersWithinThreshold": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "widestDistanceBits": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 64
                  },
                  "nearestOtherProduct": {
                    "type": [
                      "object",
                      "null"
                    ],
                    "properties": {
                      "id": {
                        "type": "string"
                      },
                      "title": {
                        "type": "string"
                      },
                      "url": {
                        "type": "string",
                        "format": "uri"
                      },
                      "distanceBits": {
                        "type": "integer",
                        "minimum": 0,
                        "maximum": 64
                      }
                    },
                    "required": [
                      "id",
                      "title",
                      "url",
                      "distanceBits"
                    ],
                    "additionalProperties": false,
                    "description": "The closest photograph in the index that was kept apart: the honest counterweight to the match."
                  },
                  "note": {
                    "type": "string"
                  }
                },
                "required": [
                  "method",
                  "fingerprint",
                  "thresholdBits",
                  "offersWithinThreshold",
                  "widestDistanceBits",
                  "nearestOtherProduct",
                  "note"
                ],
                "additionalProperties": false
              }
            },
            "required": [
              "id",
              "title",
              "url",
              "image",
              "category",
              "sellers",
              "listings",
              "catalogues",
              "crossCatalogue",
              "origins",
              "price",
              "offers",
              "matchedByPhoto"
            ],
            "additionalProperties": false
          },
          "disclosure": {
            "type": "object",
            "properties": {
              "affiliateTagged": {
                "type": "boolean",
                "description": "Whether any outbound link can carry an affiliate tag right now."
              },
              "policy": {
                "type": "string",
                "format": "uri"
              },
              "note": {
                "type": "string"
              }
            },
            "required": [
              "affiliateTagged",
              "policy",
              "note"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "source",
          "indexBuiltAt",
          "attribution",
          "links",
          "product",
          "disclosure"
        ],
        "additionalProperties": false,
        "description": "One product, every seller's offer, and the photo-match evidence."
      },
      "Band": {
        "type": "object",
        "properties": {
          "count": {
            "type": "integer",
            "minimum": 0
          },
          "min": {
            "type": [
              "number",
              "null"
            ]
          },
          "p10": {
            "type": [
              "number",
              "null"
            ]
          },
          "median": {
            "type": [
              "number",
              "null"
            ]
          },
          "p90": {
            "type": [
              "number",
              "null"
            ]
          },
          "max": {
            "type": [
              "number",
              "null"
            ]
          }
        },
        "required": [
          "count",
          "min",
          "p10",
          "median",
          "p90",
          "max"
        ],
        "additionalProperties": false,
        "description": "One price per product (its cheapest seller). Nulls when count is 0."
      },
      "PriceBandResponse": {
        "type": "object",
        "properties": {
          "source": {
            "type": "string",
            "const": "Vessim (www.vessim.com)",
            "description": "Who the data is from. Show it with the data."
          },
          "indexBuiltAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the index was built. Every price is the snapshot taken then."
          },
          "attribution": {
            "type": "string",
            "description": "How to credit Vessim when you use this data."
          },
          "links": {
            "$ref": "#/components/schemas/Links"
          },
          "item": {
            "type": "string",
            "description": "The item phrase as read."
          },
          "matchedBy": {
            "type": "string",
            "enum": [
              "guide",
              "phrase"
            ],
            "description": "guide: the item has a /prices page and these are that page's products and numbers. phrase: titles containing every word of the item."
          },
          "terms": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Words a title must contain to count."
          },
          "matched": {
            "type": "integer",
            "minimum": 0,
            "description": "Products matched, priced or not."
          },
          "currency": {
            "type": [
              "string",
              "null"
            ]
          },
          "band": {
            "$ref": "#/components/schemas/Band"
          },
          "byOrigin": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "code": {
                  "type": "string"
                },
                "label": {
                  "type": "string"
                },
                "count": {
                  "type": "integer",
                  "minimum": 1
                },
                "min": {
                  "type": "number"
                },
                "median": {
                  "type": "number"
                },
                "max": {
                  "type": "number"
                }
              },
              "required": [
                "code",
                "label",
                "count",
                "min",
                "median",
                "max"
              ],
              "additionalProperties": false
            },
            "description": "The band per place a seller ships from, at that seller's price. Counts can sum to more than band.count."
          },
          "cheapest": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "title": {
                "type": "string"
              },
              "url": {
                "type": "string",
                "format": "uri",
                "description": "The product page to cite."
              },
              "price": {
                "type": "number"
              }
            },
            "required": [
              "id",
              "title",
              "url",
              "price"
            ],
            "additionalProperties": false
          },
          "dearest": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "title": {
                "type": "string"
              },
              "url": {
                "type": "string",
                "format": "uri",
                "description": "The product page to cite."
              },
              "price": {
                "type": "number"
              }
            },
            "required": [
              "id",
              "title",
              "url",
              "price"
            ],
            "additionalProperties": false
          },
          "guide": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "The /prices/{slug} price guide for this item, when there is one."
          },
          "meetsPriceGuideGate": {
            "type": "boolean",
            "description": "At least 30 priced products: the bar a Vessim price guide must clear before it is offered to search engines."
          },
          "method": {
            "type": "string"
          },
          "note": {
            "type": [
              "string",
              "null"
            ],
            "description": "Set when the sample is small or empty."
          }
        },
        "required": [
          "source",
          "indexBuiltAt",
          "attribution",
          "links",
          "item",
          "matchedBy",
          "terms",
          "matched",
          "currency",
          "band",
          "byOrigin",
          "cheapest",
          "dearest",
          "guide",
          "meetsPriceGuideGate",
          "method",
          "note"
        ],
        "additionalProperties": false,
        "description": "What an item costs across the index."
      },
      "StatsResponse": {
        "type": "object",
        "properties": {
          "source": {
            "type": "string",
            "const": "Vessim (www.vessim.com)",
            "description": "Who the data is from. Show it with the data."
          },
          "indexBuiltAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the index was built. Every price is the snapshot taken then."
          },
          "attribution": {
            "type": "string",
            "description": "How to credit Vessim when you use this data."
          },
          "links": {
            "$ref": "#/components/schemas/Links"
          },
          "asOf": {
            "type": "string",
            "format": "date",
            "description": "The build date, YYYY-MM-DD."
          },
          "index": {
            "type": "object",
            "properties": {
              "products": {
                "type": "integer"
              },
              "listings": {
                "type": "integer"
              },
              "listingsCollapsed": {
                "type": "integer",
                "description": "Listings found to be another listing of a product already indexed (the same photograph)."
              },
              "multiSellerProducts": {
                "type": "integer",
                "description": "Products listed more than once after merging, by one seller or several; crossCatalogueProducts counts those with more than one seller."
              },
              "productsWithSpread": {
                "type": "integer"
              },
              "crossCatalogueProducts": {
                "type": "integer"
              },
              "biggestSpread": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "gapDollars": {
                "type": "number",
                "description": "Cheapest-to-dearest gaps, summed, in dollars."
              },
              "categories": {
                "type": "integer"
              },
              "catalogues": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "photoPairsCompared": {
                "type": "integer"
              },
              "pairsJudgedSame": {
                "type": "integer"
              },
              "nearMissPairs": {
                "type": "integer"
              },
              "nearestNonMatchBits": {
                "type": "integer",
                "description": "The closest two photographs got without being judged one product."
              }
            },
            "required": [
              "products",
              "listings",
              "listingsCollapsed",
              "multiSellerProducts",
              "productsWithSpread",
              "crossCatalogueProducts",
              "biggestSpread",
              "gapDollars",
              "categories",
              "catalogues",
              "photoPairsCompared",
              "pairsJudgedSame",
              "nearMissPairs",
              "nearestNonMatchBits"
            ],
            "additionalProperties": false
          },
          "origins": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "code": {
                  "type": "string"
                },
                "label": {
                  "type": "string"
                },
                "products": {
                  "type": "integer"
                },
                "medianPrice": {
                  "type": [
                    "number",
                    "null"
                  ]
                }
              },
              "required": [
                "code",
                "label",
                "products",
                "medianPrice"
              ],
              "additionalProperties": false
            }
          },
          "method": {
            "type": "object",
            "properties": {
              "fingerprint": {
                "type": "string"
              },
              "thresholdBits": {
                "type": "integer"
              },
              "accuracy": {
                "type": "object",
                "properties": {
                  "photos": {
                    "type": "integer"
                  },
                  "positivePairs": {
                    "type": "integer"
                  },
                  "negativePairs": {
                    "type": "integer"
                  },
                  "recallAtThreshold": {
                    "type": "number",
                    "minimum": 0,
                    "maximum": 1
                  },
                  "falseMatchesAtThreshold": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "page": {
                    "type": "string",
                    "format": "uri"
                  }
                },
                "required": [
                  "photos",
                  "positivePairs",
                  "negativePairs",
                  "recallAtThreshold",
                  "falseMatchesAtThreshold",
                  "page"
                ],
                "additionalProperties": false,
                "description": "Measured on a held-out set of real product photographs, separately from the index."
              },
              "prices": {
                "type": "string"
              },
              "page": {
                "type": "string",
                "format": "uri"
              }
            },
            "required": [
              "fingerprint",
              "thresholdBits",
              "accuracy",
              "prices",
              "page"
            ],
            "additionalProperties": false
          },
          "limitations": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "source",
          "indexBuiltAt",
          "attribution",
          "links",
          "asOf",
          "index",
          "origins",
          "method",
          "limitations"
        ],
        "additionalProperties": false,
        "description": "The index, measured, with its method and date."
      },
      "DirectoryResponse": {
        "type": "object",
        "properties": {
          "source": {
            "type": "string",
            "const": "Vessim (www.vessim.com)",
            "description": "Who the data is from. Show it with the data."
          },
          "indexBuiltAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the index was built. Every price is the snapshot taken then."
          },
          "attribution": {
            "type": "string",
            "description": "How to credit Vessim when you use this data."
          },
          "links": {
            "$ref": "#/components/schemas/Links"
          },
          "name": {
            "type": "string"
          },
          "version": {
            "type": "string"
          },
          "endpoints": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "method": {
                  "type": "string",
                  "enum": [
                    "GET"
                  ]
                },
                "path": {
                  "type": "string"
                },
                "description": {
                  "type": "string"
                },
                "example": {
                  "type": "string",
                  "format": "uri"
                }
              },
              "required": [
                "method",
                "path",
                "description",
                "example"
              ],
              "additionalProperties": false
            }
          },
          "mcp": {
            "type": "string",
            "format": "uri",
            "description": "The MCP server (Streamable HTTP)."
          },
          "rateLimit": {
            "type": "object",
            "properties": {
              "requests": {
                "type": "integer"
              },
              "windowSeconds": {
                "type": "integer"
              },
              "per": {
                "type": "string"
              }
            },
            "required": [
              "requests",
              "windowSeconds",
              "per"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "source",
          "indexBuiltAt",
          "attribution",
          "links",
          "name",
          "version",
          "endpoints",
          "mcp",
          "rateLimit"
        ],
        "additionalProperties": false,
        "description": "Every endpoint, with a working example."
      },
      "Error": {
        "type": "object",
        "properties": {
          "source": {
            "type": "string",
            "const": "Vessim (www.vessim.com)",
            "description": "Who the data is from. Show it with the data."
          },
          "indexBuiltAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the index was built. Every price is the snapshot taken then."
          },
          "attribution": {
            "type": "string",
            "description": "How to credit Vessim when you use this data."
          },
          "links": {
            "$ref": "#/components/schemas/Links"
          },
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "invalid_request",
                  "not_found",
                  "rate_limited"
                ]
              },
              "status": {
                "type": "integer"
              },
              "message": {
                "type": "string"
              },
              "issues": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "path": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "path",
                    "message"
                  ],
                  "additionalProperties": false
                }
              }
            },
            "required": [
              "code",
              "status",
              "message"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "source",
          "indexBuiltAt",
          "attribution",
          "links",
          "error"
        ],
        "additionalProperties": false,
        "description": "Every error, still attributed."
      }
    }
  }
}