{"openapi":"3.1.0","info":{"title":"Prej API","version":"1.0.0","description":"Public API for Danish grocery prices and products.\n\n**Getting started:** Get a free API key instantly at https://prej.dk/api — no email approval needed, you can get going right away. Pass it as a Bearer token: `Authorization: Bearer prej_xxx...`.\n\n**Plans:**\n- **Gratis** (free) — 25 requests/day. For hobby and non-commercial development. Instant self-service key.\n- **Kommerciel** (basic) — 10,000 requests/day. For publicly released, commercial projects; includes a commercial licence. 500 kr/month.\n- **Enterprise** — custom rate limits and high-volume/bulk access by agreement. Contact hej@prej.dk.\n\nManage your keys and daily usage at https://prej.dk/konto. Terms of use: https://prej.dk/vilkaar.\n\n**Base URL:** `https://api.prej.app`\n\n**Prices** are returned in **øre** (1/100 DKK). Divide by 100 for DKK.\n\n**Caching:** Responses are cached by Cloudflare with `Cache-Control: public, s-maxage=N`.","contact":{"name":"Prej Support","email":"hej@prej.dk","url":"https://prej.dk/api"}},"servers":[{"url":"https://api.prej.app","description":"Production"}],"security":[{"ApiKeyAuth":[]}],"tags":[{"name":"Products","description":"Search, browse, and look up products."},{"name":"Categories & Labels","description":"Category tree and product labels."},{"name":"Chains & Stats","description":"Grocery chains and aggregate stats."}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"prej_*","description":"Bearer token starting with `prej_`. Get a free key instantly at https://prej.dk/api."}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}},"Price":{"type":"object","description":"A current price observed at a specific chain/store.","properties":{"chain_slug":{"type":"string","example":"rema1000"},"chain_name":{"type":"string","example":"Rema 1000"},"chain_logo_url":{"type":"string","nullable":true,"format":"uri"},"image_url":{"type":"string","nullable":true,"format":"uri"},"unit":{"type":"string","nullable":true,"example":"g"},"quantity":{"type":"number","nullable":true,"example":500},"price":{"type":"integer","description":"Price in øre (1 DKK = 100 øre).","example":2495},"unit_price":{"type":"integer","nullable":true,"description":"Unit price in øre per kg/L/stk.","example":4990},"last_seen_date":{"type":"string","format":"date","example":"2026-05-28"},"ai_matched":{"type":"boolean"},"source":{"type":"string","enum":["scraped","flyer"]},"offer_ends":{"type":"string","format":"date","nullable":true},"quantity_label":{"type":"string","nullable":true,"example":"500 g"},"unit_price_label":{"type":"string","nullable":true,"example":"49,90 kr/kg"}}},"ProductSummary":{"type":"object","description":"Product summary as returned in list endpoints.","properties":{"id":{"type":"integer","example":12345},"name":{"type":"string","nullable":true,"example":"Skyr Vanilje"},"description":{"type":"string","nullable":true},"brand":{"type":"string","nullable":true,"example":"Arla"},"image_url":{"type":"string","nullable":true,"format":"uri"},"unit":{"type":"string","nullable":true,"example":"g"},"quantity":{"type":"number","nullable":true,"example":450},"organic":{"type":"boolean"},"chain_count":{"type":"integer","description":"Number of chains selling this product."},"category_name":{"type":"string","nullable":true},"category_slug":{"type":"string","nullable":true},"subcategory_name":{"type":"string","nullable":true},"subcategory_slug":{"type":"string","nullable":true},"sub_subcategory_name":{"type":"string","nullable":true},"sub_subcategory_slug":{"type":"string","nullable":true},"quantity_label":{"type":"string","nullable":true,"example":"450 g"},"unit_price_label":{"type":"string","nullable":true,"example":"55,44 kr/kg"},"prices":{"type":"array","description":"Current prices across chains. Omitted for the price_drop sort.","items":{"$ref":"#/components/schemas/Price"}}}},"ProductDetail":{"allOf":[{"$ref":"#/components/schemas/ProductSummary"},{"type":"object","properties":{"body_html":{"type":"string","nullable":true},"gtins":{"type":"array","items":{"type":"string"}},"nutrition":{"type":"object","nullable":true,"description":"Per-100g nutrition data when available.","additionalProperties":true},"nutriscore_image_url":{"type":"string","nullable":true,"format":"uri"},"labels":{"type":"array","items":{"type":"object","properties":{"slug":{"type":"string"},"name":{"type":"string"},"image_url":{"type":"string","nullable":true}}}},"allergens":{"type":"array","items":{"type":"string"}},"country_of_origin":{"type":"string","nullable":true}}}]},"NutritionDisplay":{"type":"object","nullable":true,"description":"Pre-formatted nutrition rows for display in apps/UIs.","properties":{"nutrition_rows":{"type":"array","items":{"type":"object","properties":{"label":{"type":"string"},"value":{"type":"string"},"sub":{"type":"boolean"}}}},"scoring":{"type":"array","items":{"type":"object","description":"One of: nutriscore | grade | text.","additionalProperties":true}},"co2_rows":{"type":"array","items":{"type":"object","properties":{"label":{"type":"string"},"value":{"type":"string"},"sub":{"type":"boolean"}}}}}},"Chain":{"type":"object","properties":{"id":{"type":"integer"},"slug":{"type":"string","example":"rema1000"},"name":{"type":"string","example":"Rema 1000"},"country":{"type":"string","example":"DK"},"logo_url":{"type":"string","nullable":true,"format":"uri"},"flyer_url":{"type":"string","nullable":true,"format":"uri"},"is_border_trade":{"type":"boolean"},"is_flyer_only":{"type":"boolean"},"color":{"type":"string","nullable":true},"dash_pattern":{"type":"string","nullable":true}}},"CategoryTree":{"type":"object","properties":{"slug":{"type":"string"},"name":{"type":"string"},"icon":{"type":"string"},"subcategories":{"type":"array","items":{"type":"object","properties":{"slug":{"type":"string"},"name":{"type":"string"},"sub_subcategories":{"type":"array","items":{"type":"object","properties":{"slug":{"type":"string"},"name":{"type":"string"}}}}}}}}}}},"paths":{"/v1/products":{"get":{"tags":["Products"],"summary":"Search and browse products","description":"Returns a paginated list of products. At least one filter is required (`q`, `chain`, `category`, `subcategory`, `sub_subcategory`, or `labels`), unless `sort` is `price_drop` or `chain_count`, or `on_offer=1`.","parameters":[{"name":"q","in":"query","schema":{"type":"string","maxLength":200}},{"name":"chain","in":"query","schema":{"type":"string"},"description":"Chain slug, e.g. `rema1000`, `netto`."},{"name":"category","in":"query","schema":{"type":"string"}},{"name":"subcategory","in":"query","schema":{"type":"string"}},{"name":"sub_subcategory","in":"query","schema":{"type":"string"}},{"name":"sort","in":"query","schema":{"type":"string","enum":["chain_count","cheapest","newest","price_drop","price_spread"]}},{"name":"days","in":"query","schema":{"type":"integer","minimum":1,"maximum":30,"default":1},"description":"Window in days for price_drop sort."},{"name":"min_drop_pct","in":"query","schema":{"type":"integer","minimum":0,"maximum":100}},{"name":"max_drop_pct","in":"query","schema":{"type":"integer","minimum":0,"maximum":100}},{"name":"min_price","in":"query","schema":{"type":"number"},"description":"In øre."},{"name":"max_price","in":"query","schema":{"type":"number"},"description":"In øre."},{"name":"exclude_border_trade","in":"query","schema":{"type":"string","enum":["1"]}},{"name":"on_offer","in":"query","schema":{"type":"string","enum":["1"]}},{"name":"labels","in":"query","schema":{"type":"string"},"description":"Comma-separated label slugs."},{"name":"page","in":"query","schema":{"type":"integer","minimum":1,"maximum":1000,"default":1}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100},"description":"Items per page. Defaults vary by endpoint."}],"responses":{"200":{"description":"Paginated products.","content":{"application/json":{"schema":{"type":"object","required":["products","total","page","limit"],"properties":{"products":{"type":"array","items":{"$ref":"#/components/schemas/ProductSummary"}},"total":{"type":"integer"},"page":{"type":"integer"},"limit":{"type":"integer"}}}}}},"400":{"description":"Invalid request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/products/optimize":{"get":{"tags":["Products"],"summary":"Optimize a shopping list across stores","description":"Given a list of product IDs, returns the cheapest combination per store.","parameters":[{"name":"ids","in":"query","required":true,"schema":{"type":"string"},"description":"Comma-separated product IDs (max 500)."},{"name":"exclude_border_trade","in":"query","schema":{"type":"string","enum":["1"]}}],"responses":{"200":{"description":"Optimization result.","content":{"application/json":{"schema":{"type":"object","description":"Cheapest combo and per-store totals. See repository docs for full shape.","additionalProperties":true}}}},"400":{"description":"Invalid request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/products/batch":{"get":{"tags":["Products"],"summary":"Fetch multiple products by ID","parameters":[{"name":"ids","in":"query","required":true,"schema":{"type":"string"},"description":"Comma-separated product IDs (max 500)."}],"responses":{"200":{"description":"Products keyed by ID.","content":{"application/json":{"schema":{"type":"object","required":["products"],"properties":{"products":{"type":"array","items":{"$ref":"#/components/schemas/ProductSummary"}}}}}}},"400":{"description":"Invalid request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/products/{id}":{"get":{"tags":["Products"],"summary":"Get a single product with prices and related products","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer","minimum":1}}],"responses":{"200":{"description":"Product detail.","content":{"application/json":{"schema":{"type":"object","required":["product","related"],"properties":{"product":{"$ref":"#/components/schemas/ProductDetail"},"nutrition_display":{"$ref":"#/components/schemas/NutritionDisplay"},"related":{"type":"object","properties":{"sameBrand":{"type":"array","items":{"$ref":"#/components/schemas/ProductSummary"}},"sameCategory":{"type":"array","items":{"$ref":"#/components/schemas/ProductSummary"}}}}}}}}},"400":{"description":"Invalid request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/products/{id}/history":{"get":{"tags":["Products"],"summary":"Price history for a product","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer","minimum":1}},{"name":"days","in":"query","schema":{"type":"integer","minimum":1,"maximum":365,"default":30}}],"responses":{"200":{"description":"Price history series.","content":{"application/json":{"schema":{"type":"object","required":["history"],"properties":{"history":{"type":"array","description":"Time-series price entries per chain.","items":{"type":"object","additionalProperties":true}}}}}}},"400":{"description":"Invalid request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/categories":{"get":{"tags":["Categories & Labels"],"summary":"All product categories","responses":{"200":{"description":"Category tree.","content":{"application/json":{"schema":{"type":"object","required":["categories"],"properties":{"categories":{"type":"array","items":{"$ref":"#/components/schemas/CategoryTree"}}}}}}},"400":{"description":"Invalid request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/labels":{"get":{"tags":["Categories & Labels"],"summary":"All product labels","responses":{"200":{"description":"Available labels.","content":{"application/json":{"schema":{"type":"object","required":["labels"],"properties":{"labels":{"type":"array","items":{"type":"object","properties":{"slug":{"type":"string"},"name":{"type":"string"},"image_url":{"type":"string","nullable":true}}}}}}}}},"400":{"description":"Invalid request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/scan/{gtin}":{"get":{"tags":["Products"],"summary":"Look up a product by GTIN/EAN barcode","parameters":[{"name":"gtin","in":"path","required":true,"schema":{"type":"string","minLength":8},"description":"EAN-8, EAN-13, UPC-A, or GTIN-14 barcode."}],"responses":{"200":{"description":"Product matched to the barcode.","content":{"application/json":{"schema":{"type":"object","required":["product"],"properties":{"product":{"$ref":"#/components/schemas/ProductDetail"},"nutrition_display":{"$ref":"#/components/schemas/NutritionDisplay"}}}}}},"400":{"description":"Invalid request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/chains":{"get":{"tags":["Chains & Stats"],"summary":"All grocery chains","responses":{"200":{"description":"Chains with aisle ordering and border-trade slugs.","content":{"application/json":{"schema":{"type":"object","required":["chains","aisle_order","border_trade_slugs","flyer_only_slugs"],"properties":{"chains":{"type":"array","items":{"$ref":"#/components/schemas/Chain"}},"aisle_order":{"type":"array","items":{"type":"string"}},"border_trade_slugs":{"type":"array","items":{"type":"string"}},"flyer_only_slugs":{"type":"array","items":{"type":"string"}}}}}}},"400":{"description":"Invalid request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/stats":{"get":{"tags":["Chains & Stats"],"summary":"Aggregate stats (product count, chain count, drop overview)","responses":{"200":{"description":"Stats snapshot.","content":{"application/json":{"schema":{"type":"object","required":["total_products","chain_count","drop_stats"],"properties":{"total_products":{"type":"integer"},"chain_count":{"type":"integer"},"drop_stats":{"type":"object","additionalProperties":true}}}}}},"400":{"description":"Invalid request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}