{"openapi":"3.1.0","info":{"title":"Cook Nutrition API","description":"Nutrition lookup, aggregation, ingredient resolution and unit conversion for Cooklang recipes.\n\n📖 Writing report templates? See the [report extension guides](/docs/guides).","license":{"name":"Proprietary","identifier":"Proprietary"},"version":"0.1.0"},"paths":{"/aggregate":{"post":{"tags":["nutrition"],"summary":"Resolve and sum nutrition for a batch of items (max 200).","description":"Per-item failures are returned in `failures[]` with the original `index`,\nnot as an HTTP error; only request-level problems (empty batch, batch too\nlarge) return 4xx. `exclusions[]` flags (does not remove) matching\ningredients via `matched_exclusions`; `allergen_summary` reports the\nallergens found across verified items plus any unverified ingredients.","operationId":"aggregateNutrition","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AggregateRequest"}}},"required":true},"responses":{"200":{"description":"Summed totals + per-item results + failures","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AggregateResponse"}}}},"400":{"description":"empty_batch / batch_too_large / unknown_reference_standard","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"401":{"description":"unauthorized (when auth enabled)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"rate_limited (when rate limiting enabled)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/attributions":{"get":{"tags":["meta"],"summary":"`GET /attributions` — public provenance list for every data source.","operationId":"attributions","responses":{"200":{"description":"Data-source attributions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AttributionsResponse"}}}}}}},"/branded/lookup":{"get":{"tags":["branded"],"summary":"Look up branded products by exact UPC or by brand/description text.","operationId":"branded_lookup","parameters":[{"name":"upc","in":"query","description":"Exact UPC/GTIN barcode. Mutually exclusive with `q`.","required":false,"schema":{"type":"string"}},{"name":"q","in":"query","description":"Free-text brand + description search. Mutually exclusive with `upc`.","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Matching branded products","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BrandedLookupResponse"}}}},"400":{"description":"invalid_query (provide exactly one of `upc` or `q`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"401":{"description":"unauthorized (when auth enabled)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"branded_not_found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"rate_limited (when rate limiting enabled)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/categories":{"get":{"tags":["categories"],"summary":"List all categories (slug, name, parent) — the valid slugs for\n`GET /categories/{slug}/check`.","operationId":"listCategories","responses":{"200":{"description":"All categories","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CategoriesResponse"}}}},"401":{"description":"unauthorized (when auth enabled)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"rate_limited (when rate limiting enabled)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/categories/{slug}/check":{"get":{"tags":["categories"],"summary":"Check whether an ingredient is in a category, walking the category tree.","description":"Membership is transitive: an ingredient in a child category is also\nconsidered a member of all ancestor categories.","operationId":"checkCategory","parameters":[{"name":"slug","in":"path","description":"Category slug, e.g. oily_fish (see GET /categories for the full list)","required":true,"schema":{"type":"string"}},{"name":"ingredient","in":"query","description":"Ingredient name/alias/translation to test for membership.","required":true,"schema":{"type":"string"},"example":"salmon"}],"responses":{"200":{"description":"Membership result","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CategoryCheckResponse"}}}},"401":{"description":"unauthorized (when auth enabled)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"category_not_found / ingredient_not_found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"rate_limited (when rate limiting enabled)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/convert":{"get":{"tags":["convert"],"summary":"Convert an amount between units (mass, volume, or count portion).","description":"Mass↔mass conversions need no ingredient. Volume↔mass or count↔mass\nconversions require `ingredient` (and optionally `prep`) so the service\ncan look up density or portion weight.","operationId":"convertUnits","parameters":[{"name":"amount","in":"query","description":"Amount to convert.","required":true,"schema":{"type":"number","format":"double"},"example":"200"},{"name":"from","in":"query","description":"Source unit (mass, volume, or count portion slug).","required":true,"schema":{"type":"string"},"example":"g"},{"name":"to","in":"query","description":"Target unit (mass, volume, or count portion slug).","required":true,"schema":{"type":"string"},"example":"oz"},{"name":"ingredient","in":"query","description":"Ingredient name/alias — optional for mass↔mass, required for volume or count conversions.","required":false,"schema":{"type":"string"},"example":"salmon"},{"name":"prep","in":"query","description":"Preparation slug. Defaults to `raw`.","required":false,"schema":{"type":"string"},"example":"raw"},{"name":"region","in":"query","description":"Region for cup/tbsp/tsp sizing. One of `us`, `uk`, `metric`. Defaults to `us`.","required":false,"schema":{"type":"string"},"example":"us"}],"responses":{"200":{"description":"Conversion result","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConvertResponse"}}}},"400":{"description":"unknown_unit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"401":{"description":"unauthorized (when auth enabled)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"density_unavailable / invalid_amount","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"rate_limited (when rate limiting enabled)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/health":{"get":{"tags":["meta"],"summary":"Liveness probe. Always returns 200 while the process is running.","operationId":"getHealth","responses":{"200":{"description":"Service is up","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthResponse"}}}}}}},"/ingredients/lookup":{"get":{"tags":["ingredients"],"summary":"Resolve a name/alias/translation to a canonical ingredient.","description":"Returns the canonical name, category slugs, and available preparation slugs.\nA 404 also records the unresolved query for catalog-gap analysis.","operationId":"lookupIngredient","parameters":[{"name":"q","in":"query","description":"Name, alias, or translation to resolve.","required":true,"schema":{"type":"string"},"example":"salmon"},{"name":"lang","in":"query","description":"Optional ISO-639-1 language to scope translation matches (e.g. `ru`).","required":false,"schema":{"type":"string"},"example":"ru"}],"responses":{"200":{"description":"Resolved ingredient","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LookupResponse"}}}},"401":{"description":"unauthorized (when auth enabled)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"ingredient_not_found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"rate_limited (when rate limiting enabled)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/nutrition":{"get":{"tags":["nutrition"],"summary":"Resolve and scale nutrition for a single ingredient.","description":"Volume units (ml, l, tsp, tbsp, fl_oz, cup) require a density recorded for\nthe (ingredient, prep) pair; many USDA ingredients lack it and return\n`density_unavailable` — fall back to a mass unit (g, kg, oz, lb). For a whole\nrecipe or meal, prefer `POST /aggregate` over many calls here.","operationId":"getNutrition","parameters":[{"name":"ingredient","in":"query","description":"Ingredient name, alias, or translation to resolve.","required":true,"schema":{"type":"string"},"example":"salmon"},{"name":"amount","in":"query","description":"Amount as a numeric string (parsed server-side; non-numeric -> `invalid_amount`).","required":true,"schema":{"type":"string"},"example":"150"},{"name":"unit","in":"query","description":"Unit: a mass unit (g, kg, mg, oz, lb), a volume unit (ml, l, tsp, tbsp,\nfl_oz, cup), or a count portion recorded for the ingredient (slice,\npiece, fillet, …). Defaults to the ingredient's default portion.","required":false,"schema":{"type":"string"},"example":"g"},{"name":"prep","in":"query","description":"Preparation slug. Defaults to `raw`.","required":false,"schema":{"type":"string"},"example":"cooked"},{"name":"region","in":"query","description":"Region for cup/tbsp/tsp sizing. One of `us`, `uk`, `metric`. Defaults to `us`.","required":false,"schema":{"type":"string"},"example":"us"},{"name":"reference","in":"query","description":"Reference-intake standard for `reference_intakes`. One of `fda`, `eu`,\n`uk`. Defaults to `fda`.","required":false,"schema":{"type":"string"},"example":"fda"}],"responses":{"200":{"description":"Resolved nutrition","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NutritionEnvelope"}}}},"400":{"description":"unknown_unit / unit_required / unknown_reference_standard","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"401":{"description":"unauthorized (when auth enabled)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"ingredient_not_found / preparation_not_available","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"density_unavailable / invalid_amount","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"rate_limited (when rate limiting enabled)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/reference-intakes":{"get":{"tags":["nutrition"],"summary":"List daily reference-intake tables.","description":"Returns `{ standard: { nutrient_slug: daily_value } }`. With `?standard=`,\nreturns just that standard (still nested under its slug). Values are in each\nnutrient key's own unit.","operationId":"getReferenceIntakes","parameters":[{"name":"standard","in":"query","description":"Optional standard filter. One of `fda`, `eu`, `uk`. Omit for all.","required":false,"schema":{"type":"string"},"example":"fda"}],"responses":{"200":{"description":"Reference-intake tables","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"unknown_reference_standard","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"401":{"description":"unauthorized (when auth enabled)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"rate_limited (when rate limiting enabled)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}}},"components":{"schemas":{"AggregateFailure":{"type":"object","required":["index","ingredient","error"],"properties":{"error":{"$ref":"#/components/schemas/AggregateFailureError"},"index":{"type":"integer","minimum":0},"ingredient":{"type":"string"}}},"AggregateFailureError":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"field":{"type":["string","null"]},"message":{"type":"string"},"received":{"type":["string","null"]},"suggestions":{"type":"array","items":{"type":"string"}}}},"AggregateItem":{"type":"object","required":["ingredient","amount"],"properties":{"amount":{"type":"number","format":"double"},"ingredient":{"type":"string"},"prep":{"type":["string","null"]},"region":{"type":["string","null"]},"unit":{"type":["string","null"]}}},"AggregateRequest":{"type":"object","properties":{"exclusions":{"type":"array","items":{"type":"string"}},"items":{"type":"array","items":{"$ref":"#/components/schemas/AggregateItem"}},"reference":{"type":["string","null"],"description":"Reference-intake standard for `reference_intakes`. One of `fda`, `eu`,\n`uk`. Defaults to `fda`."}},"example":{"items":[{"amount":150,"ingredient":"salmon","prep":"cooked","unit":"g"},{"amount":1,"ingredient":"rice","prep":"cooked","region":"us","unit":"cup"}]}},"AggregateResponse":{"type":"object","required":["items","failures","totals","confidence_breakdown","matched_exclusions","unresolved_exclusions","reference_standard","reference_intakes","allergen_summary"],"properties":{"allergen_summary":{"$ref":"#/components/schemas/AllergenSummary"},"confidence_breakdown":{"$ref":"#/components/schemas/ConfidenceBreakdown"},"failures":{"type":"array","items":{"$ref":"#/components/schemas/AggregateFailure"}},"items":{"type":"array","items":{"$ref":"#/components/schemas/ResolveResponse"}},"matched_exclusions":{"type":"array","items":{"type":"string"}},"reference_intakes":{"type":"object","additionalProperties":{"type":"number","format":"double"},"propertyNames":{"type":"string"}},"reference_standard":{"type":"string"},"totals":{"$ref":"#/components/schemas/Totals"},"unresolved_exclusions":{"type":"array","items":{"type":"string"}}}},"AllergenBlock":{"type":"object","description":"Allergen block attached to every resolved item.","required":["status","contains","view"],"properties":{"contains":{"type":"array","items":{"$ref":"#/components/schemas/AllergenEntry"}},"status":{"type":"string","description":"`verified` (audited; `contains` is exhaustive w.r.t. the 14 classes)\nor `unverified` (`contains` is empty and means nothing known — never\n\"free of\")."},"view":{"type":"string"}}},"AllergenEntry":{"type":"object","description":"One allergen on a resolved item: class slug, optional subtype\n(e.g. `gluten`/`wheat`), and a display label.","required":["class","label"],"properties":{"class":{"type":"string"},"label":{"type":"string"},"subtype":{"type":["string","null"]}}},"AllergenSummary":{"type":"object","description":"Batch-level allergen rollup. `contains` is the union over VERIFIED items —\na floor, not a ceiling, whenever `unverified_ingredients` is non-empty.","required":["contains","unverified_ingredients","view"],"properties":{"contains":{"type":"array","items":{"$ref":"#/components/schemas/AllergenEntry"}},"unverified_ingredients":{"type":"array","items":{"type":"string"}},"view":{"type":"string"}}},"AmountBlock":{"type":"object","required":["value","unit","mass_g"],"properties":{"mass_g":{"type":"number","format":"double"},"unit":{"type":"string"},"value":{"type":"number","format":"double"}}},"AttributionsResponse":{"type":"object","required":["sources"],"properties":{"sources":{"type":"array","items":{"$ref":"#/components/schemas/SourceAttribution"}}}},"BrandedLookupResponse":{"type":"object","required":["products"],"properties":{"products":{"type":"array","items":{"$ref":"#/components/schemas/BrandedProduct"}}}},"BrandedNutrition":{"type":"object","properties":{"carb_g":{"type":["number","null"],"format":"double"},"fat_g":{"type":["number","null"],"format":"double"},"fiber_g":{"type":["number","null"],"format":"double"},"kcal":{"type":["number","null"],"format":"double"},"protein_g":{"type":["number","null"],"format":"double"},"sat_fat_g":{"type":["number","null"],"format":"double"},"sugar_g":{"type":["number","null"],"format":"double"}}},"BrandedProduct":{"type":"object","required":["fdc_id","description","per_100g","micronutrients","source","confidence"],"properties":{"brand_name":{"type":["string","null"]},"brand_owner":{"type":["string","null"]},"confidence":{"type":"string"},"description":{"type":"string"},"fdc_id":{"type":"integer","format":"int32"},"gtin_upc":{"type":["string","null"]},"household_serving":{"type":["string","null"]},"micronutrients":{},"per_100g":{"$ref":"#/components/schemas/BrandedNutrition","description":"Nutrition per 100 g."},"per_serving":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/BrandedNutrition","description":"Nutrition per serving, computed only when `serving_size` is in grams (else null)."}]},"serving_size":{"type":["number","null"],"format":"double"},"serving_size_unit":{"type":["string","null"]},"source":{"type":"string"}}},"CategoriesResponse":{"type":"object","description":"The full category taxonomy.","required":["categories"],"properties":{"categories":{"type":"array","items":{"$ref":"#/components/schemas/CategoryItem"}}}},"CategoryCheckResponse":{"type":"object","description":"Whether an ingredient belongs to a category (membership walks ancestors).","required":["in_category"],"properties":{"in_category":{"type":"boolean"}}},"CategoryItem":{"type":"object","description":"One category in the taxonomy.","required":["slug","name"],"properties":{"name":{"type":"string","description":"Human-readable category name.","example":"Oily fish"},"parent":{"type":["string","null"],"description":"Parent category slug, or null for a root category.","example":"fish_and_shellfish"},"slug":{"type":"string","description":"Slug used in `/categories/{slug}/check`.","example":"oily_fish"}}},"ConfidenceBreakdown":{"type":"object","required":["confirmed_items","partial_items","estimated_items","estimated_ingredients"],"properties":{"confirmed_items":{"type":"integer","minimum":0},"estimated_ingredients":{"type":"array","items":{"type":"string"}},"estimated_items":{"type":"integer","minimum":0},"estimated_share_of_micronutrients":{"type":["number","null"],"format":"double"},"partial_items":{"type":"integer","minimum":0}}},"ConvertResponse":{"type":"object","description":"Raw unit conversion result (no nutrition).","required":["from","to","value","result","mass_g"],"properties":{"from":{"type":"string"},"ingredient":{"type":["string","null"]},"mass_g":{"type":"number","format":"double"},"result":{"type":"number","format":"double"},"to":{"type":"string"},"value":{"type":"number","format":"double"}}},"HealthResponse":{"type":"object","description":"Liveness probe response.","required":["status"],"properties":{"status":{"type":"string","description":"Always `\"ok\"` when the service is up."}}},"LookupIngredient":{"type":"object","required":["id","canonical_name","categories","available_preparations"],"properties":{"available_preparations":{"type":"array","items":{"type":"string"}},"canonical_name":{"type":"string"},"categories":{"type":"array","items":{"type":"string"}},"id":{"type":"integer","format":"int64"}}},"LookupResponse":{"type":"object","required":["ingredient"],"properties":{"ingredient":{"$ref":"#/components/schemas/LookupIngredient"}}},"MacrosBlock":{"type":"object","required":["kcal","protein_g","fat_g","carb_g","fiber_g","sugar_g","sat_fat_g"],"properties":{"carb_g":{"type":"number","format":"double"},"fat_g":{"type":"number","format":"double"},"fiber_g":{"type":"number","format":"double"},"kcal":{"type":"number","format":"double"},"protein_g":{"type":"number","format":"double"},"sat_fat_g":{"type":"number","format":"double"},"sugar_g":{"type":"number","format":"double"}}},"MacrosTotals":{"type":"object","required":["kcal","protein_g","fat_g","carb_g","fiber_g","sugar_g","sat_fat_g"],"properties":{"carb_g":{"type":"number","format":"double"},"fat_g":{"type":"number","format":"double"},"fiber_g":{"type":"number","format":"double"},"kcal":{"type":"number","format":"double"},"protein_g":{"type":"number","format":"double"},"sat_fat_g":{"type":"number","format":"double"},"sugar_g":{"type":"number","format":"double"}}},"NutritionEnvelope":{"allOf":[{"$ref":"#/components/schemas/ResolveResponse"},{"type":"object","required":["reference_standard","reference_intakes"],"properties":{"reference_intakes":{"type":"object","additionalProperties":{"type":"number","format":"double"},"propertyNames":{"type":"string"}},"reference_standard":{"type":"string"}}}],"description":"`GET /nutrition` body: the resolved item plus the applied reference-intake\ntable. Reference values are daily constants, so they sit at the top level\nrather than inside each nutrient."},"ProblemDetails":{"type":"object","description":"RFC 9457 Problem Details. Serialized as the body of every error response and\nregistered as the OpenAPI error schema (single source of truth).","required":["type","title","status","code","detail"],"properties":{"code":{"type":"string","description":"Stable machine-readable code, e.g. `density_unavailable`."},"detail":{"type":"string","description":"Human-readable explanation specific to this occurrence."},"field":{"type":["string","null"],"description":"The request field the problem relates to, when applicable."},"received":{"type":["string","null"],"description":"The offending value that was received, when applicable."},"status":{"type":"integer","format":"int32","description":"HTTP status code.","minimum":0},"suggestions":{"type":"array","items":{"type":"string"},"description":"Actionable hints for recovery (valid options, close matches, fallbacks)."},"title":{"type":"string","description":"Short, human-readable summary of the problem type."},"type":{"type":"string","description":"URI reference identifying the problem type (namespaced by `code`)."}}},"ResolveResponse":{"type":"object","description":"One resolved + scaled item, ready to serialise. Same shape as the\n`GET /nutrition` response and as `items[]` under `POST /aggregate`.","required":["ingredient","preparation","amount","macros","micros","vitamins","source","confidence","warnings","allergens"],"properties":{"allergens":{"$ref":"#/components/schemas/AllergenBlock"},"amount":{"$ref":"#/components/schemas/AmountBlock"},"confidence":{"type":"string"},"ingredient":{"type":"string"},"macros":{"$ref":"#/components/schemas/MacrosBlock"},"matched_exclusions":{"type":"array","items":{"type":"string"}},"micros":{"type":"object","additionalProperties":{"type":"number","format":"double"},"propertyNames":{"type":"string"}},"preparation":{"type":"string"},"source":{"type":"string"},"vitamins":{"type":"object","additionalProperties":{"type":"number","format":"double"},"propertyNames":{"type":"string"}},"warnings":{"type":"array","items":{"$ref":"#/components/schemas/Warning"}}}},"SourceAttribution":{"type":"object","required":["id","name","licence","citation","url"],"properties":{"citation":{"type":"string"},"id":{"type":"string","description":"The `source` string used in nutrition/ingredients rows (e.g. \"cofid\")."},"licence":{"type":"string"},"name":{"type":"string"},"url":{"type":"string"}}},"Totals":{"type":"object","required":["mass_g","macros","micros","vitamins","confidence","confidence_weighted","is_partial","included_count","failed_count"],"properties":{"confidence":{"type":"string"},"confidence_weighted":{"type":"string","description":"Mass-weighted-by-contribution confidence (spec §12.6). Less pessimistic\nthan `confidence` (worst-of) when a small-contribution item is low. When\nany item failed, this is forced to `estimated` just like `confidence`."},"failed_count":{"type":"integer","minimum":0},"included_count":{"type":"integer","minimum":0},"is_partial":{"type":"boolean"},"macros":{"$ref":"#/components/schemas/MacrosTotals"},"mass_g":{"type":"number","format":"double"},"micros":{"type":"object","additionalProperties":{"type":"number","format":"double"},"propertyNames":{"type":"string"}},"vitamins":{"type":"object","additionalProperties":{"type":"number","format":"double"},"propertyNames":{"type":"string"}}}},"Warning":{"type":"object","required":["code","message","details"],"properties":{"code":{"type":"string"},"details":{},"message":{"type":"string"}}}},"securitySchemes":{"api_key":{"type":"apiKey","in":"header","name":"X-API-Key"},"bearer":{"type":"http","scheme":"bearer"}}}}