{
  "components": {
    "headers": {
      "RateLimit-Limit": {
        "description": "Requests allowed in the tighter of the two windows (minute or day).",
        "schema": {
          "type": "integer"
        }
      },
      "RateLimit-Policy": {
        "description": "Both windows, e.g. `300;w=60, 10000;w=86400`.",
        "schema": {
          "type": "string"
        }
      },
      "RateLimit-Remaining": {
        "description": "Requests left in that window.",
        "schema": {
          "type": "integer"
        }
      },
      "RateLimit-Reset": {
        "description": "Seconds until that window resets.",
        "schema": {
          "type": "integer"
        }
      }
    },
    "parameters": {
      "listingId": {
        "description": "Listing id.",
        "in": "path",
        "name": "id",
        "required": true,
        "schema": {
          "minimum": 1,
          "type": "integer"
        }
      },
      "productId": {
        "description": "Product id.",
        "in": "path",
        "name": "id",
        "required": true,
        "schema": {
          "minimum": 1,
          "type": "integer"
        }
      },
      "q": {
        "description": "Free text (up to 100 characters): a product, brand, category, or the digits under a barcode.",
        "in": "query",
        "name": "q",
        "schema": {
          "maxLength": 100,
          "type": "string"
        }
      },
      "storeId": {
        "description": "Store id.",
        "in": "path",
        "name": "id",
        "required": true,
        "schema": {
          "minimum": 1,
          "type": "integer"
        }
      }
    },
    "responses": {
      "BadRequest": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        },
        "description": "A parameter is malformed or out of range."
      },
      "NotFound": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        },
        "description": "No such thing."
      },
      "RateLimited": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        },
        "description": "Over the per-IP budget. Wait `Retry-After` seconds, or use the exports for bulk work.",
        "headers": {
          "Retry-After": {
            "schema": {
              "type": "integer"
            }
          }
        }
      }
    },
    "schemas": {
      "Alternative": {
        "description": "Another product in the same generic.",
        "properties": {
          "brand": {
            "type": [
              "string",
              "null"
            ]
          },
          "id": {
            "description": "Product id.",
            "type": "integer"
          },
          "image": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/ImagePath"
              },
              {
                "type": "null"
              }
            ]
          },
          "name": {
            "type": "string"
          },
          "own_brand": {
            "description": "A house brand.",
            "type": "boolean"
          },
          "price": {
            "description": "Cheapest current shelf price, cents.",
            "type": [
              "integer",
              "null"
            ]
          },
          "retailer": {
            "description": "Where that price is.",
            "type": [
              "string",
              "null"
            ]
          },
          "retailers": {
            "description": "Chains that sell it.",
            "type": "integer"
          },
          "size_text": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "id",
          "name",
          "retailers",
          "own_brand"
        ],
        "type": "object"
      },
      "Area": {
        "properties": {
          "chains": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "key": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "level": {
            "description": "Item-weighted geometric mean of the stores' levels.",
            "type": [
              "number",
              "null"
            ]
          },
          "stores": {
            "type": "integer"
          },
          "towns": {
            "items": {
              "type": "string"
            },
            "type": "array"
          }
        },
        "required": [
          "key",
          "label",
          "stores",
          "chains",
          "towns"
        ],
        "type": "object"
      },
      "Areas": {
        "properties": {
          "regions": {
            "items": {
              "$ref": "#/components/schemas/Area"
            },
            "type": "array"
          },
          "towns": {
            "items": {
              "$ref": "#/components/schemas/Area"
            },
            "type": "array"
          },
          "urban": {
            "description": "By Stats NZ urban class, main centres first.",
            "items": {
              "$ref": "#/components/schemas/Area"
            },
            "type": "array"
          }
        },
        "required": [
          "regions",
          "urban",
          "towns"
        ],
        "type": "object"
      },
      "Attribute": {
        "properties": {
          "key": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "value": {
            "type": "string"
          }
        },
        "required": [
          "key",
          "label",
          "value"
        ],
        "type": "object"
      },
      "BasketItem": {
        "properties": {
          "code": {
            "description": "Stats NZ classification code.",
            "type": "string"
          },
          "exclude": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "group_code": {
            "type": "string"
          },
          "group_name": {
            "type": "string"
          },
          "group_weight": {
            "type": "number"
          },
          "id": {
            "type": "integer"
          },
          "keywords": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "match_list": {
            "description": "Only with `include=matches`.",
            "items": {
              "$ref": "#/components/schemas/BasketMatch"
            },
            "type": "array"
          },
          "matches": {
            "description": "Listings currently matched.",
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "observable": {
            "description": "Whether the group is observable from supermarket shelves.",
            "type": "boolean"
          },
          "per_unit": {
            "type": "boolean"
          },
          "qty_max": {
            "type": [
              "number",
              "null"
            ]
          },
          "qty_min": {
            "type": [
              "number",
              "null"
            ]
          },
          "relative": {
            "description": "Latest relative price of the item (1.0 = base).",
            "type": [
              "number",
              "null"
            ]
          },
          "spec": {
            "description": "The size specification priced, e.g. `Apples, fresh — 1 kg`.",
            "type": [
              "string",
              "null"
            ]
          },
          "stale": {
            "description": "Carried forward past the staleness cap.",
            "type": [
              "boolean",
              "null"
            ]
          },
          "unit": {
            "type": [
              "string",
              "null"
            ]
          },
          "weight": {
            "description": "Item weight within the basket, percent.",
            "type": "number"
          }
        },
        "required": [
          "id",
          "code",
          "name",
          "group_code",
          "group_name",
          "group_weight",
          "weight",
          "observable",
          "per_unit",
          "keywords",
          "exclude",
          "matches"
        ],
        "type": "object"
      },
      "BasketItemDetail": {
        "properties": {
          "group": {
            "type": "string"
          },
          "group_name": {
            "type": "string"
          },
          "id": {
            "type": "integer"
          },
          "listings": {
            "items": {
              "$ref": "#/components/schemas/BasketItemListing"
            },
            "type": "array"
          },
          "name": {
            "type": "string"
          },
          "per_unit": {
            "type": "boolean"
          },
          "spec": {
            "type": [
              "string",
              "null"
            ]
          },
          "unit": {
            "enum": [
              "g",
              "ml",
              "each",
              "m",
              null
            ],
            "type": [
              "string",
              "null"
            ]
          },
          "weight": {
            "type": "number"
          }
        },
        "required": [
          "id",
          "name",
          "group",
          "group_name",
          "per_unit",
          "weight",
          "listings"
        ],
        "type": "object"
      },
      "BasketItemListing": {
        "properties": {
          "available": {
            "type": "boolean"
          },
          "basis_price": {
            "description": "The price that counts, cents, on the item's comparison basis.",
            "type": [
              "number",
              "null"
            ]
          },
          "brand": {
            "type": [
              "string",
              "null"
            ]
          },
          "image": {
            "type": [
              "string",
              "null"
            ]
          },
          "listing_id": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "origin": {
            "enum": [
              "auto",
              "manual"
            ],
            "type": "string"
          },
          "price": {
            "description": "Shelf price, cents.",
            "type": [
              "integer",
              "null"
            ]
          },
          "priced_on": {
            "description": "Last day the price was seen.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/Day"
              },
              {
                "type": "null"
              }
            ]
          },
          "promo_price": {
            "description": "Special or club price, cents, when one applies.",
            "type": [
              "integer",
              "null"
            ]
          },
          "qty": {
            "type": [
              "number",
              "null"
            ]
          },
          "retailer": {
            "description": "Retailer slug.",
            "type": "string"
          },
          "size_text": {
            "type": [
              "string",
              "null"
            ]
          },
          "unit": {
            "enum": [
              "g",
              "ml",
              "each",
              "m",
              null
            ],
            "type": [
              "string",
              "null"
            ]
          },
          "valid_from": {
            "$ref": "#/components/schemas/Day"
          },
          "weighable": {
            "description": "Sold loose by weight; the price is per kilogram.",
            "type": "boolean"
          }
        },
        "required": [
          "listing_id",
          "retailer",
          "name",
          "weighable",
          "valid_from",
          "origin",
          "available"
        ],
        "type": "object"
      },
      "BasketMatch": {
        "properties": {
          "brand": {
            "type": [
              "string",
              "null"
            ]
          },
          "confidence": {
            "type": [
              "number",
              "null"
            ]
          },
          "image": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/ImagePath"
              },
              {
                "type": "null"
              }
            ]
          },
          "linked_relative": {
            "type": [
              "number",
              "null"
            ]
          },
          "listing_id": {
            "type": "integer"
          },
          "locked": {
            "type": "boolean"
          },
          "name": {
            "type": "string"
          },
          "origin": {
            "enum": [
              "auto",
              "manual"
            ],
            "type": "string"
          },
          "price": {
            "type": [
              "integer",
              "null"
            ]
          },
          "promo_price": {
            "type": [
              "integer",
              "null"
            ]
          },
          "ref_price": {
            "description": "Price when the match was made; the link factor's base.",
            "type": [
              "integer",
              "null"
            ]
          },
          "retailer": {
            "type": "string"
          },
          "size_text": {
            "type": [
              "string",
              "null"
            ]
          },
          "valid_from": {
            "$ref": "#/components/schemas/Day"
          }
        },
        "required": [
          "listing_id",
          "retailer",
          "name",
          "origin",
          "locked",
          "valid_from"
        ],
        "type": "object"
      },
      "Bin": {
        "properties": {
          "bucket": {
            "description": "0 and 21 are the tails; 1–20 span 0.80–1.20 in 0.02 steps.",
            "type": "integer"
          },
          "n": {
            "type": "integer"
          }
        },
        "required": [
          "bucket",
          "n"
        ],
        "type": "object"
      },
      "CategoryNode": {
        "properties": {
          "label": {
            "type": "string"
          },
          "listings": {
            "description": "Listings under this node (matches, when `q` is set).",
            "type": "integer"
          }
        },
        "required": [
          "label",
          "listings"
        ],
        "type": "object"
      },
      "Cents": {
        "description": "New Zealand cents.",
        "example": 499,
        "type": "integer"
      },
      "Compare": {
        "properties": {
          "groups": {
            "items": {
              "properties": {
                "code": {
                  "type": "string"
                },
                "levels": {
                  "additionalProperties": {
                    "maxItems": 2,
                    "minItems": 2,
                    "prefixItems": [
                      {
                        "type": "number"
                      },
                      {
                        "type": "integer"
                      }
                    ],
                    "type": "array"
                  },
                  "type": "object"
                },
                "name": {
                  "type": "string"
                },
                "weight": {
                  "type": "number"
                }
              },
              "required": [
                "code",
                "name",
                "weight",
                "levels"
              ],
              "type": "object"
            },
            "type": "array"
          },
          "headline": {
            "additionalProperties": {
              "maxItems": 2,
              "minItems": 2,
              "prefixItems": [
                {
                  "type": "number"
                },
                {
                  "type": "integer"
                }
              ],
              "type": "array"
            },
            "description": "Retailer slug → `[level, items]`; `1.0` is the cross-chain median.",
            "type": "object"
          },
          "items": {
            "items": {
              "properties": {
                "group": {
                  "description": "Group code.",
                  "type": "string"
                },
                "id": {
                  "description": "Basket item id.",
                  "type": "integer"
                },
                "name": {
                  "type": "string"
                },
                "per_unit": {
                  "description": "Prices are per kg / L rather than per pack.",
                  "type": "boolean"
                },
                "prices": {
                  "additionalProperties": {
                    "type": "integer"
                  },
                  "description": "Retailer slug → median price, cents.",
                  "type": "object"
                },
                "spec": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "unit": {
                  "type": [
                    "string",
                    "null"
                  ]
                }
              },
              "required": [
                "id",
                "name",
                "group",
                "per_unit",
                "prices"
              ],
              "type": "object"
            },
            "type": "array"
          },
          "retailers": {
            "items": {
              "type": "string"
            },
            "type": "array"
          }
        },
        "required": [
          "retailers",
          "headline",
          "groups",
          "items"
        ],
        "type": "object"
      },
      "CurrentPrice": {
        "description": "The latest national span of a listing.",
        "properties": {
          "day": {
            "$ref": "#/components/schemas/Day"
          },
          "multibuy_price": {
            "type": [
              "integer",
              "null"
            ]
          },
          "multibuy_qty": {
            "type": [
              "integer",
              "null"
            ]
          },
          "price": {
            "$ref": "#/components/schemas/Cents"
          },
          "promo_kind": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/PromoKind"
              },
              {
                "type": "null"
              }
            ]
          },
          "promo_price": {
            "type": [
              "integer",
              "null"
            ]
          }
        },
        "required": [
          "day",
          "price"
        ],
        "type": "object"
      },
      "DailyStats": {
        "description": "Columnar daily spread across stores. Empty object when there is none.",
        "properties": {
          "day": {
            "items": {
              "$ref": "#/components/schemas/Day"
            },
            "type": "array"
          },
          "max": {
            "items": {
              "type": "integer"
            },
            "type": "array"
          },
          "med": {
            "items": {
              "type": "integer"
            },
            "type": "array"
          },
          "min": {
            "items": {
              "type": "integer"
            },
            "type": "array"
          },
          "n": {
            "description": "Stores reporting that day.",
            "items": {
              "type": "integer"
            },
            "type": "array"
          }
        },
        "type": "object"
      },
      "Day": {
        "description": "New Zealand calendar day.",
        "example": "2026-09-04",
        "format": "date",
        "type": "string"
      },
      "Deals": {
        "properties": {
          "kind": {
            "enum": [
              "special",
              "member",
              "multibuy"
            ],
            "type": "string"
          },
          "listings": {
            "items": {
              "$ref": "#/components/schemas/ListingCard"
            },
            "type": "array"
          },
          "total": {
            "description": "Size of the whole feed.",
            "type": "integer"
          }
        },
        "required": [
          "kind",
          "total",
          "listings"
        ],
        "type": "object"
      },
      "Error": {
        "properties": {
          "error": {
            "properties": {
              "bulk_data": {
                "description": "On `429`, the export manifest to use instead of crawling.",
                "format": "uri",
                "type": "string"
              },
              "docs": {
                "description": "Where the documentation lives.",
                "format": "uri",
                "type": "string"
              },
              "message": {
                "example": "Not Found",
                "type": "string"
              },
              "status": {
                "example": 404,
                "type": "integer"
              }
            },
            "required": [
              "status",
              "message"
            ],
            "type": "object"
          }
        },
        "required": [
          "error"
        ],
        "type": "object"
      },
      "ExportManifest": {
        "properties": {
          "day": {
            "$ref": "#/components/schemas/Day"
          },
          "files": {
            "items": {
              "properties": {
                "bytes": {
                  "type": "integer"
                },
                "name": {
                  "example": "price_spans.parquet",
                  "type": "string"
                },
                "rows": {
                  "type": "integer"
                },
                "sha256": {
                  "type": "string"
                }
              },
              "required": [
                "name",
                "bytes",
                "rows",
                "sha256"
              ],
              "type": "object"
            },
            "type": "array"
          },
          "generated_at": {
            "format": "date-time",
            "type": "string"
          },
          "licence": {
            "const": "CC BY 4.0",
            "type": "string"
          }
        },
        "required": [
          "day",
          "licence",
          "generated_at",
          "files"
        ],
        "type": "object"
      },
      "GroupLevel": {
        "properties": {
          "code": {
            "type": "string"
          },
          "level": {
            "type": "number"
          },
          "n": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          }
        },
        "required": [
          "code",
          "name",
          "level",
          "n"
        ],
        "type": "object"
      },
      "GroupSeries": {
        "properties": {
          "anchored": {
            "items": {
              "type": [
                "number",
                "null"
              ]
            },
            "type": "array"
          },
          "code": {
            "type": "string"
          },
          "coverage": {
            "items": {
              "type": [
                "number",
                "null"
              ]
            },
            "type": "array"
          },
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "raw": {
            "items": {
              "type": [
                "number",
                "null"
              ]
            },
            "type": "array"
          },
          "weight": {
            "description": "Basket weight, percent.",
            "type": "number"
          }
        },
        "required": [
          "id",
          "code",
          "name",
          "weight",
          "anchored",
          "raw",
          "coverage"
        ],
        "type": "object"
      },
      "History": {
        "description": "Price spans as parallel arrays; index `i` of each array is one span.",
        "properties": {
          "from": {
            "description": "First day of each span.",
            "items": {
              "$ref": "#/components/schemas/Day"
            },
            "type": "array"
          },
          "kind": {
            "items": {
              "oneOf": [
                {
                  "$ref": "#/components/schemas/PromoKind"
                },
                {
                  "type": "null"
                }
              ]
            },
            "type": "array"
          },
          "multibuy_price": {
            "items": {
              "type": [
                "integer",
                "null"
              ]
            },
            "type": "array"
          },
          "multibuy_qty": {
            "items": {
              "type": [
                "integer",
                "null"
              ]
            },
            "type": "array"
          },
          "price": {
            "description": "Shelf price, cents.",
            "items": {
              "type": "integer"
            },
            "type": "array"
          },
          "promo": {
            "description": "Promotional price, cents.",
            "items": {
              "type": [
                "integer",
                "null"
              ]
            },
            "type": "array"
          },
          "to": {
            "description": "Last day each span was observed.",
            "items": {
              "$ref": "#/components/schemas/Day"
            },
            "type": "array"
          }
        },
        "required": [
          "from",
          "to",
          "price",
          "promo",
          "kind",
          "multibuy_qty",
          "multibuy_price"
        ],
        "type": "object"
      },
      "HistoryByRetailer": {
        "additionalProperties": {
          "$ref": "#/components/schemas/History"
        },
        "description": "Retailer slug → history.",
        "type": "object"
      },
      "ImagePath": {
        "description": "Path on this host to a 720 px AVIF; insert `_t` before `.avif` for the 200 px thumbnail.",
        "example": "/img/c2/c219a3fd48a0e1f14f682fa8df71ed0ddb161b55b5b794f0ce2b46ed4d58e7c2.avif",
        "type": "string"
      },
      "IndexGroups": {
        "properties": {
          "days": {
            "items": {
              "$ref": "#/components/schemas/Day"
            },
            "type": "array"
          },
          "groups": {
            "items": {
              "$ref": "#/components/schemas/GroupSeries"
            },
            "type": "array"
          }
        },
        "required": [
          "days",
          "groups"
        ],
        "type": "object"
      },
      "IndexItems": {
        "properties": {
          "days": {
            "items": {
              "$ref": "#/components/schemas/Day"
            },
            "type": "array"
          },
          "items": {
            "items": {
              "$ref": "#/components/schemas/ItemSeries"
            },
            "type": "array"
          }
        },
        "required": [
          "days",
          "items"
        ],
        "type": "object"
      },
      "IndexLatest": {
        "properties": {
          "as_of": {
            "$ref": "#/components/schemas/Day"
          },
          "coverage": {
            "properties": {
              "items": {
                "type": "integer"
              },
              "listings": {
                "type": "integer"
              },
              "weight": {
                "description": "Share of basket weight observed (0–1).",
                "type": "number"
              }
            },
            "required": [
              "weight",
              "listings",
              "items"
            ],
            "type": "object"
          },
          "groups": {
            "items": {
              "properties": {
                "code": {
                  "type": "string"
                },
                "coverage": {
                  "type": "number"
                },
                "name": {
                  "type": "string"
                },
                "value": {
                  "type": "number"
                }
              },
              "required": [
                "code",
                "name",
                "value",
                "coverage"
              ],
              "type": "object"
            },
            "type": "array"
          },
          "headline": {
            "properties": {
              "d1": {
                "description": "Change over one day, percent.",
                "type": "number"
              },
              "d30": {
                "description": "Change over 30 days, percent.",
                "type": "number"
              },
              "value": {
                "description": "Anchored index level (the official base is 1000).",
                "type": "number"
              },
              "yoy": {
                "description": "Change over a year, percent.",
                "type": "number"
              }
            },
            "required": [
              "value",
              "d1",
              "d30",
              "yoy"
            ],
            "type": "object"
          },
          "sparkline": {
            "properties": {
              "t0": {
                "description": "Day of the first value.",
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/Day"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "v": {
                "description": "One value per day from `t0`.",
                "items": {
                  "type": "number"
                },
                "type": "array"
              }
            },
            "required": [
              "t0",
              "v"
            ],
            "type": "object"
          }
        },
        "required": [
          "as_of",
          "headline",
          "coverage",
          "groups",
          "sparkline"
        ],
        "type": "object"
      },
      "IndexPoint": {
        "properties": {
          "anchored": {
            "description": "Anchored to the last official monthly value.",
            "type": [
              "number",
              "null"
            ]
          },
          "coverage": {
            "type": "number"
          },
          "day": {
            "$ref": "#/components/schemas/Day"
          },
          "group_id": {
            "type": "integer"
          },
          "raw": {
            "description": "Chained series, base 1000 on the first day.",
            "type": "number"
          }
        },
        "required": [
          "day",
          "group_id",
          "raw",
          "coverage"
        ],
        "type": "object"
      },
      "ItemSeries": {
        "properties": {
          "group": {
            "description": "Basket group code.",
            "type": "string"
          },
          "id": {
            "type": "integer"
          },
          "n": {
            "description": "Listings feeding the item that day; 0 = carried forward.",
            "items": {
              "type": [
                "integer",
                "null"
              ]
            },
            "type": "array"
          },
          "name": {
            "type": "string"
          },
          "per_unit": {
            "description": "Compared per kilogram / litre rather than per pack.",
            "type": "boolean"
          },
          "relative": {
            "description": "Aligned with `days`; 1.0 = the item's level on its base day.",
            "items": {
              "type": [
                "number",
                "null"
              ]
            },
            "type": "array"
          },
          "spec": {
            "description": "The Stats NZ representative size.",
            "type": [
              "string",
              "null"
            ]
          },
          "unit": {
            "enum": [
              "g",
              "ml",
              "each",
              "m",
              null
            ],
            "type": [
              "string",
              "null"
            ]
          },
          "weight": {
            "type": "number"
          }
        },
        "required": [
          "id",
          "name",
          "group",
          "weight",
          "per_unit",
          "relative",
          "n"
        ],
        "type": "object"
      },
      "LevelSeries": {
        "properties": {
          "items": {
            "items": {
              "type": [
                "integer",
                "null"
              ]
            },
            "type": "array"
          },
          "level": {
            "description": "1.0 = the cross-chain median that day.",
            "items": {
              "type": [
                "number",
                "null"
              ]
            },
            "type": "array"
          },
          "retailer": {
            "description": "Retailer slug.",
            "type": "string"
          }
        },
        "required": [
          "retailer",
          "level",
          "items"
        ],
        "type": "object"
      },
      "ListingCard": {
        "description": "A listing as shown on any card, with an honest price.",
        "properties": {
          "available": {
            "description": "Still on the retailer's site at the last collection.",
            "example": true,
            "type": "boolean"
          },
          "brand": {
            "example": "L&P",
            "type": [
              "string",
              "null"
            ]
          },
          "id": {
            "description": "Listing id.",
            "example": 34324,
            "type": "integer"
          },
          "image": {
            "example": "/img/c2/c219a3fd48a0e1f14f682fa8df71ed0ddb161b55b5b794f0ce2b46ed4d58e7c2.avif",
            "oneOf": [
              {
                "$ref": "#/components/schemas/ImagePath"
              },
              {
                "type": "null"
              }
            ]
          },
          "multibuy_price": {
            "description": "Total price for `multibuy_qty` units, cents.",
            "example": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "multibuy_qty": {
            "description": "Units you must take for `multibuy_price`.",
            "example": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "name": {
            "example": "Soft Drink",
            "type": "string"
          },
          "price": {
            "description": "Shelf price, cents.",
            "example": 499,
            "type": [
              "integer",
              "null"
            ]
          },
          "product_id": {
            "description": "Cross-chain product, once linked.",
            "example": 285,
            "type": [
              "integer",
              "null"
            ]
          },
          "promo_kind": {
            "example": 2,
            "oneOf": [
              {
                "$ref": "#/components/schemas/PromoKind"
              },
              {
                "type": "null"
              }
            ]
          },
          "promo_price": {
            "description": "Promotional single-unit price, cents.",
            "example": 149,
            "type": [
              "integer",
              "null"
            ]
          },
          "retailer": {
            "description": "Retailer slug.",
            "example": "newworld",
            "type": "string"
          },
          "size_text": {
            "description": "Size as printed, e.g. `1.5l`.",
            "example": "1.5l",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "id",
          "retailer",
          "name",
          "available"
        ],
        "type": "object"
      },
      "ListingDetail": {
        "properties": {
          "available": {
            "type": "boolean"
          },
          "brand": {
            "type": [
              "string",
              "null"
            ]
          },
          "category_path": {
            "description": "The retailer's own category breadcrumb.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "current": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/CurrentPrice"
              },
              {
                "type": "null"
              }
            ]
          },
          "external_id": {
            "description": "The retailer's own product id.",
            "type": "string"
          },
          "first_seen": {
            "$ref": "#/components/schemas/Day"
          },
          "gtin": {
            "description": "Barcode as an integer.",
            "type": [
              "integer",
              "null"
            ]
          },
          "id": {
            "type": "integer"
          },
          "image": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/ImagePath"
              },
              {
                "type": "null"
              }
            ]
          },
          "last_seen": {
            "$ref": "#/components/schemas/Day"
          },
          "name": {
            "type": "string"
          },
          "product_id": {
            "type": [
              "integer",
              "null"
            ]
          },
          "qty": {
            "description": "Quantity in `unit`.",
            "type": [
              "number",
              "null"
            ]
          },
          "retailer": {
            "description": "Retailer display name.",
            "type": "string"
          },
          "retailer_slug": {
            "type": "string"
          },
          "retailer_url": {
            "description": "The product on the retailer's own site, when it has one.",
            "format": "uri",
            "type": [
              "string",
              "null"
            ]
          },
          "size_text": {
            "type": [
              "string",
              "null"
            ]
          },
          "stats": {
            "$ref": "#/components/schemas/DailyStats"
          },
          "stores": {
            "description": "Every store pricing it today, cheapest first.",
            "items": {
              "$ref": "#/components/schemas/ListingStorePrice"
            },
            "type": "array"
          },
          "stores_total": {
            "description": "Stores the chain has in the panel.",
            "type": "integer"
          },
          "unit": {
            "enum": [
              "g",
              "ml",
              "each",
              "m",
              null
            ],
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "id",
          "retailer",
          "retailer_slug",
          "external_id",
          "name",
          "category_path",
          "first_seen",
          "last_seen",
          "available",
          "stores_total",
          "stores",
          "stats"
        ],
        "type": "object"
      },
      "ListingStorePrice": {
        "description": "Today's price of a listing at one store.",
        "properties": {
          "day": {
            "$ref": "#/components/schemas/Day"
          },
          "lat": {
            "type": [
              "number",
              "null"
            ]
          },
          "lng": {
            "type": [
              "number",
              "null"
            ]
          },
          "multibuy_price": {
            "type": [
              "integer",
              "null"
            ]
          },
          "multibuy_qty": {
            "type": [
              "integer",
              "null"
            ]
          },
          "price": {
            "$ref": "#/components/schemas/Cents"
          },
          "promo_kind": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/PromoKind"
              },
              {
                "type": "null"
              }
            ]
          },
          "promo_price": {
            "type": [
              "integer",
              "null"
            ]
          },
          "region": {
            "description": "The retailer's own region code (`NI`/`SI` for Foodstuffs chains, a regional code such as `WKO` for Woolworths). For a consistent place, use the store's `area` and `town` from `list_stores`.",
            "type": [
              "string",
              "null"
            ]
          },
          "store": {
            "type": "string"
          },
          "store_id": {
            "type": "integer"
          }
        },
        "required": [
          "store_id",
          "store",
          "day",
          "price"
        ],
        "type": "object"
      },
      "Offer": {
        "description": "One chain's listing on a product page, with how it got there.",
        "properties": {
          "available": {
            "type": "boolean"
          },
          "brand": {
            "type": [
              "string",
              "null"
            ]
          },
          "category_path": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "confidence": {
            "maximum": 1,
            "minimum": 0,
            "type": "number"
          },
          "id": {
            "description": "Listing id.",
            "type": "integer"
          },
          "image": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/ImagePath"
              },
              {
                "type": "null"
              }
            ]
          },
          "last_seen": {
            "$ref": "#/components/schemas/Day"
          },
          "locked": {
            "description": "Pinned by a curator.",
            "type": "boolean"
          },
          "method": {
            "description": "How the listing was linked: `gtin`, `name`, `llm`, `manual`, …",
            "type": "string"
          },
          "multibuy_price": {
            "type": [
              "integer",
              "null"
            ]
          },
          "multibuy_qty": {
            "type": [
              "integer",
              "null"
            ]
          },
          "name": {
            "type": "string"
          },
          "origin": {
            "description": "Country-of-origin statement, when the retailer gives one.",
            "type": [
              "string",
              "null"
            ]
          },
          "pack_count": {
            "type": [
              "integer",
              "null"
            ]
          },
          "package_type": {
            "type": [
              "string",
              "null"
            ]
          },
          "price": {
            "description": "National shelf price today, cents.",
            "type": [
              "integer",
              "null"
            ]
          },
          "price_day": {
            "description": "Day of the latest national observation.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/Day"
              },
              {
                "type": "null"
              }
            ]
          },
          "promo_kind": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/PromoKind"
              },
              {
                "type": "null"
              }
            ]
          },
          "promo_price": {
            "type": [
              "integer",
              "null"
            ]
          },
          "qty": {
            "type": [
              "number",
              "null"
            ]
          },
          "retailer": {
            "description": "Retailer slug.",
            "type": "string"
          },
          "retailer_name": {
            "type": "string"
          },
          "retailer_url": {
            "format": "uri",
            "type": [
              "string",
              "null"
            ]
          },
          "size_text": {
            "type": [
              "string",
              "null"
            ]
          },
          "stores": {
            "description": "Stores with a price today (0 for a national-only chain).",
            "type": "integer"
          },
          "stores_total": {
            "type": "integer"
          },
          "unit": {
            "type": [
              "string",
              "null"
            ]
          },
          "weighable": {
            "description": "Sold by weight.",
            "type": "boolean"
          }
        },
        "required": [
          "id",
          "retailer",
          "retailer_name",
          "name",
          "weighable",
          "available",
          "last_seen",
          "method",
          "confidence",
          "locked",
          "stores",
          "stores_total",
          "category_path"
        ],
        "type": "object"
      },
      "ProductDetail": {
        "properties": {
          "aliases": {
            "description": "Other names it is searched by.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "alternatives": {
            "items": {
              "$ref": "#/components/schemas/Alternative"
            },
            "type": "array"
          },
          "attributes": {
            "items": {
              "$ref": "#/components/schemas/Attribute"
            },
            "type": "array"
          },
          "brand": {
            "type": [
              "string",
              "null"
            ]
          },
          "brand_id": {
            "type": [
              "integer",
              "null"
            ]
          },
          "brand_owner": {
            "description": "Retailer slug when the brand is a house brand.",
            "type": [
              "string",
              "null"
            ]
          },
          "brand_parent": {
            "description": "Parent brand of a sub-brand.",
            "type": [
              "string",
              "null"
            ]
          },
          "compare_unit": {
            "description": "The unit prices in this kind are compared per (`kg`, `l`, `each`).",
            "type": [
              "string",
              "null"
            ]
          },
          "generic_id": {
            "type": [
              "integer",
              "null"
            ]
          },
          "generic_kind": {
            "description": "Slug of the kind, e.g. `soft-drink`.",
            "type": [
              "string",
              "null"
            ]
          },
          "generic_kind_label": {
            "type": [
              "string",
              "null"
            ]
          },
          "generic_label": {
            "type": [
              "string",
              "null"
            ]
          },
          "gtins": {
            "description": "Barcodes seen for it.",
            "items": {
              "type": "integer"
            },
            "type": "array"
          },
          "id": {
            "type": "integer"
          },
          "image": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/ImagePath"
              },
              {
                "type": "null"
              }
            ]
          },
          "name": {
            "type": "string"
          },
          "offers": {
            "description": "One per linked listing; available and cheapest first.",
            "items": {
              "$ref": "#/components/schemas/Offer"
            },
            "type": "array"
          },
          "pack_count": {
            "type": [
              "integer",
              "null"
            ]
          },
          "qty": {
            "type": [
              "number",
              "null"
            ]
          },
          "retailers": {
            "description": "Chains that sell it.",
            "type": "integer"
          },
          "shelf_label": {
            "example": "Craft Beer",
            "type": [
              "string",
              "null"
            ]
          },
          "shelf_slug": {
            "description": "The most specific shelf its kind is on, for `browse_shelf`.",
            "example": "craft-beer",
            "type": [
              "string",
              "null"
            ]
          },
          "size_text": {
            "type": [
              "string",
              "null"
            ]
          },
          "unit": {
            "type": [
              "string",
              "null"
            ]
          },
          "weighable": {
            "type": "boolean"
          }
        },
        "required": [
          "id",
          "name",
          "weighable",
          "aliases",
          "retailers",
          "gtins",
          "attributes",
          "offers",
          "alternatives"
        ],
        "type": "object"
      },
      "ProductGroup": {
        "description": "One product with its offers at every chain that sells it, cheapest first.",
        "properties": {
          "brand": {
            "example": "L&P",
            "type": [
              "string",
              "null"
            ]
          },
          "generic_id": {
            "example": 14715,
            "type": [
              "integer",
              "null"
            ]
          },
          "generic_label": {
            "1.5 L": null,
            "description": "The generic the product belongs to, e.g. `Regular soft drink, 1.5 L`.",
            "example": "Regular soft drink",
            "type": [
              "string",
              "null"
            ]
          },
          "image": {
            "example": "/img/23/23924e586ad446cda2bbfefbac3e72c535db21b742b737ccddac13434558ce8a.avif",
            "oneOf": [
              {
                "$ref": "#/components/schemas/ImagePath"
              },
              {
                "type": "null"
              }
            ]
          },
          "name": {
            "example": "Soft Drink",
            "type": "string"
          },
          "offers": {
            "items": {
              "$ref": "#/components/schemas/ListingCard"
            },
            "type": "array"
          },
          "product_id": {
            "description": "`null` for a listing not yet linked to a product.",
            "example": 285,
            "type": [
              "integer",
              "null"
            ]
          },
          "size_text": {
            "example": "1.5l",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "product_id",
          "name",
          "offers"
        ],
        "type": "object"
      },
      "ProductStorePrice": {
        "properties": {
          "lat": {
            "type": [
              "number",
              "null"
            ]
          },
          "lng": {
            "type": [
              "number",
              "null"
            ]
          },
          "multibuy_price": {
            "type": [
              "integer",
              "null"
            ]
          },
          "multibuy_qty": {
            "type": [
              "integer",
              "null"
            ]
          },
          "price": {
            "$ref": "#/components/schemas/Cents"
          },
          "promo_kind": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/PromoKind"
              },
              {
                "type": "null"
              }
            ]
          },
          "promo_price": {
            "type": [
              "integer",
              "null"
            ]
          },
          "retailer": {
            "description": "Retailer slug.",
            "type": "string"
          },
          "store": {
            "type": "string"
          },
          "store_id": {
            "type": "integer"
          }
        },
        "required": [
          "store_id",
          "store",
          "retailer",
          "price"
        ],
        "type": "object"
      },
      "PromoKind": {
        "description": "Who the promotion is for: `1` special (everyone), `2` member/club price, `3` multibuy, `4` clearance.",
        "enum": [
          1,
          2,
          3,
          4
        ],
        "type": "integer"
      },
      "Retailer": {
        "properties": {
          "last_run": {
            "description": "When the catalogue was last collected.",
            "format": "date-time",
            "type": [
              "string",
              "null"
            ]
          },
          "listings": {
            "description": "Catalogue size.",
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "slug": {
            "example": "paknsave",
            "type": "string"
          },
          "stores": {
            "description": "Active stores in the price panel.",
            "type": "integer"
          }
        },
        "required": [
          "slug",
          "name",
          "listings",
          "stores"
        ],
        "type": "object"
      },
      "RetailerBrowse": {
        "properties": {
          "categories": {
            "description": "Child categories, biggest first.",
            "items": {
              "$ref": "#/components/schemas/CategoryNode"
            },
            "type": "array"
          },
          "limit": {
            "type": "integer"
          },
          "listings": {
            "items": {
              "$ref": "#/components/schemas/ListingCard"
            },
            "type": "array"
          },
          "offset": {
            "type": "integer"
          },
          "path": {
            "description": "The category path you asked for.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "q": {
            "description": "The filter, echoed.",
            "type": "string"
          },
          "retailer": {
            "$ref": "#/components/schemas/Retailer"
          },
          "total": {
            "description": "Listings at and below this node (matching `q`).",
            "type": "integer"
          }
        },
        "required": [
          "retailer",
          "path",
          "categories",
          "total",
          "limit",
          "offset",
          "listings",
          "q"
        ],
        "type": "object"
      },
      "RetailerLevels": {
        "properties": {
          "days": {
            "items": {
              "$ref": "#/components/schemas/Day"
            },
            "type": "array"
          },
          "retailers": {
            "items": {
              "$ref": "#/components/schemas/LevelSeries"
            },
            "type": "array"
          }
        },
        "required": [
          "days",
          "retailers"
        ],
        "type": "object"
      },
      "ShelfPage": {
        "properties": {
          "children": {
            "items": {
              "$ref": "#/components/schemas/ShelfSummary"
            },
            "type": "array"
          },
          "crumbs": {
            "description": "Root first, the shelf itself last.",
            "items": {
              "$ref": "#/components/schemas/ShelfSummary"
            },
            "type": "array"
          },
          "kinds": {
            "description": "Kinds represented under the current filters (the kind filter excepted), most products first.",
            "items": {
              "properties": {
                "label": {
                  "example": "IPA",
                  "type": "string"
                },
                "products": {
                  "example": 358,
                  "type": "integer"
                },
                "slug": {
                  "example": "ipa",
                  "type": "string"
                }
              },
              "required": [
                "slug",
                "label",
                "products"
              ],
              "type": "object"
            },
            "type": "array"
          },
          "params": {
            "description": "The request as applied, so a client can draw its controls from the answer.",
            "properties": {
              "deals": {
                "type": "boolean"
              },
              "kind": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "limit": {
                "type": "integer"
              },
              "offset": {
                "type": "integer"
              },
              "q": {
                "type": "string"
              },
              "retailer": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "sort": {
                "enum": [
                  "popular",
                  "per_unit",
                  "per_unit_desc",
                  "price",
                  "price_desc",
                  "deal",
                  "relevance"
                ],
                "type": "string"
              }
            },
            "required": [
              "sort",
              "retailer",
              "deals",
              "kind",
              "q",
              "limit",
              "offset"
            ],
            "type": "object"
          },
          "products": {
            "items": {
              "$ref": "#/components/schemas/ShelfProduct"
            },
            "type": "array"
          },
          "retailers": {
            "description": "Chains stocking the current set (the chain filter excepted).",
            "items": {
              "properties": {
                "products": {
                  "example": 1772,
                  "type": "integer"
                },
                "slug": {
                  "example": "newworld",
                  "type": "string"
                }
              },
              "required": [
                "slug",
                "products"
              ],
              "type": "object"
            },
            "type": "array"
          },
          "shelf": {
            "$ref": "#/components/schemas/ShelfSummary"
          },
          "total": {
            "description": "Products in the set under the current filters.",
            "example": 1862,
            "type": "integer"
          },
          "unit": {
            "description": "The unit family the per-unit sort compares on.",
            "enum": [
              "ml",
              "g",
              "each",
              null
            ],
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "shelf",
          "crumbs",
          "children",
          "unit",
          "kinds",
          "retailers",
          "total",
          "params",
          "products"
        ],
        "type": "object"
      },
      "ShelfProduct": {
        "allOf": [
          {
            "$ref": "#/components/schemas/ProductGroup"
          },
          {
            "properties": {
              "best": {
                "description": "Cheapest admitted current price in cents, counting specials, club prices and multibuy rates.",
                "example": 1399,
                "type": [
                  "integer",
                  "null"
                ]
              },
              "kind": {
                "description": "Generic kind slug.",
                "example": "craft-beer",
                "type": [
                  "string",
                  "null"
                ]
              },
              "kind_label": {
                "example": "Craft Beer",
                "type": [
                  "string",
                  "null"
                ]
              },
              "off": {
                "description": "Biggest discount off a shelf price among admitted offers, 0–1.",
                "example": 0.2,
                "type": [
                  "number",
                  "null"
                ]
              },
              "per_unit": {
                "description": "`best` per 1000 ml, 1000 g or one item, in cents; null when not measured in the shelf's unit.",
                "example": 707,
                "type": [
                  "integer",
                  "null"
                ]
              },
              "qty": {
                "description": "Total quantity in `unit` (a 6 × 330 ml pack is 1980).",
                "example": 1980,
                "type": [
                  "number",
                  "null"
                ]
              },
              "unit": {
                "enum": [
                  "g",
                  "ml",
                  "each",
                  "m",
                  null
                ],
                "example": "ml",
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "qty",
              "unit",
              "best",
              "per_unit",
              "off",
              "kind",
              "kind_label"
            ],
            "type": "object"
          }
        ]
      },
      "ShelfSummary": {
        "description": "A shelf as the directory, a breadcrumb or a child list shows it.",
        "properties": {
          "compare_unit": {
            "description": "The unit its per-unit sort compares on: `kg`, `l`, `100g`, `100ml`, `each` or `100sheet`.",
            "example": "l",
            "type": [
              "string",
              "null"
            ]
          },
          "depth": {
            "description": "1 = department, 2 = aisle, 3 = a curated sub-shelf.",
            "example": 2,
            "type": "integer"
          },
          "label": {
            "example": "Beer",
            "type": "string"
          },
          "parent": {
            "description": "Parent shelf's slug; null at a department.",
            "example": "beer-wine-cider",
            "type": [
              "string",
              "null"
            ]
          },
          "products": {
            "description": "Products with a current offer, at the last sync.",
            "example": 1862,
            "type": "integer"
          },
          "slug": {
            "example": "beer",
            "type": "string"
          }
        },
        "required": [
          "slug",
          "label",
          "depth",
          "parent",
          "products",
          "compare_unit"
        ],
        "type": "object"
      },
      "Spread": {
        "properties": {
          "day": {
            "items": {
              "$ref": "#/components/schemas/Day"
            },
            "type": "array"
          },
          "max": {
            "items": {
              "type": "integer"
            },
            "type": "array"
          },
          "med": {
            "items": {
              "type": "integer"
            },
            "type": "array"
          },
          "min": {
            "items": {
              "type": "integer"
            },
            "type": "array"
          },
          "n": {
            "description": "Stores reporting.",
            "items": {
              "type": "integer"
            },
            "type": "array"
          }
        },
        "required": [
          "day",
          "min",
          "med",
          "max",
          "n"
        ],
        "type": "object"
      },
      "SpreadByRetailer": {
        "additionalProperties": {
          "$ref": "#/components/schemas/Spread"
        },
        "description": "Retailer slug → daily spread.",
        "type": "object"
      },
      "Store": {
        "properties": {
          "area": {
            "description": "Region (Stats NZ Regional Council).",
            "type": "string"
          },
          "club": {
            "description": "Club-price promotions this week.",
            "type": "integer"
          },
          "day": {
            "description": "Latest day with prices.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/Day"
              },
              {
                "type": "null"
              }
            ]
          },
          "id": {
            "type": "integer"
          },
          "lat": {
            "type": "number"
          },
          "level": {
            "description": "Basket price level against the chain's national median; `1.03` = 3 % dearer.",
            "type": [
              "number",
              "null"
            ]
          },
          "lng": {
            "type": "number"
          },
          "multibuy": {
            "description": "Multibuys this week.",
            "type": "integer"
          },
          "n_items": {
            "description": "Basket items the level is computed from.",
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "region": {
            "description": "The retailer's own region code (`NI`/`SI` for Foodstuffs chains, a regional code such as `WKO` for Woolworths). For a consistent place, use the store's `area` and `town` from `list_stores`.",
            "type": [
              "string",
              "null"
            ]
          },
          "retailer": {
            "description": "Retailer slug.",
            "type": "string"
          },
          "sale": {
            "description": "Specials this week.",
            "type": "integer"
          },
          "town": {
            "description": "Nearest urban area, or `Rural <region>`.",
            "type": "string"
          },
          "urban": {
            "enum": [
              "major",
              "large",
              "medium",
              "small",
              "rural",
              null
            ],
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "id",
          "retailer",
          "name",
          "lat",
          "lng",
          "n_items",
          "club",
          "sale",
          "multibuy",
          "town",
          "area"
        ],
        "type": "object"
      },
      "StoreDetail": {
        "properties": {
          "cheaper": {
            "items": {
              "$ref": "#/components/schemas/StoreListing"
            },
            "type": "array"
          },
          "deals": {
            "items": {
              "$ref": "#/components/schemas/ListingCard"
            },
            "type": "array"
          },
          "dearer": {
            "items": {
              "$ref": "#/components/schemas/StoreListing"
            },
            "type": "array"
          },
          "groups": {
            "items": {
              "$ref": "#/components/schemas/GroupLevel"
            },
            "type": "array"
          },
          "listings_priced": {
            "type": "integer"
          },
          "spread": {
            "items": {
              "$ref": "#/components/schemas/Bin"
            },
            "type": "array"
          },
          "store": {
            "$ref": "#/components/schemas/Store"
          }
        },
        "required": [
          "store",
          "groups",
          "dearer",
          "cheaper",
          "spread",
          "listings_priced",
          "deals"
        ],
        "type": "object"
      },
      "StoreLevels": {
        "properties": {
          "days": {
            "items": {
              "$ref": "#/components/schemas/Day"
            },
            "type": "array"
          },
          "items": {
            "description": "Basket items priced at the store that day.",
            "items": {
              "type": [
                "integer",
                "null"
              ]
            },
            "type": "array"
          },
          "level": {
            "description": "Aligned with `days`; `1.0` is the chain's national median that day.",
            "items": {
              "type": [
                "number",
                "null"
              ]
            },
            "type": "array"
          }
        },
        "required": [
          "days",
          "level",
          "items"
        ],
        "type": "object"
      },
      "StoreListing": {
        "properties": {
          "brand": {
            "type": [
              "string",
              "null"
            ]
          },
          "id": {
            "description": "Listing id.",
            "type": "integer"
          },
          "image": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/ImagePath"
              },
              {
                "type": "null"
              }
            ]
          },
          "median": {
            "description": "The chain's national median, cents.",
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "price": {
            "$ref": "#/components/schemas/Cents"
          },
          "promo_kind": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/PromoKind"
              },
              {
                "type": "null"
              }
            ]
          },
          "promo_price": {
            "type": [
              "integer",
              "null"
            ]
          },
          "ratio": {
            "description": "`price / median`.",
            "type": "number"
          },
          "size_text": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "id",
          "name",
          "price",
          "median",
          "ratio"
        ],
        "type": "object"
      },
      "StoreListings": {
        "properties": {
          "limit": {
            "type": "integer"
          },
          "listings": {
            "items": {
              "$ref": "#/components/schemas/StoreListing"
            },
            "type": "array"
          },
          "offset": {
            "type": "integer"
          },
          "total": {
            "type": "integer"
          }
        },
        "required": [
          "total",
          "limit",
          "offset",
          "listings"
        ],
        "type": "object"
      },
      "Suggestion": {
        "properties": {
          "detail": {
            "type": [
              "string",
              "null"
            ]
          },
          "href": {
            "description": "Site path to open.",
            "type": "string"
          },
          "id": {
            "type": [
              "integer",
              "null"
            ]
          },
          "image": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/ImagePath"
              },
              {
                "type": "null"
              }
            ]
          },
          "kind": {
            "enum": [
              "category",
              "brand",
              "product",
              "listing"
            ],
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "price": {
            "type": [
              "integer",
              "null"
            ]
          },
          "promo_kind": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/PromoKind"
              },
              {
                "type": "null"
              }
            ]
          },
          "promo_price": {
            "type": [
              "integer",
              "null"
            ]
          }
        },
        "required": [
          "kind",
          "label",
          "href"
        ],
        "type": "object"
      }
    }
  },
  "info": {
    "contact": {
      "email": "hello@supertracker.nz",
      "name": "Super Tracker",
      "url": "https://supertracker.nz/contact"
    },
    "description": "Super Tracker records the online shelf price of every product at every store of the\nbig New Zealand supermarket chains (Pak'nSave, New World, Four Square, Woolworths,\nFreshChoice, SuperValue), every day, and keeps the history as price *spans*: one row\nper price, from the day it appeared to the day it changed. On top of that sit\ncross-chain product matching (the same item at each chain), store-level price\ncomparisons, and a daily food-price index weighted with the Stats NZ Food Price Index\nbasket. This API is how the website reads all of it, and it is open to everyone.\n\n## Quick start\n\n```\ncurl \"https://api.supertracker.nz/api/v1/search?q=anchor+blue+milk+2l\"\ncurl \"https://api.supertracker.nz/api/v1/products/285\"\ncurl \"https://api.supertracker.nz/api/v1/products/285/history\"\ncurl \"https://api.supertracker.nz/api/v1/stores?town=Tokoroa\"\n```\n\nTypical flow: **search** (`/api/v1/search`) to find a product id, then the **product**\n(`/api/v1/products/{id}`) for today's price at every chain, its **history** for how\nthat moved, and its **stores** for the price at every shop in the country. A single\nchain's own listing has the same trio under `/api/v1/listings/{id}`.\n\n## Conventions\n\n- **Money is integer cents** (NZD). `price` is the shelf price. When `promo_price`\n  is set, `promo_kind` says who gets it: `1` special (everyone), `2` member/club\n  price (loyalty card), `3` multibuy (`multibuy_qty` for `multibuy_price` in total),\n  `4` clearance.\n- **Days** are New Zealand calendar dates, `YYYY-MM-DD`. Timestamps are UTC, RFC 3339.\n- **Ids are integers and stable.** A *listing* is one chain's product (its own name,\n  size and barcode). A *product* is the same item linked across chains by barcode\n  and name matching; a listing carries `product_id` when it has been placed.\n- **Series are columnar**: parallel arrays (`from[]`, `to[]`, `price[]`, …) rather\n  than arrays of objects. Index `i` of every array belongs to the same span.\n- **National prices** are the median across the chain's stores that report the\n  item; store-level prices are exact.\n- `image` is a path on this host to a 720 px AVIF (background removed). Put `_t`\n  before `.avif` for the 200 px thumbnail.\n- Responses carry `Cache-Control: public, max-age=300`; the heavier derived views\n  (map, comparisons, deals) are recomputed at most every ten minutes.\n- Errors are JSON: `{ \"error\": { \"status\": 404, \"message\": \"…\" } }`.\n- CORS is open to every origin. No cookies, no keys.\n\n## Fair use and rate limits\n\nThere are no API keys. Each IP address may make **300 requests per minute** and\n**10,000 per day** across `/api/v1/*` and `/mcp`. Every response carries\n`RateLimit-Limit`, `RateLimit-Remaining`, `RateLimit-Reset` and `RateLimit-Policy`\nheaders; over the limit you get `429` with `Retry-After`.\n\n**This API is for looking things up, not for copying the dataset.** If you want\neverything, it is already packaged for you: every listing, store, price span, index\npoint and basket item is written nightly as Parquet and CSV under\n[`/data/latest/`](/data/latest/manifest.json), the exact same tables this API reads,\nunder CC BY 4.0. Crawling the listing, product or history endpoints one id at a time\nwill run into the daily limit long before it finishes; the exports never will.\n\n## For agents\n\n- **MCP**: `POST /mcp` is a stateless Streamable HTTP MCP server (protocol revision\n  `2026-07-28`, with the legacy `initialize` handshake still accepted). Its tools are\n  these endpoints, one to one, same names as the `operationId`s below, same JSON back.\n- **llms.txt**: [`/llms.txt`](/llms.txt) is the index; [`/llms-full.txt`](/llms-full.txt)\n  is this whole reference as Markdown; [`/openapi.json`](/openapi.json) is this document.\n- **Guides**: connecting an agent step by step is at\n  [supertracker.nz/agents](https://supertracker.nz/agents); the developer guide is at\n  [supertracker.nz/developers](https://supertracker.nz/developers); the website's own\n  pages are indexed at [supertracker.nz/llms.txt](https://supertracker.nz/llms.txt).\n\n## Licence and attribution\n\nData is **CC BY 4.0**: attribute *Super Tracker (supertracker.nz)* and acknowledge the\nsource retailers. Prices are as advertised online by each supermarket; in-store prices\nmay differ. The food-price index is a nowcast, not an official statistic: the basket\nand weights are Stats NZ's [Food Price Index](https://datainfoplus.stats.govt.nz/item/nz.govt.stats/749d8c27-1bed-45fb-b941-a1905aee632f),\nreviewed in the [2024 CPI review](https://www.stats.govt.nz/methods/consumers-price-index-review-2024/)\nand [published monthly](https://datainfoplus.stats.govt.nz/Item/nz.govt.stats/9e9f65b8-533f-4e96-8d6e-030d37de1a65).\n\n## Stability\n\n`/api/v1` is stable: fields are added, never removed or renamed, without a new version\nprefix. Endpoints that are not in this document (`/api/v1/pile`, `/api/v1/costs`,\n`/api/v1/search/status`, `/api/v1/auth/*`, `/api/v1/admin/*`) exist for the website\nand may change without notice.\n",
    "license": {
      "name": "CC BY 4.0 (data)",
      "url": "https://creativecommons.org/licenses/by/4.0/"
    },
    "summary": "Free, keyless, read-only JSON over every New Zealand supermarket price, daily.",
    "title": "Super Tracker API",
    "version": "1"
  },
  "openapi": "3.1.0",
  "paths": {
    "/api/v1/areas": {
      "get": {
        "description": "The store levels from `list_stores` rolled up three ways: by region, by Stats NZ\nurban class (main centres down to rural), and by town. Because every store is\nmeasured against its own chain, an area's level is the premium of the place, with\nthe chain mix taken out: \"does Gisborne pay more than Hamilton for the same shops\".\n",
        "operationId": "list_areas",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Areas"
                }
              }
            },
            "description": "Three roll-ups."
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Price levels by region, town size and town",
        "tags": [
          "Stores"
        ]
      }
    },
    "/api/v1/basket": {
      "get": {
        "description": "Every item in the Food Price Index basket the index is computed from: its Stats\nNZ code and name, group, weights, the size specification, the keywords the matcher\nuses, how many listings are currently matched to it, and its latest relative price\nand staleness. With `include=matches` each item also carries `match_list`, the\nmatched listings themselves (several megabytes; the `basket` export is the better\nway to get that).\n",
        "operationId": "get_basket",
        "parameters": [
          {
            "description": "Set to `matches` to embed every matched listing per item.",
            "in": "query",
            "name": "include",
            "schema": {
              "enum": [
                "matches"
              ],
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/BasketItem"
                  },
                  "type": "array"
                }
              }
            },
            "description": "Items, by group then name."
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "The Stats NZ basket items and their weights",
        "tags": [
          "Basket"
        ]
      }
    },
    "/api/v1/basket/{id}": {
      "get": {
        "description": "The item (name, Stats NZ spec, group, weight, whether it compares per kilogram or\nper pack) and every listing currently matched to it, with the latest shelf and\npromotion price, the price on the item's comparison basis (`basis_price`, cents per\nkg / L for a per-unit item with a weight or volume pack, else the pack price) and\nthe last day it was priced. A listing priced more than 14 days ago is shown but\ndoes not count. This is what opens when a cell of the comparison table is clicked.\n",
        "operationId": "get_basket_item",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BasketItemDetail"
                }
              }
            },
            "description": "The item and its listings, by retailer then basis price."
          },
          "404": {
            "description": "No such basket item."
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "One basket item and the products it is priced from",
        "tags": [
          "Basket"
        ]
      }
    },
    "/api/v1/deals": {
      "get": {
        "description": "Current promotions, biggest saving first, split by who gets them: `special`\n(everyone), `member` (needs the chain's loyalty card), or `multibuy` (only if you\ntake the set). Optionally restricted to one chain and/or filtered by text.\n`total` is the size of the whole feed, `listings` the page you asked for.\n",
        "operationId": "list_deals",
        "parameters": [
          {
            "in": "query",
            "name": "kind",
            "schema": {
              "default": "special",
              "enum": [
                "special",
                "member",
                "multibuy"
              ],
              "type": "string"
            }
          },
          {
            "description": "Retailer slug.",
            "in": "query",
            "name": "retailer",
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/q"
          },
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "default": 24,
              "maximum": 96,
              "minimum": 1,
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Deals"
                }
              }
            },
            "description": "The feed."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "What is cheaper than shelf right now",
        "tags": [
          "Deals"
        ]
      }
    },
    "/api/v1/gtin/{gtin}": {
      "get": {
        "description": "Exact lookup of a GTIN/EAN/UPC as printed on the pack (up to 14 digits; leading\nzeros are fine, non-digits are ignored). Returns the product sold under that\nbarcode with its offers at every chain, or `404` when the barcode is unknown.\nThis is what the site's camera scanner calls.\n",
        "operationId": "lookup_gtin",
        "parameters": [
          {
            "description": "The barcode digits, e.g. `9415087020031`.",
            "in": "path",
            "name": "gtin",
            "required": true,
            "schema": {
              "pattern": "^[0-9 -]{1,20}$",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProductGroup"
                }
              }
            },
            "description": "The product behind the barcode."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Look up a barcode",
        "tags": [
          "Search"
        ]
      }
    },
    "/api/v1/index/daily": {
      "get": {
        "description": "One row per day for one group over a date range (default the last year): the raw\nchained series, the series anchored to the last official Stats NZ monthly print,\nand the share of basket weight observed that day. Group `0` is all food; the other\nids are listed by `get_index_groups`.\n",
        "operationId": "get_index_daily",
        "parameters": [
          {
            "description": "First day, inclusive. Default one year before today.",
            "in": "query",
            "name": "from",
            "schema": {
              "format": "date",
              "type": "string"
            }
          },
          {
            "description": "Last day, inclusive. Default today.",
            "in": "query",
            "name": "to",
            "schema": {
              "format": "date",
              "type": "string"
            }
          },
          {
            "description": "Basket group id (`0` = all food).",
            "in": "query",
            "name": "group",
            "schema": {
              "default": 0,
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/IndexPoint"
                  },
                  "type": "array"
                }
              }
            },
            "description": "Daily points, oldest first."
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Daily index series for one group",
        "tags": [
          "Food price index"
        ]
      }
    },
    "/api/v1/index/groups": {
      "get": {
        "description": "`days[]` and, per group (all food first, then the Stats NZ groups such as fruit and\nvegetables, meat, grocery food), aligned `anchored[]`, `raw[]` and `coverage[]`\narrays with `null` where a group has no value that day. Also the place to learn\neach group's `id`, `code`, `name` and basket weight.\n",
        "operationId": "get_index_groups",
        "parameters": [
          {
            "description": "How many days back from today (7–3650).",
            "in": "query",
            "name": "days",
            "schema": {
              "default": 365,
              "maximum": 3650,
              "minimum": 7,
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IndexGroups"
                }
              }
            },
            "description": "The series."
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Index series for the headline and every group, columnar",
        "tags": [
          "Food price index"
        ]
      }
    },
    "/api/v1/index/items": {
      "get": {
        "description": "`days[]` and, per observable basket item, an aligned `relative[]` (1.0 = the item's\nlevel on its base day, `null` where the day was not computed) and `n[]`, the\nnumber of listings feeding it that day (0 = carried forward). Each day is the\nnewest vintage, as in every other index answer. This is the item-by-item trend\ntable on the inflation page.\n",
        "operationId": "get_index_items",
        "parameters": [
          {
            "description": "How many days back from today (7–3650).",
            "in": "query",
            "name": "days",
            "schema": {
              "default": 365,
              "maximum": 3650,
              "minimum": 7,
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IndexItems"
                }
              }
            },
            "description": "The series."
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Every basket item's relative price by day, columnar",
        "tags": [
          "Food price index"
        ]
      }
    },
    "/api/v1/index/latest": {
      "get": {
        "description": "The landing-page number: the anchored headline index as of the latest computed\nday, its change over 1 day, 30 days and a year (percent), coverage (basket weight\nobserved, listings and items feeding it), each group's level, and a one-year\nsparkline. Served with an `ETag`; send `If-None-Match` to get `304`.\n",
        "operationId": "get_index_latest",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IndexLatest"
                }
              }
            },
            "description": "The headline."
          },
          "304": {
            "description": "Not modified since the `ETag` you sent."
          },
          "404": {
            "description": "No index has been computed yet."
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Today's food-price index headline",
        "tags": [
          "Food price index"
        ]
      }
    },
    "/api/v1/listings": {
      "get": {
        "description": "The same search as `search_products` but flat: one row per listing, not grouped\nby product. Useful when you want a specific chain's line rather than the item in\nthe abstract. Without `q` it returns recently seen listings.\n",
        "operationId": "search_listings",
        "parameters": [
          {
            "$ref": "#/components/parameters/q"
          },
          {
            "description": "Retailer slug: search that chain's listings only. Unknown slug is `400`.",
            "in": "query",
            "name": "retailer",
            "schema": {
              "example": "paknsave",
              "type": "string"
            }
          },
          {
            "description": "Maximum number of listings (1–100).",
            "in": "query",
            "name": "limit",
            "schema": {
              "default": 20,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/ListingCard"
                  },
                  "type": "array"
                }
              }
            },
            "description": "Listings, best match first."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Search listings (one row per chain product)",
        "tags": [
          "Search"
        ]
      }
    },
    "/api/v1/listings/{id}": {
      "get": {
        "description": "A single retailer's product: identity (name, brand, size, quantity and unit,\nbarcode, the chain's own category path, external id, link to the retailer's page),\nfirst and last seen, the `current` national span, `stores` (today's price at every\nstore carrying it, cheapest first, with coordinates), and `stats` (the daily\nmin/median/max across stores for the last year, columnar). `product_id` links to\nthe cross-chain product when the listing has been matched.\n",
        "operationId": "get_listing",
        "parameters": [
          {
            "$ref": "#/components/parameters/listingId"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListingDetail"
                }
              }
            },
            "description": "The listing."
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "One chain's listing with today's price at every store",
        "tags": [
          "Listings"
        ]
      }
    },
    "/api/v1/listings/{id}/history": {
      "get": {
        "description": "Every price span of the listing, oldest first, as parallel arrays. By default the\nnational series (the median across the chain's stores); pass `store` for one\nstore's exact history, including store-only promotions. An unknown listing or a\nstore that never priced it returns empty arrays, not `404`.\n",
        "operationId": "get_listing_history",
        "parameters": [
          {
            "$ref": "#/components/parameters/listingId"
          },
          {
            "description": "Store id from `list_stores` or the listing's `stores`. `0` (default) is the national series.",
            "in": "query",
            "name": "store",
            "schema": {
              "default": 0,
              "minimum": 0,
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/History"
                }
              }
            },
            "description": "Columnar spans."
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Price history of a listing, national or at one store",
        "tags": [
          "Listings"
        ]
      }
    },
    "/api/v1/products/{id}": {
      "get": {
        "description": "The cross-chain view of one item: brand, size, barcodes, the generic it belongs to\n(\"Regular soft drink, 1.5 L\") with its attributes, one `offer` per chain listing\n(today's national price, promotion, how many stores price it, how the link was\nmade and how confident it is, a link to the retailer's page), and up to 16\n`alternatives`: other products in the same generic, cheapest first, flagged when\nthey are a house brand. Product ids come from `search_products`, `lookup_gtin`, or\na listing's `product_id`.\n",
        "operationId": "get_product",
        "parameters": [
          {
            "$ref": "#/components/parameters/productId"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProductDetail"
                }
              }
            },
            "description": "The product."
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "A product with every chain's offer and its alternatives",
        "tags": [
          "Products"
        ]
      }
    },
    "/api/v1/products/{id}/history": {
      "get": {
        "description": "One columnar series per retailer slug: every national price span of every listing\nlinked to the product since it was first seen, oldest first. A span runs from\n`from[i]` to `to[i]` inclusive at `price[i]` cents, with `promo[i]`/`kind[i]` when\na promotion applied. The current price is the last span, whose `to` is the day of\nthe latest observation.\n",
        "operationId": "get_product_history",
        "parameters": [
          {
            "$ref": "#/components/parameters/productId"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HistoryByRetailer"
                }
              }
            },
            "description": "Retailer slug → history. Empty object when nothing is linked."
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "National price history of a product, per chain",
        "tags": [
          "Products"
        ]
      }
    },
    "/api/v1/products/{id}/spread": {
      "get": {
        "description": "For each retailer, the daily minimum, median and maximum store price of the\nproduct and how many stores reported, for the last 365 days: how much the same\nitem's price varies around the country, and whether that is widening.\n",
        "operationId": "get_product_spread",
        "parameters": [
          {
            "$ref": "#/components/parameters/productId"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SpreadByRetailer"
                }
              }
            },
            "description": "Retailer slug → columnar daily spread."
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Daily price spread across each chain's stores",
        "tags": [
          "Products"
        ]
      }
    },
    "/api/v1/products/{id}/stores": {
      "get": {
        "description": "Every store of every chain currently pricing the product, cheapest first, with\ncoordinates. A store appears once: if two of its chain's listings are linked to the\nproduct, the cheaper one wins. Around 80 KB for a product every chain sells; pair\nwith `list_stores` (filtered by town or area) when you only want one place.\n",
        "operationId": "get_product_stores",
        "parameters": [
          {
            "$ref": "#/components/parameters/productId"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/ProductStorePrice"
                  },
                  "type": "array"
                }
              }
            },
            "description": "Store prices, cheapest first. Empty when no store prices it."
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "The price of a product at every store in the country",
        "tags": [
          "Products"
        ]
      }
    },
    "/api/v1/retailers": {
      "get": {
        "description": "Every chain with a catalogue: slug (the key used everywhere else), display name,\nlisting and store counts, and when its catalogue was last collected. Slugs today:\n`paknsave`, `newworld`, `foursquare`, `woolworths`, `freshchoice`, `supervalue`.\n",
        "operationId": "list_retailers",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/Retailer"
                  },
                  "type": "array"
                }
              }
            },
            "description": "Chains, largest catalogue first."
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "The supermarket chains",
        "tags": [
          "Retailers"
        ]
      }
    },
    "/api/v1/retailers/compare": {
      "get": {
        "description": "Each basket item priced at each chain from national median prices (specials\ncount, club prices do not), plus weighted price levels: `headline` and per-group\n`levels` map a retailer slug to `[level, items]`, where `1.0` is the cross-chain\nmedian and `0.92` means 8 % cheaper than it. Answers \"which chain is cheapest, and\nby how much\". Recomputed every ten minutes.\n",
        "operationId": "compare_retailers",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Compare"
                }
              }
            },
            "description": "Levels and item prices per chain."
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "The Stats NZ basket priced at every chain",
        "tags": [
          "Retailers"
        ]
      }
    },
    "/api/v1/retailers/levels": {
      "get": {
        "description": "The headline number of `/retailers/compare` for every day the index has run: the\nStats NZ basket priced at each chain from national medians (club prices excluded),\nas a weighted geometric mean of each item's price over the cross-chain median.\n`days[]` and, per chain, aligned `level[]` (`1.0` is the median of all chains that\nday, `0.93` is 7 % cheaper) and `items[]` (basket items priced there that day),\n`null` where a chain has no value.\n",
        "operationId": "get_retailer_levels",
        "parameters": [
          {
            "description": "How many days back from today (7–3650).",
            "in": "query",
            "name": "days",
            "schema": {
              "default": 365,
              "maximum": 3650,
              "minimum": 7,
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RetailerLevels"
                }
              }
            },
            "description": "The series."
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Each chain's basket price level against the others, by day",
        "tags": [
          "Retailers"
        ]
      }
    },
    "/api/v1/retailers/{slug}/browse": {
      "get": {
        "description": "One point in a retailer's own taxonomy: the child categories underneath it with\nlisting counts, plus a page of the listings at and below it. The position is given\nas repeated `c` parameters, one per level (`?c=Pantry&c=Baking`), because category\nlabels contain `/`, `,` and `&`. With `q`, both the counts and the page are\nfiltered to matches, so the categories double as facets. A category that holds\nnothing is `404`.\n",
        "operationId": "browse_retailer",
        "parameters": [
          {
            "description": "Retailer slug from `list_retailers`.",
            "in": "path",
            "name": "slug",
            "required": true,
            "schema": {
              "example": "paknsave",
              "type": "string"
            }
          },
          {
            "description": "Category path, one value per level, outermost first. Omit for the top of the tree.",
            "explode": true,
            "in": "query",
            "name": "c",
            "schema": {
              "items": {
                "maxLength": 128,
                "type": "string"
              },
              "maxItems": 6,
              "type": "array"
            },
            "style": "form"
          },
          {
            "description": "Optional text filter within this category.",
            "in": "query",
            "name": "q",
            "schema": {
              "maxLength": 100,
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "default": 60,
              "maximum": 120,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "in": "query",
            "name": "offset",
            "schema": {
              "default": 0,
              "minimum": 0,
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RetailerBrowse"
                }
              }
            },
            "description": "The category node and a page of listings."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Walk a chain's own category tree",
        "tags": [
          "Retailers"
        ]
      }
    },
    "/api/v1/search": {
      "get": {
        "description": "Hybrid lexical and semantic search over the whole catalogue, grouped one card per\nproduct: each result carries the offers at every chain that sells it (cheapest\nfirst). This is the endpoint behind the site's search box and the right first call\nfor \"what does X cost\". A listing that has not been linked to a product yet is its\nown group with `product_id: null`. Without `q` it returns recently seen items.\nUse the `product_id` with the Products endpoints, or an offer's `id` with the\nListings endpoints. Search is ranked and capped at 100: for *everything* of a\nkind — every beer, sorted by price per litre — use the Shelves endpoints.\n",
        "operationId": "search_products",
        "parameters": [
          {
            "$ref": "#/components/parameters/q"
          },
          {
            "description": "Retailer slug: rank that chain's catalogue only, so the result is the best\nmatches *at* that chain rather than the cross-chain best that it happens to\nstock. Each product still carries every chain's offer. Unknown slug is `400`.\n",
            "in": "query",
            "name": "retailer",
            "schema": {
              "example": "woolworths",
              "type": "string"
            }
          },
          {
            "description": "Maximum number of product groups (1–100).",
            "in": "query",
            "name": "limit",
            "schema": {
              "default": 40,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/ProductGroup"
                  },
                  "type": "array"
                }
              }
            },
            "description": "Product groups, best match first."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Search products across every chain",
        "tags": [
          "Search"
        ]
      }
    },
    "/api/v1/shelves": {
      "get": {
        "description": "A shelf is a cross-chain aisle: the set of products of one kind of thing — beer,\ncheese, nappies — at every chain, built from the generic kind the products\npipeline assigns rather than from any one retailer's menu. Departments are\n`depth: 1`; their aisles hang off them by `parent`. Without `q` this is the whole\ndirectory. With `q` it is the one shelf that text names exactly (its label or an\nalias, so `beer` and `beers` both land on Beer), as a list of one or none — the\nway to turn a category word into a complete set instead of a ranked search.\n",
        "operationId": "list_shelves",
        "parameters": [
          {
            "description": "A category word to resolve to its shelf. Exact match only.",
            "in": "query",
            "name": "q",
            "schema": {
              "example": "beer",
              "maxLength": 100,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/ShelfSummary"
                  },
                  "type": "array"
                }
              }
            },
            "description": "Shelves, departments first, biggest first."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "The shelf directory, or the shelf a query names",
        "tags": [
          "Shelves"
        ]
      }
    },
    "/api/v1/shelves/{slug}": {
      "get": {
        "description": "Everything on the shelf, with a true `total`, sorted server-side across the whole\nset — so `sort=per_unit` really is the cheapest per litre of all 1,800 beers, not\nof a top-sixty slice. `retailer` keeps only products with a current offer at that\nchain (and sorts on that chain's price); `deals` keeps only products with an offer\nbelow shelf price; `kind` narrows to one of the kinds listed in `kinds`; `q` is a\ntext filter within the shelf (and then `sort=relevance` is the default). Facet\ncounts in `kinds` and `retailers` are for the current filters, each ignoring its\nown dimension. Page with `offset` and `limit`. A product's `best` is its cheapest\nadmitted offer counting specials, club prices and multibuy rates; `per_unit` is\nthat per litre, kilogram or item in the shelf's `unit`, null when the product is\nmeasured in something else (it then sorts last).\n",
        "operationId": "browse_shelf",
        "parameters": [
          {
            "description": "Shelf slug from `list_shelves`.",
            "in": "path",
            "name": "slug",
            "required": true,
            "schema": {
              "example": "beer",
              "type": "string"
            }
          },
          {
            "description": "`popular` (most widely stocked; the default), `per_unit` / `per_unit_desc`\n(cheapest / dearest per litre, kilogram or item), `price` / `price_desc`\n(cheapest / dearest pack), `deal` (biggest discount), `relevance` (with `q`).\n",
            "in": "query",
            "name": "sort",
            "schema": {
              "default": "popular",
              "enum": [
                "popular",
                "per_unit",
                "per_unit_desc",
                "price",
                "price_desc",
                "deal",
                "relevance"
              ],
              "type": "string"
            }
          },
          {
            "description": "Retailer slug; only products with a current offer there. Unknown slug is `400`.",
            "in": "query",
            "name": "retailer",
            "schema": {
              "example": "woolworths",
              "type": "string"
            }
          },
          {
            "description": "`1` or `true`: only products with an offer below shelf price.",
            "in": "query",
            "name": "deals",
            "schema": {
              "example": "1",
              "type": "string"
            }
          },
          {
            "description": "A kind slug from the page's `kinds` facet, e.g. `ipa`.",
            "in": "query",
            "name": "kind",
            "schema": {
              "maxLength": 100,
              "type": "string"
            }
          },
          {
            "description": "Optional text filter within the shelf.",
            "in": "query",
            "name": "q",
            "schema": {
              "maxLength": 100,
              "type": "string"
            }
          },
          {
            "description": "Products per page (1–96).",
            "in": "query",
            "name": "limit",
            "schema": {
              "default": 48,
              "maximum": 96,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "Products to skip.",
            "in": "query",
            "name": "offset",
            "schema": {
              "default": 0,
              "minimum": 0,
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ShelfPage"
                }
              }
            },
            "description": "The shelf, its facets, and one page of products in the requested order."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "One page of a shelf, sorted and filtered as a whole set",
        "tags": [
          "Shelves"
        ]
      }
    },
    "/api/v1/stores": {
      "get": {
        "description": "Every geolocated store (about 500) with coordinates, the nearest town, region and\nurban class from official Stats NZ boundaries, how many club/special/multibuy\npromotions it has this week, and its **basket price level**: shelf prices of\nbasket items against the same chain's national median, weighted like the Stats NZ\nbasket (`1.03` = 3 % dearer than the rest of its chain, `null` = not enough priced\nitems). Filter with `retailer`, `town`, `area` or `urban` to get one place instead\nof the whole country (~130 KB unfiltered).\n",
        "operationId": "list_stores",
        "parameters": [
          {
            "description": "Retailer slug.",
            "in": "query",
            "name": "retailer",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Town name as returned in `town` (case-insensitive), e.g. `Tokoroa`, `Rural Auckland`.",
            "in": "query",
            "name": "town",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Region name as returned in `area` (case-insensitive), e.g. `Waikato`.",
            "in": "query",
            "name": "area",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Urban class.",
            "in": "query",
            "name": "urban",
            "schema": {
              "enum": [
                "major",
                "large",
                "medium",
                "small",
                "rural"
              ],
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/Store"
                  },
                  "type": "array"
                }
              }
            },
            "description": "Stores, grouped by chain then name."
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Every store with its price level and place",
        "tags": [
          "Stores"
        ]
      }
    },
    "/api/v1/stores/{id}": {
      "get": {
        "description": "The store's level overall and per basket group, the eight biggest markups\n(`dearer`) and markdowns (`cheaper`) against the chain's national median, a\nhistogram of shelf ÷ median for every listing priced there (`spread`: 20 bins over\n0.80–1.20, buckets 0 and 21 are the tails), and its current `deals`. Store ids\ncome from `list_stores`.\n",
        "operationId": "get_store",
        "parameters": [
          {
            "$ref": "#/components/parameters/storeId"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StoreDetail"
                }
              }
            },
            "description": "The store panel."
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "How one store prices against its chain",
        "tags": [
          "Stores"
        ]
      }
    },
    "/api/v1/stores/{id}/levels": {
      "get": {
        "description": "The store panel's verdict (`store.level` from `get_store`) for every day the index\nhas run: the chain's national basket matches priced from this store's shelf against\nthe chain's daily median, as a weighted geometric mean. `days[]` with aligned\n`level[]` (`1.0` is the chain's median that day, `1.03` is 3 % dearer) and\n`items[]` (basket items priced here that day), `null` on days fewer than twenty were.\nThe newest point can differ from `get_store` by a hair: the panel carries the latest\nobservation, the series the span in force that day.\n",
        "operationId": "get_store_levels",
        "parameters": [
          {
            "$ref": "#/components/parameters/storeId"
          },
          {
            "description": "How many days back from today (7–3650).",
            "in": "query",
            "name": "days",
            "schema": {
              "default": 365,
              "maximum": 3650,
              "minimum": 7,
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StoreLevels"
                }
              }
            },
            "description": "The series."
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "One store's basket price level against its chain, by day",
        "tags": [
          "Stores"
        ]
      }
    },
    "/api/v1/stores/{id}/listings": {
      "get": {
        "description": "Every product priced at the store this week with its shelf price, the chain's\nnational median and the ratio between them, paged and optionally filtered by text.\nThe question a shopper standing in the aisle has: \"what does *this* store charge\nfor X\".\n",
        "operationId": "get_store_listings",
        "parameters": [
          {
            "$ref": "#/components/parameters/storeId"
          },
          {
            "$ref": "#/components/parameters/q"
          },
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "default": 24,
              "maximum": 96,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "in": "query",
            "name": "offset",
            "schema": {
              "default": 0,
              "minimum": 0,
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StoreListings"
                }
              }
            },
            "description": "A page of the store's shelf."
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "What one store charges, searchable",
        "tags": [
          "Stores"
        ]
      }
    },
    "/api/v1/suggestions": {
      "get": {
        "description": "Type-ahead for the search box: categories, brands and products that match a\nprefix, each with a site `href`. Cheap and fast (hot prefixes answer in a few\nmilliseconds). Agents should call `search_products` instead.\n",
        "operationId": "suggest",
        "parameters": [
          {
            "description": "The text typed so far (1–100 characters).",
            "in": "query",
            "name": "q",
            "required": true,
            "schema": {
              "maxLength": 100,
              "minLength": 1,
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "default": 8,
              "maximum": 12,
              "minimum": 1,
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/Suggestion"
                  },
                  "type": "array"
                }
              }
            },
            "description": "Suggestions in display order."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Autocomplete suggestions for a partial query",
        "tags": [
          "Search"
        ],
        "x-mcp": false
      }
    },
    "/data/latest/manifest.json": {
      "get": {
        "description": "The bulk path. Every night after the index run the whole dataset is written as\nZstd Parquet and gzipped CSV under `/data/latest/`: `listings`, `stores`,\n`price_spans` (the full span history; this is *the* dataset), `index_daily` and\n`basket`. The manifest lists each file's byte size, row count and SHA-256, the\ndata day and the licence. Download `/data/latest/<name>` directly; the files carry\nlong cache lifetimes and are not rate limited. Anyone who wants more than a few\nhundred lookups should start here.\n",
        "operationId": "get_export_manifest",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExportManifest"
                }
              }
            },
            "description": "The manifest."
          },
          "404": {
            "description": "No export has run yet."
          }
        },
        "summary": "The nightly exports and their checksums",
        "tags": [
          "Bulk data"
        ]
      }
    },
    "/healthz": {
      "get": {
        "description": "`ok` when the API can reach its database.",
        "operationId": "healthz",
        "responses": {
          "200": {
            "content": {
              "text/plain": {
                "schema": {
                  "const": "ok",
                  "type": "string"
                }
              }
            },
            "description": "Healthy."
          },
          "503": {
            "description": "Database unreachable."
          }
        },
        "summary": "Liveness",
        "tags": [
          "Meta"
        ],
        "x-mcp": false
      }
    },
    "/llms.txt": {
      "get": {
        "description": "An [llms.txt](https://llmstxt.org/) index for this API. `/llms-full.txt` is the complete reference as Markdown, generated from this document.",
        "operationId": "llms_txt",
        "responses": {
          "200": {
            "content": {
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "description": "Markdown."
          }
        },
        "summary": "llms.txt",
        "tags": [
          "Meta"
        ],
        "x-mcp": false
      }
    },
    "/mcp": {
      "post": {
        "description": "A stateless MCP server over Streamable HTTP. Every operation in this document\nthat is not marked internal is a read-only tool with the same name as its\n`operationId` and the same JSON result. Supports protocol revision `2026-07-28`\n(per-request `_meta`, `server/discover`, `Mcp-Method`/`Mcp-Name` headers) and the\nlegacy `initialize` handshake of `2025-03-26` through `2025-11-25`; no session ids\nare issued. Point an MCP client at `https://api.supertracker.nz/mcp` with no\nauthentication. Resources expose this document, `llms-full.txt` and the export\nmanifest.\n",
        "operationId": "mcp",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "description": "A single JSON-RPC 2.0 request or notification.",
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            },
            "description": "A JSON-RPC 2.0 response."
          },
          "202": {
            "description": "Notification accepted."
          },
          "400": {
            "description": "Header mismatch, unsupported protocol version or malformed request, as a JSON-RPC error."
          },
          "404": {
            "description": "Unknown JSON-RPC method (`-32601`)."
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Model Context Protocol endpoint",
        "tags": [
          "Meta"
        ],
        "x-mcp": false
      }
    },
    "/openapi.json": {
      "get": {
        "description": "The OpenAPI 3.1 description of the API, also available as YAML at `/openapi.yaml`. The interactive reference is at `/`.",
        "operationId": "openapi",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            },
            "description": "The OpenAPI document."
          }
        },
        "summary": "This document",
        "tags": [
          "Meta"
        ],
        "x-mcp": false
      }
    }
  },
  "servers": [
    {
      "description": "This host",
      "url": "https://api.supertracker.nz"
    }
  ],
  "tags": [
    {
      "description": "Find products and listings by text or barcode.",
      "name": "Search"
    },
    {
      "description": "One item across every chain, with its history and where it is sold.",
      "name": "Products"
    },
    {
      "description": "One chain's product, its current price everywhere and its history.",
      "name": "Listings"
    },
    {
      "description": "Cross-chain aisles — every chain's version of a kind of thing, as a complete, sortable set.",
      "name": "Shelves"
    },
    {
      "description": "The chains, their own category trees, and the basket priced at each.",
      "name": "Retailers"
    },
    {
      "description": "Every store with its price level, what it charges, and places rolled up.",
      "name": "Stores"
    },
    {
      "description": "What is cheaper than shelf right now, split by who gets it.",
      "name": "Deals"
    },
    {
      "description": "The daily inflation nowcast on the Stats NZ basket.",
      "name": "Food price index"
    },
    {
      "description": "The Stats NZ Food Price Index basket the index is built on.",
      "name": "Basket"
    },
    {
      "description": "Nightly exports of the whole dataset. Use these instead of crawling.",
      "name": "Bulk data"
    },
    {
      "description": "Health, documentation, and the MCP endpoint.",
      "name": "Meta"
    }
  ]
}