{"openapi":"3.1.0","info":{"title":"Noms API","summary":"A REST API for nutritional data: foods, brands, nutrients and food groups.","description":"**Noms** is a REST API for nutritional data: **foods**, **brands**, **nutrients**\nand **food groups**.\n\n## Authentication\n\nSend your API key in the `X-API-Key` header on every `/v1` request:\n\n```\nX-API-Key: noms_sk_...\n```\n\n## Responses\n\n- **Envelope.** Every response is wrapped as `{ \"data\": … }`. List responses also\n  carry `links` (pagination URLs) and `meta` (page info).\n- **Types.** Keys are `snake_case`, timestamps are ISO 8601 UTC (`...Z`), and decimals\n  are JSON **strings** to keep full precision (e.g. `\"12.5000\"`, never a rounded\n  float).\n\n## Querying lists\n\n- **Pagination.** Set a page size with `page_size`, then follow `links.next` until\n  `meta.has_more` is `false`. A cursor stays valid as the catalog changes underneath\n  it, so a page you fetch is never invalidated by one you have not fetched yet. If you\n  sort on a value that can change (a nutrient, a brand name), a row whose value moves\n  while you are paging can repeat or be missed — de-duplicate by `id`, and walk the\n  whole catalog on the default `id` order, which never moves.\n- **Filtering.** `?name[eq]=Milk` (a bare `?name=Milk` defaults to `eq`); operators\n  are `eq`, `in`, `gt`, `gte`, `lt`, `lte` where the field supports them. Filters\n  match only rows with a present value: on a nullable field, `[eq]=false` (or any\n  comparison) excludes `null` rows. For example, `is_foundational[eq]=false`\n  returns explicitly-`false` foods, not the undetermined (`null`) ones.\n- **Sorting.** `?sort=name` (ascending) or `?sort=-name` (descending); comma-join\n  for tie-breakers.\n- **Text search.** `?q=...` is typo-tolerant — a close-but-misspelled word still\n  finds the right food. With no explicit `sort`, results come back best match\n  first. A `sort` given *alongside* `q` breaks ties within that ordering rather\n  than replacing it — to order strictly by a column, omit `q`.\n- **Sorting by nutrient.** `?sort=-nutrient[PROTEIN]` orders foods by their\n  per-100 g/ml value for a nutrient code. Foods that do not carry the nutrient\n  rank **last** rather than being dropped, so the result count is the same as\n  the unsorted query's.\n- **Sorting by brand.** `?sort=brand_name` orders by the food's brand; unbranded\n  foods rank last, on the same rule.\n- **Sparse fieldsets.** Return only the keys you need with `fields[<resource>]`, e.g.\n  `?fields[foods]=id,name`.\n- **Includes.** `/v1/foods` can embed related resources with `include=`, e.g.\n  `include=brand,food_group,nutrients,serving_sizes,images,market_countries`.\n\n## Errors\n\nEvery error is an **RFC 9457** problem document served as `application/problem+json`,\nwith a stable `type`, a human-readable `title` and `detail`, the HTTP `status`, and\nresource-specific extension members where useful.\n\n## Rate limits\n\nYour account has a request quota for its plan, and every key you hold draws on the same\nallowance — a second key never buys a second quota. The window is a rolling 24 hours on\nthe free plan and your billing period on the paid plans.\nEvery `/v1` resource response carries `RateLimit-Limit`, `RateLimit-Remaining`, and\n`RateLimit-Reset` headers (plus legacy `X-RateLimit-*` aliases) so you can see where\nyou stand before hitting the wall. Once the quota is exhausted, requests return\n**`429 Too Many Requests`** (an RFC 9457 problem) with a `Retry-After` header. Wait\nuntil the window resets, then retry.\n\n## Free-tier limits\n\nThe free tier is built for **looking things up**, not bulk-downloading the dataset:\n\n- **Search or fetch by id.** List endpoints require a `?q=` search; a bare or\n  filter-only list returns **`403`** (`/problems/enumeration-forbidden`). Any single\n  record is always reachable with `GET /v1/{resource}/{id}`.\n- **Result depth.** Search results are capped at roughly the first **1,000** matches;\n  paging past that returns **`403`** (`/problems/result-depth-exceeded`).\n- **Single market.** The free tier serves **one** market (country). Choose it in the\n  dashboard or with `PUT /v1/market`; until you do, `foods` requests return **`403`**\n  (`/problems/market-not-selected`). After that, `foods` is scoped to that market. The\n  choice belongs to your **account**, so every key you hold shares it.\n\nPaid tiers lift all three: full enumeration, unlimited pagination depth, all markets.\n","contact":{"name":"Noms","url":"https://noms.sh/","email":"support@noms.sh"},"license":{"name":"Proprietary — © Noms, all rights reserved"},"version":"v1","x-logo":{"url":"/wordmark.svg","backgroundColor":"#ffffff","altText":"Noms","href":"https://noms.sh"}},"servers":[{"url":"https://api.noms.sh","description":"Production"}],"paths":{"/v1/foods":{"get":{"tags":["foods"],"summary":"List foods","description":"Return a keyset-paginated page of the food catalog — the primary endpoint.\n\nEmbed related resources with `include=` (`brand`, `food_group`, `nutrients`,\n`serving_sizes`, `images`, `market_countries`); filter on columns, relations\n(`brand_name`, `market_countries`), and per-nutrient values\n(`nutrient[CODE][op]`); sort on columns or a nutrient value\n(`sort=-nutrient[PROTEIN]`); trim with `fields[<resource>]`; and search with `?q=`.","operationId":"foodsListFoods","security":[{"ApiKey":[]}],"parameters":[{"name":"page_size","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Number of items to return per page (keyset pagination). Allowed range is 1–20; a value outside it is rejected with a 400.","examples":[20],"title":"Page Size"},"description":"Number of items to return per page (keyset pagination). Allowed range is 1–20; a value outside it is rejected with a 400."},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Opaque keyset-pagination cursor. Don't build it by hand; follow `links.next` from a previous response to fetch the next page.","title":"Cursor"},"description":"Opaque keyset-pagination cursor. Don't build it by hand; follow `links.next` from a previous response to fetch the next page."},{"name":"sort","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Sort key; prefix with `-` for descending (`-name`). Comma-join keys for tie-breakers. Defaults to the resource's natural order.","examples":["-name"],"title":"Sort"},"description":"Sort key; prefix with `-` for descending (`-name`). Comma-join keys for tie-breakers. Defaults to the resource's natural order."},{"name":"include","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Comma-separated related resources to embed in each item: `brand`, `food_group`, `nutrients`, `serving_sizes`, `images`, `market_countries`. Relations load only when requested.","examples":["brand,nutrients"],"title":"Include"},"description":"Comma-separated related resources to embed in each item: `brand`, `food_group`, `nutrients`, `serving_sizes`, `images`, `market_countries`. Relations load only when requested."},{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Typo-tolerant text search. Filters to matching rows and, when no `sort` is given, orders them by relevance. Composes with filters and pagination.","examples":["chocolat"],"title":"Q"},"description":"Typo-tolerant text search. Filters to matching rows and, when no `sort` is given, orders them by relevance. Composes with filters and pagination."},{"name":"fields[foods]","in":"query","required":false,"description":"Sparse fieldset. A comma-separated subset of `foods` fields to return (e.g. `id,name`). Omit to return all fields.","schema":{"type":"string"},"example":"id,name"},{"name":"fields[brands]","in":"query","required":false,"description":"Sparse fieldset. A comma-separated subset of `brands` fields to return (e.g. `id,name`). Omit to return all fields.","schema":{"type":"string"},"example":"id,name"},{"name":"fields[food-groups]","in":"query","required":false,"description":"Sparse fieldset. A comma-separated subset of `food-groups` fields to return (e.g. `id,name`). Omit to return all fields.","schema":{"type":"string"},"example":"id,name"},{"name":"fields[nutrients]","in":"query","required":false,"description":"Sparse fieldset. A comma-separated subset of `nutrients` fields to return (e.g. `id,name`). Omit to return all fields.","schema":{"type":"string"},"example":"id,name"},{"name":"fields[serving_sizes]","in":"query","required":false,"description":"Sparse fieldset. A comma-separated subset of `serving_sizes` fields to return (e.g. `descriptor,grams`). Omit to return all fields.","schema":{"type":"string"},"example":"descriptor,grams"},{"name":"fields[images]","in":"query","required":false,"description":"Sparse fieldset. A comma-separated subset of `images` fields to return (e.g. `type,url`). Omit to return all fields.","schema":{"type":"string"},"example":"type,url"},{"name":"fields[market_countries]","in":"query","required":false,"description":"Sparse fieldset. A comma-separated subset of `market_countries` fields to return (e.g. `country_code`). Omit to return all fields.","schema":{"type":"string"},"example":"country_code"},{"name":"barcode","in":"query","required":false,"description":"Filter on `barcode`. Bare `barcode=value` means `eq`; the bracket form names the operator — `barcode[eq]`, `barcode[in]`. `barcode[in]` takes a comma-separated list. Rows with no value for the field never match.","schema":{"type":"string"}},{"name":"brand_id","in":"query","required":false,"description":"Filter on `brand_id`. Bare `brand_id=value` means `eq`; the bracket form names the operator — `brand_id[eq]`, `brand_id[in]`. `brand_id[in]` takes a comma-separated list. Rows with no value for the field never match.","schema":{"type":"string"}},{"name":"brand_name","in":"query","required":false,"description":"Filter on `brand_name`. Bare `brand_name=value` means `eq`; the bracket form names the operator — `brand_name[eq]`, `brand_name[in]`. `brand_name[in]` takes a comma-separated list. Rows with no value for the field never match.","schema":{"type":"string"}},{"name":"country_of_origin","in":"query","required":false,"description":"Filter on `country_of_origin`. Bare `country_of_origin=value` means `eq`; the bracket form names the operator — `country_of_origin[eq]`, `country_of_origin[in]`. `country_of_origin[in]` takes a comma-separated list. Rows with no value for the field never match.","schema":{"type":"string"}},{"name":"food_group_id","in":"query","required":false,"description":"Filter on `food_group_id`. Bare `food_group_id=value` means `eq`; the bracket form names the operator — `food_group_id[eq]`, `food_group_id[in]`. `food_group_id[in]` takes a comma-separated list. Rows with no value for the field never match.","schema":{"type":"string"}},{"name":"id","in":"query","required":false,"description":"Filter on `id`. Bare `id=value` means `eq`; the bracket form names the operator — `id[eq]`, `id[in]`. `id[in]` takes a comma-separated list. Rows with no value for the field never match.","schema":{"type":"string"}},{"name":"is_foundational","in":"query","required":false,"description":"Filter on `is_foundational`. Bare `is_foundational=value` means `eq`; the bracket form names the operator — `is_foundational[eq]`. Rows with no value for the field never match.","schema":{"type":"string"}},{"name":"market_countries","in":"query","required":false,"description":"Filter on `market_countries`. Bare `market_countries=value` means `eq`; the bracket form names the operator — `market_countries[eq]`, `market_countries[in]`. `market_countries[in]` takes a comma-separated list. Rows with no value for the field never match.","schema":{"type":"string"}},{"name":"name","in":"query","required":false,"description":"Filter on `name`. Bare `name=value` means `eq`; the bracket form names the operator — `name[eq]`, `name[in]`. `name[in]` takes a comma-separated list. Rows with no value for the field never match.","schema":{"type":"string"}},{"name":"nutrient[CODE][op]","in":"query","required":false,"description":"Filter on a nutrient's per-100 value. `CODE` is a nutrient id from `GET /v1/nutrients` and `op` is one of `eq`, `gt`, `gte`, `lt`, `lte`. Repeat the parameter to apply several constraints at once. A food with no value recorded for that nutrient never matches.","schema":{"type":"number"},"example":"10"},{"name":"If-None-Match","in":"header","required":false,"schema":{"type":"string"},"description":"An `ETag` from a previous response for this URL. If it still matches, the API answers `304 Not Modified` with no body; otherwise you get the full `200`. Either way the request is metered.","examples":{"revalidate":{"value":"\"9f2c1b7e4a0d6538bb1c0e2f7a4d9c30\""}}}],"responses":{"200":{"description":"A page of foods. Each `data` item is the flat food resource; nested objects (`brand`, `food_group`, `nutrients`, `serving_sizes`, `images`, `market_countries`) appear only when requested via `include=`, and `fields[<resource>]` may trim the keys present.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Page_FoodDetailSchema_"}}},"headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Your account's request quota for the current window."},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests remaining in the current window (floored at 0)."},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the current window resets."},"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Legacy alias for `RateLimit-Limit`."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Legacy alias for `RateLimit-Remaining`."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Legacy alias for the reset time, as a Unix epoch timestamp (seconds)."},"ETag":{"schema":{"type":"string"},"description":"A strong entity-tag for this exact response. Send it back as `If-None-Match` on your next request for the same URL: if nothing has changed you get a `304 Not Modified` with no body instead of the full page. The tag covers everything the response depends on — your tier and market, `include`, `fields`, `sort`, and the page cursor — so it is only ever valid for the request that produced it."}}},"422":{"description":"The request was well-formed but could not be processed: a parameter failed validation, or a value is not one this resource accepts.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"/problems/validation-error","title":"Request validation error","status":422,"errors":[{"type":"int_parsing","loc":["query","page_size"],"msg":"Input should be a valid integer"}],"detail":"Request validation failed.","doc_url":"https://api.noms.sh/docs#tag/validation-error"}}}},"400":{"description":"Malformed request: an invalid query parameter, cursor, filter, sort, page size, include, or sparse-fieldset selection.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"Invalid pagination cursor":{"value":{"type":"/problems/invalid-cursor","title":"Invalid pagination cursor","status":400,"cursor":"bm90LWEtY3Vyc29y","reason":"invalid base64 payload","detail":"Cursor could not be decoded: invalid base64 payload. Follow links.next from a fresh first page rather than constructing a cursor by hand.","doc_url":"https://api.noms.sh/docs#tag/invalid-cursor"}},"Invalid filter":{"value":{"type":"/problems/invalid-filter","title":"Invalid filter","status":400,"field":"name","op":"contains","supported_operators":["eq","in"],"supported_fields":["id","name","barcode"],"detail":"Filter 'name' is invalid: operator 'contains' is not supported for 'name'. Supported operators: eq, in.","doc_url":"https://api.noms.sh/docs#tag/invalid-filter"}},"Invalid sort":{"value":{"type":"/problems/invalid-sort","title":"Invalid sort","status":400,"column":"created_at","supported_columns":["id","name"],"detail":"Sort column 'created_at' is not sortable on this endpoint. Sortable columns: id, name.","doc_url":"https://api.noms.sh/docs#tag/invalid-sort"}},"Invalid page size":{"value":{"type":"/problems/invalid-page-size","title":"Invalid page size","status":400,"requested":0,"minimum":1,"maximum":20,"detail":"page_size=0 is below the minimum of 1","doc_url":"https://api.noms.sh/docs#tag/invalid-page-size"}},"Unknown query parameter":{"value":{"type":"/problems/unknown-query-param","title":"Unknown query parameter","status":400,"param":"page[size]","supported_params":["cursor","page_size","sort"],"suggestion":"page_size","detail":"'page[size]' is not a supported query parameter. Did you mean 'page_size'? Supported parameters: cursor, page_size, sort; plus LHS-bracket filters (e.g. name[eq]).","doc_url":"https://api.noms.sh/docs#tag/unknown-query-param"}},"Invalid sparse-fieldset selection":{"value":{"type":"/problems/invalid-fields","title":"Invalid sparse-fieldset selection","status":400,"resource":"foods","field":"colour","supported_fields":["id","name","barcode"],"detail":"Field 'colour' is not exposed on resource 'foods'. Exposed fields: id, name, barcode.","doc_url":"https://api.noms.sh/docs#tag/invalid-fields"}},"Invalid include":{"value":{"type":"/problems/invalid-include","title":"Invalid include","status":400,"relation":"manufacturer","supported_relations":["brand","food_group","nutrients","serving_sizes","images","market_countries"],"detail":"Relationship 'manufacturer' cannot be included on this endpoint. Supported relationships: brand, food_group, nutrients, serving_sizes, images, market_countries.","doc_url":"https://api.noms.sh/docs#tag/invalid-include"}}}}}},"401":{"description":"Authentication is missing or invalid.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"/problems/unauthorized","title":"Unauthorized","status":401,"detail":"Valid authentication is required to access this resource.","doc_url":"https://api.noms.sh/docs#tag/unauthorized"}}}},"403":{"description":"The request is authenticated but not permitted.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"Bulk enumeration not available on this tier":{"value":{"type":"/problems/enumeration-forbidden","title":"Bulk enumeration not available on this tier","status":403,"detail":"This tier does not allow bulk listing. Add a `q` search query to this request, fetch a single item by id, or upgrade for full enumeration.","doc_url":"https://api.noms.sh/docs#tag/enumeration-forbidden"}},"Result depth limit exceeded":{"value":{"type":"/problems/result-depth-exceeded","title":"Result depth limit exceeded","status":403,"cap":1000,"detail":"This tier returns roughly the first 1000 results of a query. Narrow your search with `q` or filters, or upgrade for unlimited pagination depth.","doc_url":"https://api.noms.sh/docs#tag/result-depth-exceeded"}},"No market selected for this tier":{"value":{"type":"/problems/market-not-selected","title":"No market selected for this tier","status":403,"detail":"This tier serves one market and your account has not chosen one yet. Set your market in the dashboard or with `PUT /v1/market`, then retry — or upgrade for all-markets access.","doc_url":"https://api.noms.sh/docs#tag/market-not-selected"}}}}}},"429":{"description":"The key exceeded a rate limit: either its per-window request quota or its short-window burst rate; retry after the `Retry-After` interval (quota responses also carry `RateLimit` headers).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"Rate limit exceeded":{"value":{"type":"/problems/rate-limited","title":"Rate limit exceeded","status":429,"limit":1,"window":"second","retry_after":1,"detail":"Burst rate limit of 1 requests per second exceeded. Slow down and retry after the Retry-After interval.","doc_url":"https://api.noms.sh/docs#tag/rate-limited"}},"Quota exceeded":{"value":{"type":"/problems/quota-exceeded","title":"Quota exceeded","status":429,"limit":250,"window":"daily","retry_after":3600,"detail":"Request quota of 250 per day exceeded. Retry once the current window resets.","doc_url":"https://api.noms.sh/docs#tag/quota-exceeded"}}}}},"headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Your account's request quota for the current window."},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests remaining in the current window (floored at 0)."},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the current window resets."},"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Legacy alias for `RateLimit-Limit`."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Legacy alias for `RateLimit-Remaining`."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Legacy alias for the reset time, as a Unix epoch timestamp (seconds)."},"Retry-After":{"schema":{"type":"integer"},"description":"Seconds until the quota window resets."}}},"503":{"description":"The service is temporarily unable to handle the request; retry shortly.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"Service unavailable":{"value":{"type":"/problems/service-unavailable","title":"Service unavailable","status":503,"detail":"The service is briefly unavailable; retry shortly.","doc_url":"https://api.noms.sh/docs#tag/service-unavailable"}},"Search unavailable":{"value":{"type":"/problems/search-unavailable","title":"Search unavailable","status":503,"detail":"The search service is temporarily unavailable, so this request could not be answered. Retry shortly. Requests that do not use `q` or a nutrient sort are unaffected.","doc_url":"https://api.noms.sh/docs#tag/search-unavailable"}}}}}},"504":{"description":"The `?q=` search outran its time budget and was cancelled; narrow the query and retry.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"/problems/search-timeout","title":"Search timed out","status":504,"timeout_ms":800,"detail":"The search exceeded its 800ms time limit and was abandoned. Narrow the query — a more specific `q`, or additional filters — and retry.","doc_url":"https://api.noms.sh/docs#tag/search-timeout"}}}},"500":{"description":"An unexpected server error occurred.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"/problems/internal-server-error","title":"Internal server error","status":500,"detail":"An unexpected error occurred while processing the request.","doc_url":"https://api.noms.sh/docs#tag/internal-server-error"}}}},"304":{"description":"Your copy is still current — the `If-None-Match` you sent matches the current entity-tag, so no body is returned. **This still counts against your quota**: the request reached the API, so it is metered and rate-limited exactly like a `200`.","headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Your account's request quota for the current window."},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests remaining in the current window (floored at 0)."},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the current window resets."},"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Legacy alias for `RateLimit-Limit`."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Legacy alias for `RateLimit-Remaining`."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Legacy alias for the reset time, as a Unix epoch timestamp (seconds)."},"ETag":{"schema":{"type":"string"},"description":"A strong entity-tag for this exact response. Send it back as `If-None-Match` on your next request for the same URL: if nothing has changed you get a `304 Not Modified` with no body instead of the full page. The tag covers everything the response depends on — your tier and market, `include`, `fields`, `sort`, and the page cursor — so it is only ever valid for the request that produced it."}}}}}},"/v1/foods/{id}":{"get":{"tags":["foods"],"summary":"Get a food","description":"Return a single food by its short-id, or a 404 if no food has that id.\n\nEmbed related resources with `include=` and trim with `fields[<resource>]`, the\nsame way the list endpoint does. A **retired** id (one merged into another\nfood) forwards to the surviving food at `200` with an added `canonical_id`\nfield, so a stored id never dead-ends after a merge.","operationId":"foodsGetFood","security":[{"ApiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"Food short-id (12-char base62).","examples":["3f9a1c0b7e2d"],"title":"Id"},"description":"Food short-id (12-char base62)."},{"name":"include","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Comma-separated related resources to embed: `brand`, `food_group`, `nutrients`, `serving_sizes`, `images`, `market_countries`.","examples":["brand,nutrients"],"title":"Include"},"description":"Comma-separated related resources to embed: `brand`, `food_group`, `nutrients`, `serving_sizes`, `images`, `market_countries`."},{"name":"fields[foods]","in":"query","required":false,"description":"Sparse fieldset. A comma-separated subset of `foods` fields to return (e.g. `id,name`). Omit to return all fields.","schema":{"type":"string"},"example":"id,name"},{"name":"fields[brands]","in":"query","required":false,"description":"Sparse fieldset. A comma-separated subset of `brands` fields to return (e.g. `id,name`). Omit to return all fields.","schema":{"type":"string"},"example":"id,name"},{"name":"fields[food-groups]","in":"query","required":false,"description":"Sparse fieldset. A comma-separated subset of `food-groups` fields to return (e.g. `id,name`). Omit to return all fields.","schema":{"type":"string"},"example":"id,name"},{"name":"fields[nutrients]","in":"query","required":false,"description":"Sparse fieldset. A comma-separated subset of `nutrients` fields to return (e.g. `id,name`). Omit to return all fields.","schema":{"type":"string"},"example":"id,name"},{"name":"fields[serving_sizes]","in":"query","required":false,"description":"Sparse fieldset. A comma-separated subset of `serving_sizes` fields to return (e.g. `descriptor,grams`). Omit to return all fields.","schema":{"type":"string"},"example":"descriptor,grams"},{"name":"fields[images]","in":"query","required":false,"description":"Sparse fieldset. A comma-separated subset of `images` fields to return (e.g. `type,url`). Omit to return all fields.","schema":{"type":"string"},"example":"type,url"},{"name":"fields[market_countries]","in":"query","required":false,"description":"Sparse fieldset. A comma-separated subset of `market_countries` fields to return (e.g. `country_code`). Omit to return all fields.","schema":{"type":"string"},"example":"country_code"},{"name":"If-None-Match","in":"header","required":false,"schema":{"type":"string"},"description":"An `ETag` from a previous response for this URL. If it still matches, the API answers `304 Not Modified` with no body; otherwise you get the full `200`. Either way the request is metered.","examples":{"revalidate":{"value":"\"9f2c1b7e4a0d6538bb1c0e2f7a4d9c30\""}}}],"responses":{"200":{"description":"A single food. The flat food resource; nested objects (`brand`, `food_group`, `nutrients`, `serving_sizes`, `images`, `market_countries`) appear only when requested via `include=`, and `fields[<resource>]` may trim the keys present. If the requested id was **retired** by a merge, the canonical food is returned with an extra `canonical_id` field naming its live id — update any stored reference to it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Item_FoodDetailSchema_"}}},"headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Your account's request quota for the current window."},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests remaining in the current window (floored at 0)."},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the current window resets."},"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Legacy alias for `RateLimit-Limit`."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Legacy alias for `RateLimit-Remaining`."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Legacy alias for the reset time, as a Unix epoch timestamp (seconds)."},"ETag":{"schema":{"type":"string"},"description":"A strong entity-tag for this exact response. Send it back as `If-None-Match` on your next request for the same URL: if nothing has changed you get a `304 Not Modified` with no body instead of the full page. The tag covers everything the response depends on — your tier and market, `include`, `fields`, `sort`, and the page cursor — so it is only ever valid for the request that produced it."}}},"422":{"description":"The request was well-formed but could not be processed: a parameter failed validation, or a value is not one this resource accepts.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"/problems/validation-error","title":"Request validation error","status":422,"errors":[{"type":"int_parsing","loc":["query","page_size"],"msg":"Input should be a valid integer"}],"detail":"Request validation failed.","doc_url":"https://api.noms.sh/docs#tag/validation-error"}}}},"404":{"description":"No resource matches the requested id.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"/problems/not-found","title":"Not found","status":404,"resource":"foods","id":"abc123def456","detail":"Food with id 'abc123def456' was not found.","doc_url":"https://api.noms.sh/docs#tag/not-found"}}}},"400":{"description":"Malformed request: an invalid query parameter, cursor, filter, sort, page size, include, or sparse-fieldset selection.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"Unknown query parameter":{"value":{"type":"/problems/unknown-query-param","title":"Unknown query parameter","status":400,"param":"page[size]","supported_params":["cursor","page_size","sort"],"suggestion":"page_size","detail":"'page[size]' is not a supported query parameter. Did you mean 'page_size'? Supported parameters: cursor, page_size, sort; plus LHS-bracket filters (e.g. name[eq]).","doc_url":"https://api.noms.sh/docs#tag/unknown-query-param"}},"Invalid sparse-fieldset selection":{"value":{"type":"/problems/invalid-fields","title":"Invalid sparse-fieldset selection","status":400,"resource":"foods","field":"colour","supported_fields":["id","name","barcode"],"detail":"Field 'colour' is not exposed on resource 'foods'. Exposed fields: id, name, barcode.","doc_url":"https://api.noms.sh/docs#tag/invalid-fields"}},"Invalid include":{"value":{"type":"/problems/invalid-include","title":"Invalid include","status":400,"relation":"manufacturer","supported_relations":["brand","food_group","nutrients","serving_sizes","images","market_countries"],"detail":"Relationship 'manufacturer' cannot be included on this endpoint. Supported relationships: brand, food_group, nutrients, serving_sizes, images, market_countries.","doc_url":"https://api.noms.sh/docs#tag/invalid-include"}}}}}},"401":{"description":"Authentication is missing or invalid.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"/problems/unauthorized","title":"Unauthorized","status":401,"detail":"Valid authentication is required to access this resource.","doc_url":"https://api.noms.sh/docs#tag/unauthorized"}}}},"429":{"description":"The key exceeded a rate limit: either its per-window request quota or its short-window burst rate; retry after the `Retry-After` interval (quota responses also carry `RateLimit` headers).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"Rate limit exceeded":{"value":{"type":"/problems/rate-limited","title":"Rate limit exceeded","status":429,"limit":1,"window":"second","retry_after":1,"detail":"Burst rate limit of 1 requests per second exceeded. Slow down and retry after the Retry-After interval.","doc_url":"https://api.noms.sh/docs#tag/rate-limited"}},"Quota exceeded":{"value":{"type":"/problems/quota-exceeded","title":"Quota exceeded","status":429,"limit":250,"window":"daily","retry_after":3600,"detail":"Request quota of 250 per day exceeded. Retry once the current window resets.","doc_url":"https://api.noms.sh/docs#tag/quota-exceeded"}}}}},"headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Your account's request quota for the current window."},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests remaining in the current window (floored at 0)."},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the current window resets."},"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Legacy alias for `RateLimit-Limit`."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Legacy alias for `RateLimit-Remaining`."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Legacy alias for the reset time, as a Unix epoch timestamp (seconds)."},"Retry-After":{"schema":{"type":"integer"},"description":"Seconds until the quota window resets."}}},"503":{"description":"The service is temporarily unable to handle the request; retry shortly.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"/problems/service-unavailable","title":"Service unavailable","status":503,"detail":"The service is briefly unavailable; retry shortly.","doc_url":"https://api.noms.sh/docs#tag/service-unavailable"}}}},"500":{"description":"An unexpected server error occurred.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"/problems/internal-server-error","title":"Internal server error","status":500,"detail":"An unexpected error occurred while processing the request.","doc_url":"https://api.noms.sh/docs#tag/internal-server-error"}}}},"403":{"description":"The request is authenticated but not permitted.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"/problems/market-not-selected","title":"No market selected for this tier","status":403,"detail":"This tier serves one market and your account has not chosen one yet. Set your market in the dashboard or with `PUT /v1/market`, then retry — or upgrade for all-markets access.","doc_url":"https://api.noms.sh/docs#tag/market-not-selected"}}}},"304":{"description":"Your copy is still current — the `If-None-Match` you sent matches the current entity-tag, so no body is returned. **This still counts against your quota**: the request reached the API, so it is metered and rate-limited exactly like a `200`.","headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Your account's request quota for the current window."},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests remaining in the current window (floored at 0)."},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the current window resets."},"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Legacy alias for `RateLimit-Limit`."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Legacy alias for `RateLimit-Remaining`."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Legacy alias for the reset time, as a Unix epoch timestamp (seconds)."},"ETag":{"schema":{"type":"string"},"description":"A strong entity-tag for this exact response. Send it back as `If-None-Match` on your next request for the same URL: if nothing has changed you get a `304 Not Modified` with no body instead of the full page. The tag covers everything the response depends on — your tier and market, `include`, `fields`, `sort`, and the page cursor — so it is only ever valid for the request that produced it."}}}}}},"/v1/nutrients":{"get":{"tags":["nutrients"],"summary":"List nutrients","description":"Return a keyset-paginated page of the canonical nutrient catalog.\n\nSupports filtering (`id`, `name`, `unit`), sorting, sparse fieldsets\n(`fields[nutrients]`), and typo-tolerant search (`?q=`).","operationId":"nutrientsListNutrients","security":[{"ApiKey":[]}],"parameters":[{"name":"page_size","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Number of items to return per page (keyset pagination). Allowed range is 1–20; a value outside it is rejected with a 400.","examples":[20],"title":"Page Size"},"description":"Number of items to return per page (keyset pagination). Allowed range is 1–20; a value outside it is rejected with a 400."},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Opaque keyset-pagination cursor. Don't build it by hand; follow `links.next` from a previous response to fetch the next page.","title":"Cursor"},"description":"Opaque keyset-pagination cursor. Don't build it by hand; follow `links.next` from a previous response to fetch the next page."},{"name":"sort","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Sort key; prefix with `-` for descending (`-name`). Comma-join keys for tie-breakers. Defaults to the resource's natural order.","examples":["-name"],"title":"Sort"},"description":"Sort key; prefix with `-` for descending (`-name`). Comma-join keys for tie-breakers. Defaults to the resource's natural order."},{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Typo-tolerant text search. Filters to matching rows and, when no `sort` is given, orders them by relevance. Composes with filters and pagination.","examples":["chocolat"],"title":"Q"},"description":"Typo-tolerant text search. Filters to matching rows and, when no `sort` is given, orders them by relevance. Composes with filters and pagination."},{"name":"fields[nutrients]","in":"query","required":false,"description":"Sparse fieldset. A comma-separated subset of `nutrients` fields to return (e.g. `id,name`). Omit to return all fields.","schema":{"type":"string"},"example":"id,name"},{"name":"id","in":"query","required":false,"description":"Filter on `id`. Bare `id=value` means `eq`; the bracket form names the operator — `id[eq]`, `id[in]`. `id[in]` takes a comma-separated list. Rows with no value for the field never match.","schema":{"type":"string"}},{"name":"name","in":"query","required":false,"description":"Filter on `name`. Bare `name=value` means `eq`; the bracket form names the operator — `name[eq]`, `name[in]`. `name[in]` takes a comma-separated list. Rows with no value for the field never match.","schema":{"type":"string"}},{"name":"unit","in":"query","required":false,"description":"Filter on `unit`. Bare `unit=value` means `eq`; the bracket form names the operator — `unit[eq]`, `unit[in]`. `unit[in]` takes a comma-separated list. Rows with no value for the field never match.","schema":{"type":"string"}},{"name":"If-None-Match","in":"header","required":false,"schema":{"type":"string"},"description":"An `ETag` from a previous response for this URL. If it still matches, the API answers `304 Not Modified` with no body; otherwise you get the full `200`. Either way the request is metered.","examples":{"revalidate":{"value":"\"9f2c1b7e4a0d6538bb1c0e2f7a4d9c30\""}}}],"responses":{"200":{"description":"A page of nutrients. `fields[nutrients]` may trim the keys present in each `data` item.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Page_NutrientSchema_"}}},"headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Your account's request quota for the current window."},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests remaining in the current window (floored at 0)."},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the current window resets."},"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Legacy alias for `RateLimit-Limit`."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Legacy alias for `RateLimit-Remaining`."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Legacy alias for the reset time, as a Unix epoch timestamp (seconds)."},"ETag":{"schema":{"type":"string"},"description":"A strong entity-tag for this exact response. Send it back as `If-None-Match` on your next request for the same URL: if nothing has changed you get a `304 Not Modified` with no body instead of the full page. The tag covers everything the response depends on — your tier and market, `include`, `fields`, `sort`, and the page cursor — so it is only ever valid for the request that produced it."}}},"422":{"description":"The request was well-formed but could not be processed: a parameter failed validation, or a value is not one this resource accepts.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"/problems/validation-error","title":"Request validation error","status":422,"errors":[{"type":"int_parsing","loc":["query","page_size"],"msg":"Input should be a valid integer"}],"detail":"Request validation failed.","doc_url":"https://api.noms.sh/docs#tag/validation-error"}}}},"400":{"description":"Malformed request: an invalid query parameter, cursor, filter, sort, page size, include, or sparse-fieldset selection.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"Invalid pagination cursor":{"value":{"type":"/problems/invalid-cursor","title":"Invalid pagination cursor","status":400,"cursor":"bm90LWEtY3Vyc29y","reason":"invalid base64 payload","detail":"Cursor could not be decoded: invalid base64 payload. Follow links.next from a fresh first page rather than constructing a cursor by hand.","doc_url":"https://api.noms.sh/docs#tag/invalid-cursor"}},"Invalid filter":{"value":{"type":"/problems/invalid-filter","title":"Invalid filter","status":400,"field":"name","op":"contains","supported_operators":["eq","in"],"supported_fields":["id","name","barcode"],"detail":"Filter 'name' is invalid: operator 'contains' is not supported for 'name'. Supported operators: eq, in.","doc_url":"https://api.noms.sh/docs#tag/invalid-filter"}},"Invalid sort":{"value":{"type":"/problems/invalid-sort","title":"Invalid sort","status":400,"column":"created_at","supported_columns":["id","name"],"detail":"Sort column 'created_at' is not sortable on this endpoint. Sortable columns: id, name.","doc_url":"https://api.noms.sh/docs#tag/invalid-sort"}},"Invalid page size":{"value":{"type":"/problems/invalid-page-size","title":"Invalid page size","status":400,"requested":0,"minimum":1,"maximum":20,"detail":"page_size=0 is below the minimum of 1","doc_url":"https://api.noms.sh/docs#tag/invalid-page-size"}},"Unknown query parameter":{"value":{"type":"/problems/unknown-query-param","title":"Unknown query parameter","status":400,"param":"page[size]","supported_params":["cursor","page_size","sort"],"suggestion":"page_size","detail":"'page[size]' is not a supported query parameter. Did you mean 'page_size'? Supported parameters: cursor, page_size, sort; plus LHS-bracket filters (e.g. name[eq]).","doc_url":"https://api.noms.sh/docs#tag/unknown-query-param"}},"Invalid sparse-fieldset selection":{"value":{"type":"/problems/invalid-fields","title":"Invalid sparse-fieldset selection","status":400,"resource":"foods","field":"colour","supported_fields":["id","name","barcode"],"detail":"Field 'colour' is not exposed on resource 'foods'. Exposed fields: id, name, barcode.","doc_url":"https://api.noms.sh/docs#tag/invalid-fields"}}}}}},"401":{"description":"Authentication is missing or invalid.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"/problems/unauthorized","title":"Unauthorized","status":401,"detail":"Valid authentication is required to access this resource.","doc_url":"https://api.noms.sh/docs#tag/unauthorized"}}}},"403":{"description":"The request is authenticated but not permitted.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"Bulk enumeration not available on this tier":{"value":{"type":"/problems/enumeration-forbidden","title":"Bulk enumeration not available on this tier","status":403,"detail":"This tier does not allow bulk listing. Add a `q` search query to this request, fetch a single item by id, or upgrade for full enumeration.","doc_url":"https://api.noms.sh/docs#tag/enumeration-forbidden"}},"Result depth limit exceeded":{"value":{"type":"/problems/result-depth-exceeded","title":"Result depth limit exceeded","status":403,"cap":1000,"detail":"This tier returns roughly the first 1000 results of a query. Narrow your search with `q` or filters, or upgrade for unlimited pagination depth.","doc_url":"https://api.noms.sh/docs#tag/result-depth-exceeded"}}}}}},"429":{"description":"The key exceeded a rate limit: either its per-window request quota or its short-window burst rate; retry after the `Retry-After` interval (quota responses also carry `RateLimit` headers).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"Rate limit exceeded":{"value":{"type":"/problems/rate-limited","title":"Rate limit exceeded","status":429,"limit":1,"window":"second","retry_after":1,"detail":"Burst rate limit of 1 requests per second exceeded. Slow down and retry after the Retry-After interval.","doc_url":"https://api.noms.sh/docs#tag/rate-limited"}},"Quota exceeded":{"value":{"type":"/problems/quota-exceeded","title":"Quota exceeded","status":429,"limit":250,"window":"daily","retry_after":3600,"detail":"Request quota of 250 per day exceeded. Retry once the current window resets.","doc_url":"https://api.noms.sh/docs#tag/quota-exceeded"}}}}},"headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Your account's request quota for the current window."},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests remaining in the current window (floored at 0)."},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the current window resets."},"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Legacy alias for `RateLimit-Limit`."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Legacy alias for `RateLimit-Remaining`."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Legacy alias for the reset time, as a Unix epoch timestamp (seconds)."},"Retry-After":{"schema":{"type":"integer"},"description":"Seconds until the quota window resets."}}},"503":{"description":"The service is temporarily unable to handle the request; retry shortly.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"Service unavailable":{"value":{"type":"/problems/service-unavailable","title":"Service unavailable","status":503,"detail":"The service is briefly unavailable; retry shortly.","doc_url":"https://api.noms.sh/docs#tag/service-unavailable"}},"Search unavailable":{"value":{"type":"/problems/search-unavailable","title":"Search unavailable","status":503,"detail":"The search service is temporarily unavailable, so this request could not be answered. Retry shortly. Requests that do not use `q` or a nutrient sort are unaffected.","doc_url":"https://api.noms.sh/docs#tag/search-unavailable"}}}}}},"504":{"description":"The `?q=` search outran its time budget and was cancelled; narrow the query and retry.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"/problems/search-timeout","title":"Search timed out","status":504,"timeout_ms":800,"detail":"The search exceeded its 800ms time limit and was abandoned. Narrow the query — a more specific `q`, or additional filters — and retry.","doc_url":"https://api.noms.sh/docs#tag/search-timeout"}}}},"500":{"description":"An unexpected server error occurred.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"/problems/internal-server-error","title":"Internal server error","status":500,"detail":"An unexpected error occurred while processing the request.","doc_url":"https://api.noms.sh/docs#tag/internal-server-error"}}}},"304":{"description":"Your copy is still current — the `If-None-Match` you sent matches the current entity-tag, so no body is returned. **This still counts against your quota**: the request reached the API, so it is metered and rate-limited exactly like a `200`.","headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Your account's request quota for the current window."},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests remaining in the current window (floored at 0)."},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the current window resets."},"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Legacy alias for `RateLimit-Limit`."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Legacy alias for `RateLimit-Remaining`."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Legacy alias for the reset time, as a Unix epoch timestamp (seconds)."},"ETag":{"schema":{"type":"string"},"description":"A strong entity-tag for this exact response. Send it back as `If-None-Match` on your next request for the same URL: if nothing has changed you get a `304 Not Modified` with no body instead of the full page. The tag covers everything the response depends on — your tier and market, `include`, `fields`, `sort`, and the page cursor — so it is only ever valid for the request that produced it."}}}}}},"/v1/nutrients/{id}":{"get":{"tags":["nutrients"],"summary":"Get a nutrient","description":"Return a single nutrient by its canonical code, or a 404 if unknown.\n\nSupports sparse fieldsets via `fields[nutrients]`. A non-code id (lowercase\nslug, legacy integer) resolves to a clean 404, not a validation error.","operationId":"nutrientsGetNutrient","security":[{"ApiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"Canonical nutrient code (UPPER_SNAKE, e.g. `TOTAL_FAT`).","examples":["TOTAL_FAT"],"title":"Id"},"description":"Canonical nutrient code (UPPER_SNAKE, e.g. `TOTAL_FAT`)."},{"name":"fields[nutrients]","in":"query","required":false,"description":"Sparse fieldset. A comma-separated subset of `nutrients` fields to return (e.g. `id,name`). Omit to return all fields.","schema":{"type":"string"},"example":"id,name"},{"name":"If-None-Match","in":"header","required":false,"schema":{"type":"string"},"description":"An `ETag` from a previous response for this URL. If it still matches, the API answers `304 Not Modified` with no body; otherwise you get the full `200`. Either way the request is metered.","examples":{"revalidate":{"value":"\"9f2c1b7e4a0d6538bb1c0e2f7a4d9c30\""}}}],"responses":{"200":{"description":"A single nutrient. `fields[nutrients]` may trim the keys present in `data`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Item_NutrientSchema_"}}},"headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Your account's request quota for the current window."},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests remaining in the current window (floored at 0)."},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the current window resets."},"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Legacy alias for `RateLimit-Limit`."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Legacy alias for `RateLimit-Remaining`."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Legacy alias for the reset time, as a Unix epoch timestamp (seconds)."},"ETag":{"schema":{"type":"string"},"description":"A strong entity-tag for this exact response. Send it back as `If-None-Match` on your next request for the same URL: if nothing has changed you get a `304 Not Modified` with no body instead of the full page. The tag covers everything the response depends on — your tier and market, `include`, `fields`, `sort`, and the page cursor — so it is only ever valid for the request that produced it."}}},"422":{"description":"The request was well-formed but could not be processed: a parameter failed validation, or a value is not one this resource accepts.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"/problems/validation-error","title":"Request validation error","status":422,"errors":[{"type":"int_parsing","loc":["query","page_size"],"msg":"Input should be a valid integer"}],"detail":"Request validation failed.","doc_url":"https://api.noms.sh/docs#tag/validation-error"}}}},"404":{"description":"No resource matches the requested id.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"/problems/not-found","title":"Not found","status":404,"resource":"foods","id":"abc123def456","detail":"Food with id 'abc123def456' was not found.","doc_url":"https://api.noms.sh/docs#tag/not-found"}}}},"400":{"description":"Malformed request: an invalid query parameter, cursor, filter, sort, page size, include, or sparse-fieldset selection.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"Unknown query parameter":{"value":{"type":"/problems/unknown-query-param","title":"Unknown query parameter","status":400,"param":"page[size]","supported_params":["cursor","page_size","sort"],"suggestion":"page_size","detail":"'page[size]' is not a supported query parameter. Did you mean 'page_size'? Supported parameters: cursor, page_size, sort; plus LHS-bracket filters (e.g. name[eq]).","doc_url":"https://api.noms.sh/docs#tag/unknown-query-param"}},"Invalid sparse-fieldset selection":{"value":{"type":"/problems/invalid-fields","title":"Invalid sparse-fieldset selection","status":400,"resource":"foods","field":"colour","supported_fields":["id","name","barcode"],"detail":"Field 'colour' is not exposed on resource 'foods'. Exposed fields: id, name, barcode.","doc_url":"https://api.noms.sh/docs#tag/invalid-fields"}}}}}},"401":{"description":"Authentication is missing or invalid.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"/problems/unauthorized","title":"Unauthorized","status":401,"detail":"Valid authentication is required to access this resource.","doc_url":"https://api.noms.sh/docs#tag/unauthorized"}}}},"429":{"description":"The key exceeded a rate limit: either its per-window request quota or its short-window burst rate; retry after the `Retry-After` interval (quota responses also carry `RateLimit` headers).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"Rate limit exceeded":{"value":{"type":"/problems/rate-limited","title":"Rate limit exceeded","status":429,"limit":1,"window":"second","retry_after":1,"detail":"Burst rate limit of 1 requests per second exceeded. Slow down and retry after the Retry-After interval.","doc_url":"https://api.noms.sh/docs#tag/rate-limited"}},"Quota exceeded":{"value":{"type":"/problems/quota-exceeded","title":"Quota exceeded","status":429,"limit":250,"window":"daily","retry_after":3600,"detail":"Request quota of 250 per day exceeded. Retry once the current window resets.","doc_url":"https://api.noms.sh/docs#tag/quota-exceeded"}}}}},"headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Your account's request quota for the current window."},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests remaining in the current window (floored at 0)."},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the current window resets."},"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Legacy alias for `RateLimit-Limit`."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Legacy alias for `RateLimit-Remaining`."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Legacy alias for the reset time, as a Unix epoch timestamp (seconds)."},"Retry-After":{"schema":{"type":"integer"},"description":"Seconds until the quota window resets."}}},"503":{"description":"The service is temporarily unable to handle the request; retry shortly.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"/problems/service-unavailable","title":"Service unavailable","status":503,"detail":"The service is briefly unavailable; retry shortly.","doc_url":"https://api.noms.sh/docs#tag/service-unavailable"}}}},"500":{"description":"An unexpected server error occurred.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"/problems/internal-server-error","title":"Internal server error","status":500,"detail":"An unexpected error occurred while processing the request.","doc_url":"https://api.noms.sh/docs#tag/internal-server-error"}}}},"304":{"description":"Your copy is still current — the `If-None-Match` you sent matches the current entity-tag, so no body is returned. **This still counts against your quota**: the request reached the API, so it is metered and rate-limited exactly like a `200`.","headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Your account's request quota for the current window."},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests remaining in the current window (floored at 0)."},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the current window resets."},"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Legacy alias for `RateLimit-Limit`."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Legacy alias for `RateLimit-Remaining`."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Legacy alias for the reset time, as a Unix epoch timestamp (seconds)."},"ETag":{"schema":{"type":"string"},"description":"A strong entity-tag for this exact response. Send it back as `If-None-Match` on your next request for the same URL: if nothing has changed you get a `304 Not Modified` with no body instead of the full page. The tag covers everything the response depends on — your tier and market, `include`, `fields`, `sort`, and the page cursor — so it is only ever valid for the request that produced it."}}}}}},"/v1/food-groups":{"get":{"tags":["foodGroups"],"summary":"List food groups","description":"Return a keyset-paginated page of the canonical food-group catalog.\n\nThe catalog is small and stable, so a single page usually covers it. Supports\nfiltering (`id`, `name`), sorting, sparse fieldsets (`fields[food-groups]`),\nand `?q=`.","operationId":"foodGroupsListFoodGroups","security":[{"ApiKey":[]}],"parameters":[{"name":"page_size","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Number of items to return per page (keyset pagination). Allowed range is 1–20; a value outside it is rejected with a 400.","examples":[20],"title":"Page Size"},"description":"Number of items to return per page (keyset pagination). Allowed range is 1–20; a value outside it is rejected with a 400."},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Opaque keyset-pagination cursor. Don't build it by hand; follow `links.next` from a previous response to fetch the next page.","title":"Cursor"},"description":"Opaque keyset-pagination cursor. Don't build it by hand; follow `links.next` from a previous response to fetch the next page."},{"name":"sort","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Sort key; prefix with `-` for descending (`-name`). Comma-join keys for tie-breakers. Defaults to the resource's natural order.","examples":["-name"],"title":"Sort"},"description":"Sort key; prefix with `-` for descending (`-name`). Comma-join keys for tie-breakers. Defaults to the resource's natural order."},{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Typo-tolerant text search. Filters to matching rows and, when no `sort` is given, orders them by relevance. Composes with filters and pagination.","examples":["chocolat"],"title":"Q"},"description":"Typo-tolerant text search. Filters to matching rows and, when no `sort` is given, orders them by relevance. Composes with filters and pagination."},{"name":"fields[food-groups]","in":"query","required":false,"description":"Sparse fieldset. A comma-separated subset of `food-groups` fields to return (e.g. `id,name`). Omit to return all fields.","schema":{"type":"string"},"example":"id,name"},{"name":"id","in":"query","required":false,"description":"Filter on `id`. Bare `id=value` means `eq`; the bracket form names the operator — `id[eq]`, `id[in]`. `id[in]` takes a comma-separated list. Rows with no value for the field never match.","schema":{"type":"string"}},{"name":"name","in":"query","required":false,"description":"Filter on `name`. Bare `name=value` means `eq`; the bracket form names the operator — `name[eq]`, `name[in]`. `name[in]` takes a comma-separated list. Rows with no value for the field never match.","schema":{"type":"string"}},{"name":"If-None-Match","in":"header","required":false,"schema":{"type":"string"},"description":"An `ETag` from a previous response for this URL. If it still matches, the API answers `304 Not Modified` with no body; otherwise you get the full `200`. Either way the request is metered.","examples":{"revalidate":{"value":"\"9f2c1b7e4a0d6538bb1c0e2f7a4d9c30\""}}}],"responses":{"200":{"description":"A page of food groups. `fields[food-groups]` may trim the keys present in each `data` item.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Page_FoodGroupSchema_"}}},"headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Your account's request quota for the current window."},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests remaining in the current window (floored at 0)."},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the current window resets."},"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Legacy alias for `RateLimit-Limit`."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Legacy alias for `RateLimit-Remaining`."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Legacy alias for the reset time, as a Unix epoch timestamp (seconds)."},"ETag":{"schema":{"type":"string"},"description":"A strong entity-tag for this exact response. Send it back as `If-None-Match` on your next request for the same URL: if nothing has changed you get a `304 Not Modified` with no body instead of the full page. The tag covers everything the response depends on — your tier and market, `include`, `fields`, `sort`, and the page cursor — so it is only ever valid for the request that produced it."}}},"422":{"description":"The request was well-formed but could not be processed: a parameter failed validation, or a value is not one this resource accepts.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"/problems/validation-error","title":"Request validation error","status":422,"errors":[{"type":"int_parsing","loc":["query","page_size"],"msg":"Input should be a valid integer"}],"detail":"Request validation failed.","doc_url":"https://api.noms.sh/docs#tag/validation-error"}}}},"400":{"description":"Malformed request: an invalid query parameter, cursor, filter, sort, page size, include, or sparse-fieldset selection.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"Invalid pagination cursor":{"value":{"type":"/problems/invalid-cursor","title":"Invalid pagination cursor","status":400,"cursor":"bm90LWEtY3Vyc29y","reason":"invalid base64 payload","detail":"Cursor could not be decoded: invalid base64 payload. Follow links.next from a fresh first page rather than constructing a cursor by hand.","doc_url":"https://api.noms.sh/docs#tag/invalid-cursor"}},"Invalid filter":{"value":{"type":"/problems/invalid-filter","title":"Invalid filter","status":400,"field":"name","op":"contains","supported_operators":["eq","in"],"supported_fields":["id","name","barcode"],"detail":"Filter 'name' is invalid: operator 'contains' is not supported for 'name'. Supported operators: eq, in.","doc_url":"https://api.noms.sh/docs#tag/invalid-filter"}},"Invalid sort":{"value":{"type":"/problems/invalid-sort","title":"Invalid sort","status":400,"column":"created_at","supported_columns":["id","name"],"detail":"Sort column 'created_at' is not sortable on this endpoint. Sortable columns: id, name.","doc_url":"https://api.noms.sh/docs#tag/invalid-sort"}},"Invalid page size":{"value":{"type":"/problems/invalid-page-size","title":"Invalid page size","status":400,"requested":0,"minimum":1,"maximum":20,"detail":"page_size=0 is below the minimum of 1","doc_url":"https://api.noms.sh/docs#tag/invalid-page-size"}},"Unknown query parameter":{"value":{"type":"/problems/unknown-query-param","title":"Unknown query parameter","status":400,"param":"page[size]","supported_params":["cursor","page_size","sort"],"suggestion":"page_size","detail":"'page[size]' is not a supported query parameter. Did you mean 'page_size'? Supported parameters: cursor, page_size, sort; plus LHS-bracket filters (e.g. name[eq]).","doc_url":"https://api.noms.sh/docs#tag/unknown-query-param"}},"Invalid sparse-fieldset selection":{"value":{"type":"/problems/invalid-fields","title":"Invalid sparse-fieldset selection","status":400,"resource":"foods","field":"colour","supported_fields":["id","name","barcode"],"detail":"Field 'colour' is not exposed on resource 'foods'. Exposed fields: id, name, barcode.","doc_url":"https://api.noms.sh/docs#tag/invalid-fields"}}}}}},"401":{"description":"Authentication is missing or invalid.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"/problems/unauthorized","title":"Unauthorized","status":401,"detail":"Valid authentication is required to access this resource.","doc_url":"https://api.noms.sh/docs#tag/unauthorized"}}}},"403":{"description":"The request is authenticated but not permitted.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"Bulk enumeration not available on this tier":{"value":{"type":"/problems/enumeration-forbidden","title":"Bulk enumeration not available on this tier","status":403,"detail":"This tier does not allow bulk listing. Add a `q` search query to this request, fetch a single item by id, or upgrade for full enumeration.","doc_url":"https://api.noms.sh/docs#tag/enumeration-forbidden"}},"Result depth limit exceeded":{"value":{"type":"/problems/result-depth-exceeded","title":"Result depth limit exceeded","status":403,"cap":1000,"detail":"This tier returns roughly the first 1000 results of a query. Narrow your search with `q` or filters, or upgrade for unlimited pagination depth.","doc_url":"https://api.noms.sh/docs#tag/result-depth-exceeded"}}}}}},"429":{"description":"The key exceeded a rate limit: either its per-window request quota or its short-window burst rate; retry after the `Retry-After` interval (quota responses also carry `RateLimit` headers).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"Rate limit exceeded":{"value":{"type":"/problems/rate-limited","title":"Rate limit exceeded","status":429,"limit":1,"window":"second","retry_after":1,"detail":"Burst rate limit of 1 requests per second exceeded. Slow down and retry after the Retry-After interval.","doc_url":"https://api.noms.sh/docs#tag/rate-limited"}},"Quota exceeded":{"value":{"type":"/problems/quota-exceeded","title":"Quota exceeded","status":429,"limit":250,"window":"daily","retry_after":3600,"detail":"Request quota of 250 per day exceeded. Retry once the current window resets.","doc_url":"https://api.noms.sh/docs#tag/quota-exceeded"}}}}},"headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Your account's request quota for the current window."},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests remaining in the current window (floored at 0)."},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the current window resets."},"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Legacy alias for `RateLimit-Limit`."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Legacy alias for `RateLimit-Remaining`."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Legacy alias for the reset time, as a Unix epoch timestamp (seconds)."},"Retry-After":{"schema":{"type":"integer"},"description":"Seconds until the quota window resets."}}},"503":{"description":"The service is temporarily unable to handle the request; retry shortly.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"Service unavailable":{"value":{"type":"/problems/service-unavailable","title":"Service unavailable","status":503,"detail":"The service is briefly unavailable; retry shortly.","doc_url":"https://api.noms.sh/docs#tag/service-unavailable"}},"Search unavailable":{"value":{"type":"/problems/search-unavailable","title":"Search unavailable","status":503,"detail":"The search service is temporarily unavailable, so this request could not be answered. Retry shortly. Requests that do not use `q` or a nutrient sort are unaffected.","doc_url":"https://api.noms.sh/docs#tag/search-unavailable"}}}}}},"504":{"description":"The `?q=` search outran its time budget and was cancelled; narrow the query and retry.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"/problems/search-timeout","title":"Search timed out","status":504,"timeout_ms":800,"detail":"The search exceeded its 800ms time limit and was abandoned. Narrow the query — a more specific `q`, or additional filters — and retry.","doc_url":"https://api.noms.sh/docs#tag/search-timeout"}}}},"500":{"description":"An unexpected server error occurred.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"/problems/internal-server-error","title":"Internal server error","status":500,"detail":"An unexpected error occurred while processing the request.","doc_url":"https://api.noms.sh/docs#tag/internal-server-error"}}}},"304":{"description":"Your copy is still current — the `If-None-Match` you sent matches the current entity-tag, so no body is returned. **This still counts against your quota**: the request reached the API, so it is metered and rate-limited exactly like a `200`.","headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Your account's request quota for the current window."},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests remaining in the current window (floored at 0)."},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the current window resets."},"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Legacy alias for `RateLimit-Limit`."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Legacy alias for `RateLimit-Remaining`."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Legacy alias for the reset time, as a Unix epoch timestamp (seconds)."},"ETag":{"schema":{"type":"string"},"description":"A strong entity-tag for this exact response. Send it back as `If-None-Match` on your next request for the same URL: if nothing has changed you get a `304 Not Modified` with no body instead of the full page. The tag covers everything the response depends on — your tier and market, `include`, `fields`, `sort`, and the page cursor — so it is only ever valid for the request that produced it."}}}}}},"/v1/food-groups/{id}":{"get":{"tags":["foodGroups"],"summary":"Get a food group","description":"Return a single food group by its canonical code, or a 404 if unknown.\n\nSupports sparse fieldsets via `fields[food-groups]`.","operationId":"foodGroupsGetFoodGroup","security":[{"ApiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"Canonical food-group code (UPPER_SNAKE, e.g. `DAIRY`).","examples":["DAIRY"],"title":"Id"},"description":"Canonical food-group code (UPPER_SNAKE, e.g. `DAIRY`)."},{"name":"fields[food-groups]","in":"query","required":false,"description":"Sparse fieldset. A comma-separated subset of `food-groups` fields to return (e.g. `id,name`). Omit to return all fields.","schema":{"type":"string"},"example":"id,name"},{"name":"If-None-Match","in":"header","required":false,"schema":{"type":"string"},"description":"An `ETag` from a previous response for this URL. If it still matches, the API answers `304 Not Modified` with no body; otherwise you get the full `200`. Either way the request is metered.","examples":{"revalidate":{"value":"\"9f2c1b7e4a0d6538bb1c0e2f7a4d9c30\""}}}],"responses":{"200":{"description":"A single food group. `fields[food-groups]` may trim the keys present in `data`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Item_FoodGroupSchema_"}}},"headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Your account's request quota for the current window."},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests remaining in the current window (floored at 0)."},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the current window resets."},"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Legacy alias for `RateLimit-Limit`."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Legacy alias for `RateLimit-Remaining`."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Legacy alias for the reset time, as a Unix epoch timestamp (seconds)."},"ETag":{"schema":{"type":"string"},"description":"A strong entity-tag for this exact response. Send it back as `If-None-Match` on your next request for the same URL: if nothing has changed you get a `304 Not Modified` with no body instead of the full page. The tag covers everything the response depends on — your tier and market, `include`, `fields`, `sort`, and the page cursor — so it is only ever valid for the request that produced it."}}},"422":{"description":"The request was well-formed but could not be processed: a parameter failed validation, or a value is not one this resource accepts.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"/problems/validation-error","title":"Request validation error","status":422,"errors":[{"type":"int_parsing","loc":["query","page_size"],"msg":"Input should be a valid integer"}],"detail":"Request validation failed.","doc_url":"https://api.noms.sh/docs#tag/validation-error"}}}},"404":{"description":"No resource matches the requested id.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"/problems/not-found","title":"Not found","status":404,"resource":"foods","id":"abc123def456","detail":"Food with id 'abc123def456' was not found.","doc_url":"https://api.noms.sh/docs#tag/not-found"}}}},"400":{"description":"Malformed request: an invalid query parameter, cursor, filter, sort, page size, include, or sparse-fieldset selection.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"Unknown query parameter":{"value":{"type":"/problems/unknown-query-param","title":"Unknown query parameter","status":400,"param":"page[size]","supported_params":["cursor","page_size","sort"],"suggestion":"page_size","detail":"'page[size]' is not a supported query parameter. Did you mean 'page_size'? Supported parameters: cursor, page_size, sort; plus LHS-bracket filters (e.g. name[eq]).","doc_url":"https://api.noms.sh/docs#tag/unknown-query-param"}},"Invalid sparse-fieldset selection":{"value":{"type":"/problems/invalid-fields","title":"Invalid sparse-fieldset selection","status":400,"resource":"foods","field":"colour","supported_fields":["id","name","barcode"],"detail":"Field 'colour' is not exposed on resource 'foods'. Exposed fields: id, name, barcode.","doc_url":"https://api.noms.sh/docs#tag/invalid-fields"}}}}}},"401":{"description":"Authentication is missing or invalid.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"/problems/unauthorized","title":"Unauthorized","status":401,"detail":"Valid authentication is required to access this resource.","doc_url":"https://api.noms.sh/docs#tag/unauthorized"}}}},"429":{"description":"The key exceeded a rate limit: either its per-window request quota or its short-window burst rate; retry after the `Retry-After` interval (quota responses also carry `RateLimit` headers).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"Rate limit exceeded":{"value":{"type":"/problems/rate-limited","title":"Rate limit exceeded","status":429,"limit":1,"window":"second","retry_after":1,"detail":"Burst rate limit of 1 requests per second exceeded. Slow down and retry after the Retry-After interval.","doc_url":"https://api.noms.sh/docs#tag/rate-limited"}},"Quota exceeded":{"value":{"type":"/problems/quota-exceeded","title":"Quota exceeded","status":429,"limit":250,"window":"daily","retry_after":3600,"detail":"Request quota of 250 per day exceeded. Retry once the current window resets.","doc_url":"https://api.noms.sh/docs#tag/quota-exceeded"}}}}},"headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Your account's request quota for the current window."},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests remaining in the current window (floored at 0)."},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the current window resets."},"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Legacy alias for `RateLimit-Limit`."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Legacy alias for `RateLimit-Remaining`."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Legacy alias for the reset time, as a Unix epoch timestamp (seconds)."},"Retry-After":{"schema":{"type":"integer"},"description":"Seconds until the quota window resets."}}},"503":{"description":"The service is temporarily unable to handle the request; retry shortly.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"/problems/service-unavailable","title":"Service unavailable","status":503,"detail":"The service is briefly unavailable; retry shortly.","doc_url":"https://api.noms.sh/docs#tag/service-unavailable"}}}},"500":{"description":"An unexpected server error occurred.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"/problems/internal-server-error","title":"Internal server error","status":500,"detail":"An unexpected error occurred while processing the request.","doc_url":"https://api.noms.sh/docs#tag/internal-server-error"}}}},"304":{"description":"Your copy is still current — the `If-None-Match` you sent matches the current entity-tag, so no body is returned. **This still counts against your quota**: the request reached the API, so it is metered and rate-limited exactly like a `200`.","headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Your account's request quota for the current window."},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests remaining in the current window (floored at 0)."},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the current window resets."},"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Legacy alias for `RateLimit-Limit`."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Legacy alias for `RateLimit-Remaining`."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Legacy alias for the reset time, as a Unix epoch timestamp (seconds)."},"ETag":{"schema":{"type":"string"},"description":"A strong entity-tag for this exact response. Send it back as `If-None-Match` on your next request for the same URL: if nothing has changed you get a `304 Not Modified` with no body instead of the full page. The tag covers everything the response depends on — your tier and market, `include`, `fields`, `sort`, and the page cursor — so it is only ever valid for the request that produced it."}}}}}},"/v1/brands":{"get":{"tags":["brands"],"summary":"List brands","description":"Return a keyset-paginated page of food brands.\n\nSupports filtering (`id`, `name`), sorting (`sort=id`, `sort=name`), sparse fieldsets\n(`fields[brands]`), and typo-tolerant search (`?q=`).","operationId":"brandsListBrands","security":[{"ApiKey":[]}],"parameters":[{"name":"page_size","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Number of items to return per page (keyset pagination). Allowed range is 1–20; a value outside it is rejected with a 400.","examples":[20],"title":"Page Size"},"description":"Number of items to return per page (keyset pagination). Allowed range is 1–20; a value outside it is rejected with a 400."},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Opaque keyset-pagination cursor. Don't build it by hand; follow `links.next` from a previous response to fetch the next page.","title":"Cursor"},"description":"Opaque keyset-pagination cursor. Don't build it by hand; follow `links.next` from a previous response to fetch the next page."},{"name":"sort","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Sort key; prefix with `-` for descending (`-name`). Comma-join keys for tie-breakers. Defaults to the resource's natural order.","examples":["-name"],"title":"Sort"},"description":"Sort key; prefix with `-` for descending (`-name`). Comma-join keys for tie-breakers. Defaults to the resource's natural order."},{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Typo-tolerant text search. Filters to matching rows and, when no `sort` is given, orders them by relevance. Composes with filters and pagination.","examples":["chocolat"],"title":"Q"},"description":"Typo-tolerant text search. Filters to matching rows and, when no `sort` is given, orders them by relevance. Composes with filters and pagination."},{"name":"fields[brands]","in":"query","required":false,"description":"Sparse fieldset. A comma-separated subset of `brands` fields to return (e.g. `id,name`). Omit to return all fields.","schema":{"type":"string"},"example":"id,name"},{"name":"id","in":"query","required":false,"description":"Filter on `id`. Bare `id=value` means `eq`; the bracket form names the operator — `id[eq]`, `id[in]`. `id[in]` takes a comma-separated list. Rows with no value for the field never match.","schema":{"type":"string"}},{"name":"name","in":"query","required":false,"description":"Filter on `name`. Bare `name=value` means `eq`; the bracket form names the operator — `name[eq]`, `name[in]`. `name[in]` takes a comma-separated list. Rows with no value for the field never match.","schema":{"type":"string"}},{"name":"If-None-Match","in":"header","required":false,"schema":{"type":"string"},"description":"An `ETag` from a previous response for this URL. If it still matches, the API answers `304 Not Modified` with no body; otherwise you get the full `200`. Either way the request is metered.","examples":{"revalidate":{"value":"\"9f2c1b7e4a0d6538bb1c0e2f7a4d9c30\""}}}],"responses":{"200":{"description":"A page of brands. `fields[brands]` may trim the keys present in each `data` item.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Page_BrandDetailSchema_"}}},"headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Your account's request quota for the current window."},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests remaining in the current window (floored at 0)."},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the current window resets."},"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Legacy alias for `RateLimit-Limit`."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Legacy alias for `RateLimit-Remaining`."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Legacy alias for the reset time, as a Unix epoch timestamp (seconds)."},"ETag":{"schema":{"type":"string"},"description":"A strong entity-tag for this exact response. Send it back as `If-None-Match` on your next request for the same URL: if nothing has changed you get a `304 Not Modified` with no body instead of the full page. The tag covers everything the response depends on — your tier and market, `include`, `fields`, `sort`, and the page cursor — so it is only ever valid for the request that produced it."}}},"422":{"description":"The request was well-formed but could not be processed: a parameter failed validation, or a value is not one this resource accepts.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"/problems/validation-error","title":"Request validation error","status":422,"errors":[{"type":"int_parsing","loc":["query","page_size"],"msg":"Input should be a valid integer"}],"detail":"Request validation failed.","doc_url":"https://api.noms.sh/docs#tag/validation-error"}}}},"400":{"description":"Malformed request: an invalid query parameter, cursor, filter, sort, page size, include, or sparse-fieldset selection.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"Invalid pagination cursor":{"value":{"type":"/problems/invalid-cursor","title":"Invalid pagination cursor","status":400,"cursor":"bm90LWEtY3Vyc29y","reason":"invalid base64 payload","detail":"Cursor could not be decoded: invalid base64 payload. Follow links.next from a fresh first page rather than constructing a cursor by hand.","doc_url":"https://api.noms.sh/docs#tag/invalid-cursor"}},"Invalid filter":{"value":{"type":"/problems/invalid-filter","title":"Invalid filter","status":400,"field":"name","op":"contains","supported_operators":["eq","in"],"supported_fields":["id","name","barcode"],"detail":"Filter 'name' is invalid: operator 'contains' is not supported for 'name'. Supported operators: eq, in.","doc_url":"https://api.noms.sh/docs#tag/invalid-filter"}},"Invalid sort":{"value":{"type":"/problems/invalid-sort","title":"Invalid sort","status":400,"column":"created_at","supported_columns":["id","name"],"detail":"Sort column 'created_at' is not sortable on this endpoint. Sortable columns: id, name.","doc_url":"https://api.noms.sh/docs#tag/invalid-sort"}},"Invalid page size":{"value":{"type":"/problems/invalid-page-size","title":"Invalid page size","status":400,"requested":0,"minimum":1,"maximum":20,"detail":"page_size=0 is below the minimum of 1","doc_url":"https://api.noms.sh/docs#tag/invalid-page-size"}},"Unknown query parameter":{"value":{"type":"/problems/unknown-query-param","title":"Unknown query parameter","status":400,"param":"page[size]","supported_params":["cursor","page_size","sort"],"suggestion":"page_size","detail":"'page[size]' is not a supported query parameter. Did you mean 'page_size'? Supported parameters: cursor, page_size, sort; plus LHS-bracket filters (e.g. name[eq]).","doc_url":"https://api.noms.sh/docs#tag/unknown-query-param"}},"Invalid sparse-fieldset selection":{"value":{"type":"/problems/invalid-fields","title":"Invalid sparse-fieldset selection","status":400,"resource":"foods","field":"colour","supported_fields":["id","name","barcode"],"detail":"Field 'colour' is not exposed on resource 'foods'. Exposed fields: id, name, barcode.","doc_url":"https://api.noms.sh/docs#tag/invalid-fields"}}}}}},"401":{"description":"Authentication is missing or invalid.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"/problems/unauthorized","title":"Unauthorized","status":401,"detail":"Valid authentication is required to access this resource.","doc_url":"https://api.noms.sh/docs#tag/unauthorized"}}}},"403":{"description":"The request is authenticated but not permitted.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"Bulk enumeration not available on this tier":{"value":{"type":"/problems/enumeration-forbidden","title":"Bulk enumeration not available on this tier","status":403,"detail":"This tier does not allow bulk listing. Add a `q` search query to this request, fetch a single item by id, or upgrade for full enumeration.","doc_url":"https://api.noms.sh/docs#tag/enumeration-forbidden"}},"Result depth limit exceeded":{"value":{"type":"/problems/result-depth-exceeded","title":"Result depth limit exceeded","status":403,"cap":1000,"detail":"This tier returns roughly the first 1000 results of a query. Narrow your search with `q` or filters, or upgrade for unlimited pagination depth.","doc_url":"https://api.noms.sh/docs#tag/result-depth-exceeded"}}}}}},"429":{"description":"The key exceeded a rate limit: either its per-window request quota or its short-window burst rate; retry after the `Retry-After` interval (quota responses also carry `RateLimit` headers).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"Rate limit exceeded":{"value":{"type":"/problems/rate-limited","title":"Rate limit exceeded","status":429,"limit":1,"window":"second","retry_after":1,"detail":"Burst rate limit of 1 requests per second exceeded. Slow down and retry after the Retry-After interval.","doc_url":"https://api.noms.sh/docs#tag/rate-limited"}},"Quota exceeded":{"value":{"type":"/problems/quota-exceeded","title":"Quota exceeded","status":429,"limit":250,"window":"daily","retry_after":3600,"detail":"Request quota of 250 per day exceeded. Retry once the current window resets.","doc_url":"https://api.noms.sh/docs#tag/quota-exceeded"}}}}},"headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Your account's request quota for the current window."},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests remaining in the current window (floored at 0)."},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the current window resets."},"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Legacy alias for `RateLimit-Limit`."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Legacy alias for `RateLimit-Remaining`."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Legacy alias for the reset time, as a Unix epoch timestamp (seconds)."},"Retry-After":{"schema":{"type":"integer"},"description":"Seconds until the quota window resets."}}},"503":{"description":"The service is temporarily unable to handle the request; retry shortly.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"Service unavailable":{"value":{"type":"/problems/service-unavailable","title":"Service unavailable","status":503,"detail":"The service is briefly unavailable; retry shortly.","doc_url":"https://api.noms.sh/docs#tag/service-unavailable"}},"Search unavailable":{"value":{"type":"/problems/search-unavailable","title":"Search unavailable","status":503,"detail":"The search service is temporarily unavailable, so this request could not be answered. Retry shortly. Requests that do not use `q` or a nutrient sort are unaffected.","doc_url":"https://api.noms.sh/docs#tag/search-unavailable"}}}}}},"504":{"description":"The `?q=` search outran its time budget and was cancelled; narrow the query and retry.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"/problems/search-timeout","title":"Search timed out","status":504,"timeout_ms":800,"detail":"The search exceeded its 800ms time limit and was abandoned. Narrow the query — a more specific `q`, or additional filters — and retry.","doc_url":"https://api.noms.sh/docs#tag/search-timeout"}}}},"500":{"description":"An unexpected server error occurred.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"/problems/internal-server-error","title":"Internal server error","status":500,"detail":"An unexpected error occurred while processing the request.","doc_url":"https://api.noms.sh/docs#tag/internal-server-error"}}}},"304":{"description":"Your copy is still current — the `If-None-Match` you sent matches the current entity-tag, so no body is returned. **This still counts against your quota**: the request reached the API, so it is metered and rate-limited exactly like a `200`.","headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Your account's request quota for the current window."},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests remaining in the current window (floored at 0)."},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the current window resets."},"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Legacy alias for `RateLimit-Limit`."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Legacy alias for `RateLimit-Remaining`."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Legacy alias for the reset time, as a Unix epoch timestamp (seconds)."},"ETag":{"schema":{"type":"string"},"description":"A strong entity-tag for this exact response. Send it back as `If-None-Match` on your next request for the same URL: if nothing has changed you get a `304 Not Modified` with no body instead of the full page. The tag covers everything the response depends on — your tier and market, `include`, `fields`, `sort`, and the page cursor — so it is only ever valid for the request that produced it."}}}}}},"/v1/brands/{id}":{"get":{"tags":["brands"],"summary":"Get a brand","description":"Return a single brand by its short-id, or a 404 if no brand has that id.\n\nSupports sparse fieldsets via `fields[brands]`. A **retired** id (one merged\ninto another brand) forwards to the surviving brand at `200` with an added\n`canonical_id` field, so a stored id never dead-ends after a merge.","operationId":"brandsGetBrand","security":[{"ApiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"Brand short-id (12-char base62).","examples":["XoluRyOx9o3C"],"title":"Id"},"description":"Brand short-id (12-char base62)."},{"name":"fields[brands]","in":"query","required":false,"description":"Sparse fieldset. A comma-separated subset of `brands` fields to return (e.g. `id,name`). Omit to return all fields.","schema":{"type":"string"},"example":"id,name"},{"name":"If-None-Match","in":"header","required":false,"schema":{"type":"string"},"description":"An `ETag` from a previous response for this URL. If it still matches, the API answers `304 Not Modified` with no body; otherwise you get the full `200`. Either way the request is metered.","examples":{"revalidate":{"value":"\"9f2c1b7e4a0d6538bb1c0e2f7a4d9c30\""}}}],"responses":{"200":{"description":"A single brand. `fields[brands]` may trim the keys present in `data`. If the requested id was **retired** by a merge, the canonical brand is returned with an extra `canonical_id` field naming its live id — update any stored reference to it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Item_BrandDetailSchema_"}}},"headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Your account's request quota for the current window."},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests remaining in the current window (floored at 0)."},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the current window resets."},"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Legacy alias for `RateLimit-Limit`."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Legacy alias for `RateLimit-Remaining`."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Legacy alias for the reset time, as a Unix epoch timestamp (seconds)."},"ETag":{"schema":{"type":"string"},"description":"A strong entity-tag for this exact response. Send it back as `If-None-Match` on your next request for the same URL: if nothing has changed you get a `304 Not Modified` with no body instead of the full page. The tag covers everything the response depends on — your tier and market, `include`, `fields`, `sort`, and the page cursor — so it is only ever valid for the request that produced it."}}},"422":{"description":"The request was well-formed but could not be processed: a parameter failed validation, or a value is not one this resource accepts.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"/problems/validation-error","title":"Request validation error","status":422,"errors":[{"type":"int_parsing","loc":["query","page_size"],"msg":"Input should be a valid integer"}],"detail":"Request validation failed.","doc_url":"https://api.noms.sh/docs#tag/validation-error"}}}},"404":{"description":"No resource matches the requested id.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"/problems/not-found","title":"Not found","status":404,"resource":"foods","id":"abc123def456","detail":"Food with id 'abc123def456' was not found.","doc_url":"https://api.noms.sh/docs#tag/not-found"}}}},"400":{"description":"Malformed request: an invalid query parameter, cursor, filter, sort, page size, include, or sparse-fieldset selection.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"Unknown query parameter":{"value":{"type":"/problems/unknown-query-param","title":"Unknown query parameter","status":400,"param":"page[size]","supported_params":["cursor","page_size","sort"],"suggestion":"page_size","detail":"'page[size]' is not a supported query parameter. Did you mean 'page_size'? Supported parameters: cursor, page_size, sort; plus LHS-bracket filters (e.g. name[eq]).","doc_url":"https://api.noms.sh/docs#tag/unknown-query-param"}},"Invalid sparse-fieldset selection":{"value":{"type":"/problems/invalid-fields","title":"Invalid sparse-fieldset selection","status":400,"resource":"foods","field":"colour","supported_fields":["id","name","barcode"],"detail":"Field 'colour' is not exposed on resource 'foods'. Exposed fields: id, name, barcode.","doc_url":"https://api.noms.sh/docs#tag/invalid-fields"}}}}}},"401":{"description":"Authentication is missing or invalid.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"/problems/unauthorized","title":"Unauthorized","status":401,"detail":"Valid authentication is required to access this resource.","doc_url":"https://api.noms.sh/docs#tag/unauthorized"}}}},"429":{"description":"The key exceeded a rate limit: either its per-window request quota or its short-window burst rate; retry after the `Retry-After` interval (quota responses also carry `RateLimit` headers).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"Rate limit exceeded":{"value":{"type":"/problems/rate-limited","title":"Rate limit exceeded","status":429,"limit":1,"window":"second","retry_after":1,"detail":"Burst rate limit of 1 requests per second exceeded. Slow down and retry after the Retry-After interval.","doc_url":"https://api.noms.sh/docs#tag/rate-limited"}},"Quota exceeded":{"value":{"type":"/problems/quota-exceeded","title":"Quota exceeded","status":429,"limit":250,"window":"daily","retry_after":3600,"detail":"Request quota of 250 per day exceeded. Retry once the current window resets.","doc_url":"https://api.noms.sh/docs#tag/quota-exceeded"}}}}},"headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Your account's request quota for the current window."},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests remaining in the current window (floored at 0)."},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the current window resets."},"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Legacy alias for `RateLimit-Limit`."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Legacy alias for `RateLimit-Remaining`."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Legacy alias for the reset time, as a Unix epoch timestamp (seconds)."},"Retry-After":{"schema":{"type":"integer"},"description":"Seconds until the quota window resets."}}},"503":{"description":"The service is temporarily unable to handle the request; retry shortly.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"/problems/service-unavailable","title":"Service unavailable","status":503,"detail":"The service is briefly unavailable; retry shortly.","doc_url":"https://api.noms.sh/docs#tag/service-unavailable"}}}},"500":{"description":"An unexpected server error occurred.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"/problems/internal-server-error","title":"Internal server error","status":500,"detail":"An unexpected error occurred while processing the request.","doc_url":"https://api.noms.sh/docs#tag/internal-server-error"}}}},"304":{"description":"Your copy is still current — the `If-None-Match` you sent matches the current entity-tag, so no body is returned. **This still counts against your quota**: the request reached the API, so it is metered and rate-limited exactly like a `200`.","headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Your account's request quota for the current window."},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests remaining in the current window (floored at 0)."},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the current window resets."},"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Legacy alias for `RateLimit-Limit`."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Legacy alias for `RateLimit-Remaining`."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Legacy alias for the reset time, as a Unix epoch timestamp (seconds)."},"ETag":{"schema":{"type":"string"},"description":"A strong entity-tag for this exact response. Send it back as `If-None-Match` on your next request for the same URL: if nothing has changed you get a `304 Not Modified` with no body instead of the full page. The tag covers everything the response depends on — your tier and market, `include`, `fields`, `sort`, and the page cursor — so it is only ever valid for the request that produced it."}}}}}},"/v1/usage":{"get":{"tags":["usage"],"summary":"Get your usage and quota","description":"Return the calling account's current-window usage against its quota.\n\nReports the **account** total — the sum across every API key you own, which\nis the number quota enforcement actually compares against. Holding\nseveral keys does not raise the allowance; they share one. The same numbers\nback the `RateLimit-*` headers on every metered response, so this endpoint and\na `429` can never disagree.\n\nAn account with no requests yet in the window reports `request_count: 0`.\nThis read is itself exempt from metering, so calling it does not consume\nquota — it stays reachable even while you are over cap.","operationId":"usageUsage","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsageResponse"}}}},"401":{"description":"Authentication is missing or invalid.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"/problems/unauthorized","title":"Unauthorized","status":401,"detail":"Valid authentication is required to access this resource.","doc_url":"https://api.noms.sh/docs#tag/unauthorized"}}}},"503":{"description":"The service is temporarily unable to handle the request; retry shortly.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"/problems/service-unavailable","title":"Service unavailable","status":503,"detail":"The service is briefly unavailable; retry shortly.","doc_url":"https://api.noms.sh/docs#tag/service-unavailable"}}}}},"security":[{"ApiKey":[]}]}},"/v1/market":{"get":{"tags":["market"],"summary":"Get your market selection","description":"Return the account's chosen market and the markets it may choose from.\n\n`chosen` is `null` until a single-market account sets one, and always `null` on\na tier that serves all markets — that case carries a `note` explaining how to\nnarrow a request instead. `available` lists every market foods exist in.\nReading this never consumes quota.","operationId":"marketGetMarket","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MarketResponse"}}}},"401":{"description":"Authentication is missing or invalid.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"/problems/unauthorized","title":"Unauthorized","status":401,"detail":"Valid authentication is required to access this resource.","doc_url":"https://api.noms.sh/docs#tag/unauthorized"}}}},"503":{"description":"The service is temporarily unable to handle the request; retry shortly.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"/problems/service-unavailable","title":"Service unavailable","status":503,"detail":"The service is briefly unavailable; retry shortly.","doc_url":"https://api.noms.sh/docs#tag/service-unavailable"}}}}},"security":[{"ApiKey":[]}]},"put":{"tags":["market"],"summary":"Set your market selection","description":"Set (or change) the account's market, then return the new selection.\n\nThe change applies to **every API key this account owns**. The server that handles\nthe write serves the new market straight away; others may serve the previous\nselection for up to a minute, so a request made right after the change can still\nreflect the old one. It settles on its own within that minute — wait it out rather\nthan retrying in a loop. Only single-market plans may set a\nmarket; a plan that already serves all markets gets a 409. The chosen market must\nbe one a food is actually available in, else a 422.","operationId":"marketSetMarket","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MarketChoice"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MarketResponse"}}}},"401":{"description":"Authentication is missing or invalid.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"/problems/unauthorized","title":"Unauthorized","status":401,"detail":"Valid authentication is required to access this resource.","doc_url":"https://api.noms.sh/docs#tag/unauthorized"}}}},"422":{"description":"The request was well-formed but could not be processed: a parameter failed validation, or a value is not one this resource accepts.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"/problems/unknown-market","title":"Unknown market","status":422,"market":"ZZ","available":["CL","US"],"detail":"No foods are available in market 'ZZ'. Choose one of the available markets (see `available`, or GET /v1/market).","doc_url":"https://api.noms.sh/docs#tag/unknown-market"}}}},"409":{"description":"The request conflicts with the current state of the resource.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"Market selection not applicable to this tier":{"value":{"type":"/problems/market-not-applicable","title":"Market selection not applicable to this tier","status":409,"detail":"Your plan already serves all markets, so there is no single market to set. Narrow an individual request with the `market_countries` filter instead.","doc_url":"https://api.noms.sh/docs#tag/market-not-applicable"}},"No account to set a market on":{"value":{"type":"/problems/market-account-required","title":"No account to set a market on","status":409,"detail":"A market is an account setting, and this credential has no account to set it on. Sign in to the dashboard, or use a key that belongs to a registered account.","doc_url":"https://api.noms.sh/docs#tag/market-account-required"}}}}}},"503":{"description":"The service is temporarily unable to handle the request; retry shortly.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"/problems/service-unavailable","title":"Service unavailable","status":503,"detail":"The service is briefly unavailable; retry shortly.","doc_url":"https://api.noms.sh/docs#tag/service-unavailable"}}}}},"security":[{"ApiKey":[]}]}}},"components":{"schemas":{"BrandDetailSchema":{"properties":{"id":{"type":"string","title":"Id","description":"Stable 12-char base62 short-id for the brand name; the `/v1/brands/{id}` handle."},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name","description":"Brand name as it appears on packaging (e.g. `Blue Diamond`); `null` when unknown."},"canonical_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Canonical Id","description":"Present **only** when the requested id was retired by a merge: the live id this brand now has. Update any stored reference to it."}},"type":"object","required":["id","name"],"title":"BrandDetailSchema","description":"A brand item response.\n\nIdentical to the list brand resource plus the optional `canonical_id`, which\nis present only when the requested brand id was retired by a merge and\nforwarded to a surviving brand. Update any stored reference to the id it\ncarries.","examples":[{"id":"XoluRyOx9o3C","name":"Blue Diamond"}]},"BrandSchema":{"properties":{"id":{"type":"string","title":"Id","description":"Stable 12-char base62 short-id for the brand name; the `/v1/brands/{id}` handle."},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name","description":"Brand name as it appears on packaging (e.g. `Blue Diamond`); `null` when unknown."}},"type":"object","required":["id","name"],"title":"BrandSchema","description":"A food brand: a stable short-id and the brand name.","examples":[{"id":"XoluRyOx9o3C","name":"Blue Diamond"}]},"FoodDetailSchema":{"properties":{"id":{"type":"string","title":"Id","description":"Deterministic 12-char base62 short-id; the `/v1/foods/{id}` handle."},"barcode":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Barcode","description":"Product barcode when known; `null` otherwise. Returned as a **14-digit GTIN**, zero-padded on the left. Filtering is normalized for you: the 12-digit UPC or 13-digit EAN printed on a pack resolves the same food as its padded form."},"name":{"type":"string","title":"Name","description":"Food name."},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description","description":"Free-text description; `null` when none."},"scientific_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Scientific Name","description":"Scientific (binomial) name for whole foods; `null` when not applicable."},"country_of_origin":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Country Of Origin","description":"ISO 3166-1 alpha-2 country of origin (uppercase); `null` when unknown."},"ingredients_text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ingredients Text","description":"Raw ingredients statement preserved from the source; `null` when none."},"is_foundational":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Foundational","description":"Whether this is a *foundational* food — an unbranded, whole / single-ingredient food (e.g. raw chicken, an apple). Tri-state: `true` = unbranded whole food; `false` = branded or otherwise not foundational; `null` = undetermined for this source. Note: filtering `is_foundational[eq]=false` matches only explicitly `false` rows and excludes `null` ones."},"basis_unit":{"type":"string","enum":["grams","milliliters"],"title":"Basis Unit","description":"What this food's nutrient values are measured against — `grams` or `milliliters`. Every `nutrients[].value` is the amount per **100** of this unit, and the name matches the serving field to divide by, so `value * serving[basis_unit] / 100` is the amount in that serving. Solids are `grams`; drinks a source reports by volume are `milliliters`. Never `null`."},"brand_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Brand Id","description":"Short-id of the food's brand; `null` for unbranded foods. Expand via `include=brand`."},"food_group_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Food Group Id","description":"Code of the food's group; `null` when unclassified. Expand via `include=food_group`."},"canonical_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Canonical Id","description":"Present **only** when the requested id was retired by a merge: the live id this food now has. Update any stored reference to it."},"brand":{"anyOf":[{"$ref":"#/components/schemas/BrandSchema"},{"type":"null"}],"description":"The food's brand. Present only with `include=brand`."},"food_group":{"anyOf":[{"$ref":"#/components/schemas/FoodGroupSchema"},{"type":"null"}],"description":"The food's group. Present only with `include=food_group`."},"nutrients":{"anyOf":[{"items":{"$ref":"#/components/schemas/FoodNutrientSchema"},"type":"array"},{"type":"null"}],"title":"Nutrients","description":"Per-100 g/ml nutrient measurements. Present only with `include=nutrients`."},"serving_sizes":{"anyOf":[{"items":{"$ref":"#/components/schemas/FoodServingSizeSchema"},"type":"array"},{"type":"null"}],"title":"Serving Sizes","description":"Defined servings for the food. Present only with `include=serving_sizes`."},"images":{"anyOf":[{"items":{"$ref":"#/components/schemas/FoodImageSchema"},"type":"array"},{"type":"null"}],"title":"Images","description":"Food images in display order. Present only with `include=images`."},"market_countries":{"anyOf":[{"items":{"$ref":"#/components/schemas/FoodMarketCountrySchema"},"type":"array"},{"type":"null"}],"title":"Market Countries","description":"Countries the food is sold in. Present only with `include=market_countries`."}},"type":"object","required":["id","barcode","name","description","scientific_name","country_of_origin","ingredients_text","is_foundational","basis_unit","brand_id","food_group_id"],"title":"FoodDetailSchema","description":"A food **with its includable relations**.\n\nThe flat food resource is always present; each relation below appears in the\n``data`` object only when the client requests it via ``include=``\n(e.g. ``?include=brand,nutrients``), so every relation is optional and\nabsent by default. The keys present are exactly those the client selected\nvia ``include=`` / ``fields[...]``.","examples":[{"barcode":"00041570110645","basis_unit":"grams","brand":{"id":"XoluRyOx9o3C","name":"Blue Diamond"},"brand_id":"XoluRyOx9o3C","food_group":{"icon_url":"https://images.noms.sh/60f440137489aed9240588a68ffebf455102b257021bfa1cde4d6f6b0b28ac6e","id":"NUTS_AND_SEEDS","name":"Nuts & Seeds"},"food_group_id":"NUTS_AND_SEEDS","id":"TIgbNPnzCIjX","images":[{"type":"photo","url":"https://images.noms.sh/60f440137489aed9240588a68ffebf455102b257021bfa1cde4d6f6b0b28ac6e"}],"ingredients_text":"Almonds, sunflower oil, sea salt.","is_foundational":false,"market_countries":[{"country_code":"US"}],"name":"Roasted salted almonds","nutrients":[{"id":"PROTEIN","name":"Protein","unit":"g","value":"21.428571"}],"serving_sizes":[{"grams":"28.0","is_default":true,"quantity":"28.0","unit":"g"}]}]},"FoodGroupSchema":{"properties":{"id":{"type":"string","title":"Id","description":"Stable canonical UPPER_SNAKE code and the `/v1/food-groups/{id}` handle (e.g. `DAIRY`)."},"name":{"type":"string","title":"Name","description":"Human-readable display label (e.g. `Dairy`). Searchable, sortable, and filterable."},"icon_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Icon Url","description":"Public URL of the group's icon (served from the Noms image CDN); `null` when the group has no icon available. A food in this group gets this icon appended as a fallback image (with `type: icon`) only when it has no real images of its own."}},"type":"object","required":["id","name","icon_url"],"title":"FoodGroupSchema","description":"A canonical food group — a stable code plus a curated display name.","examples":[{"icon_url":"https://images.noms.sh/dairy-icon-hash","id":"DAIRY","name":"Dairy"}]},"FoodImageSchema":{"properties":{"url":{"type":"string","title":"Url","description":"Public image URL (served from the Noms image CDN). Array order conveys display sequence."},"type":{"type":"string","enum":["photo","icon"],"title":"Type","description":"Image category: `photo` (believed to be an actual photo of the food) or `icon` (a generic representation — a source icon or the food's food-group icon). Lets a consumer separate real photos from generic fallbacks."}},"type":"object","required":["url","type"],"title":"FoodImageSchema","description":"One image for a food. Images arrive in display order, so the array\nindex conveys sequence.\n\n``type`` categorizes the image so a consumer can separate believed-real food\nphotos from generic representations: ``photo`` is a photo believed to show the\nactual food; ``icon`` is a generic stand-in (a source icon, or the food's\nfood-group icon, appended as a fallback only when the food has no real images\nof its own).","examples":[{"type":"photo","url":"https://images.noms.sh/60f440137489aed9240588a68ffebf455102b257021bfa1cde4d6f6b0b28ac6e"}]},"FoodMarketCountrySchema":{"properties":{"country_code":{"type":"string","title":"Country Code","description":"ISO 3166-1 alpha-2 country code, uppercase (e.g. `CL`)."}},"type":"object","required":["country_code"],"title":"FoodMarketCountrySchema","description":"One country a food is sold in, as an ISO 3166-1 alpha-2 code.","examples":[{"country_code":"CL"}]},"FoodNutrientSchema":{"properties":{"id":{"type":"string","title":"Id","description":"Nutrient code (e.g. `PROTEIN`); matches the standalone `/v1/nutrients` id."},"name":{"type":"string","title":"Name","description":"Nutrient display name; trimmable via `fields[nutrients]`."},"value":{"type":"string","format":"decimal","title":"Value","description":"Amount of this nutrient per **100** of the food's `basis_unit` (`grams` or `milliliters`). Multiply by `serving[basis_unit] / 100` to get the amount in that serving. Serialized as a string to preserve decimal precision."},"unit":{"anyOf":[{"type":"string","enum":["g","mg","mcg","kcal"]},{"type":"null"}],"title":"Unit","description":"Measurement unit `{g, mg, mcg, kcal}`, or `null` for a dimensionless nutrient."}},"type":"object","required":["id","name","value","unit"],"title":"FoodNutrientSchema","description":"One nutrient measurement attached to a food, shaped ``{id, name,\nvalue, unit}``. ``id`` is the nutrient's own code (e.g. ``PROTEIN``) and\n``name`` its display name, making the embedded object symmetric with the\nstandalone ``/v1/nutrients`` resource and the ``brand`` / ``food_group``\nembeds; ``value`` is the measurement specific to this food, expressed\n**per 100** of the food's ``basis_unit`` (directly comparable across foods;\nmultiply by ``serving[basis_unit] / 100`` to get the amount in that\nserving). ``unit`` is one of the closed set ``{g, mg, mcg, kcal}``, or\n``null`` for a dimensionless nutrient. ``name`` is trimmable via\n``fields[nutrients]``.","examples":[{"id":"PROTEIN","name":"Protein","unit":"g","value":"3.2000"}]},"FoodServingSizeSchema":{"properties":{"unit":{"type":"string","enum":["g","ml","cup","tbsp","tsp","L","oz","fl_oz","each"],"title":"Unit","description":"Serving unit. One of the closed 9-value set `{g, ml, cup, tbsp, tsp, L, oz, fl_oz, each}`; never null."},"descriptor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Descriptor","description":"Free-text qualifier preserved from the source (e.g. `chopped`); `null` when the unit fully describes the serving."},"quantity":{"type":"string","format":"decimal","title":"Quantity","description":"Count of `unit`s in one serving (e.g. `1` cup, `30` g). String-encoded decimal."},"grams":{"anyOf":[{"type":"string","format":"decimal"},{"type":"null"}],"title":"Grams","description":"Absolute weight of this serving in grams — a real mass, never a volume. Populated for a food whose `basis_unit` is `grams`; `null` for a volume-anchored (`milliliters`-basis) food unless the source gave a genuine mass, since deriving a mass from a volume needs the food's density, which we do not guess. String-encoded decimal."},"milliliters":{"anyOf":[{"type":"string","format":"decimal"},{"type":"null"}],"title":"Milliliters","description":"Absolute volume of this serving in millilitres, when knowable; `null` otherwise. Populated for a food whose `basis_unit` is `milliliters`, and for any serving measured in a volume unit (`1 cup` is 236.588 ml by definition). `null` for a solid measured only by weight — deriving a volume from a mass needs the food's density, which we do not guess. String-encoded decimal."},"is_default":{"type":"boolean","title":"Is Default","description":"Whether this is the food's primary reference serving (exactly one serving per food is the default)."}},"type":"object","required":["unit","descriptor","quantity","grams","milliliters","is_default"],"title":"FoodServingSizeSchema","description":"One serving definition, shaped ``{unit, descriptor, quantity, grams,\nmilliliters, is_default}``. ``quantity`` is the count of ``unit``s in one\nserving (e.g. ``1`` cup, ``30`` g); ``grams`` and ``milliliters`` are that\nserving's absolute size in each dimension. Multiply a nutrient's per-100\n``value`` by ``serving[food.basis_unit] / 100`` to get its amount in this\nserving — the field the food's ``basis_unit`` names is **never ``null``**, so\nthat lookup always resolves; the other dimension is best-effort and may be\n``null`` (a volume-anchored food has no per-serving mass, a mass-only solid no\nvolume — neither dimension is invented from the other). ``is_default`` marks\nthe food's primary (reference) serving; exactly one serving per food is the\ndefault. ``descriptor`` is a free-text qualifier preserved from the source\n(e.g. ``chopped``, ``bar, each 2.4 oz``); null when the canonical unit\nalready fully describes the serving. ``unit`` is one of the closed 9-value\nserving set and is never null; it is ``each`` when the source names no\nunit.","examples":[{"descriptor":"chopped","grams":"170","is_default":true,"milliliters":"236.5882","quantity":"1","unit":"cup"},{"is_default":true,"milliliters":"250","quantity":"250","unit":"ml"}]},"Item_BrandDetailSchema_":{"properties":{"data":{"$ref":"#/components/schemas/BrandDetailSchema"}},"type":"object","required":["data"],"title":"Item[BrandDetailSchema]"},"Item_FoodDetailSchema_":{"properties":{"data":{"$ref":"#/components/schemas/FoodDetailSchema"}},"type":"object","required":["data"],"title":"Item[FoodDetailSchema]"},"Item_FoodGroupSchema_":{"properties":{"data":{"$ref":"#/components/schemas/FoodGroupSchema"}},"type":"object","required":["data"],"title":"Item[FoodGroupSchema]"},"Item_NutrientSchema_":{"properties":{"data":{"$ref":"#/components/schemas/NutrientSchema"}},"type":"object","required":["data"],"title":"Item[NutrientSchema]"},"MarketChoice":{"properties":{"market":{"type":"string","minLength":1,"title":"Market","description":"The market (country code) to scope this account to.","examples":["CL"]}},"type":"object","required":["market"],"title":"MarketChoice","description":"The market to scope the account to — one of `GET /v1/market`'s `available`."},"MarketData":{"properties":{"chosen":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Chosen","description":"The market this account is scoped to (a country code), or `null` — either because a single-market tier has not chosen yet, or because the tier serves all markets (see `note`). Every API key the account owns reads the same value."},"available":{"items":{"type":"string"},"type":"array","title":"Available","description":"Every market foods are available in — the valid choices for `PUT`."},"note":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Note","description":"Why `chosen` is `null` when it can never be set — present only on a tier that already serves every market. `null` on a single-market tier."}},"type":"object","required":["chosen","available"],"title":"MarketData","description":"The account's market selection and the markets it may choose from."},"MarketResponse":{"properties":{"data":{"$ref":"#/components/schemas/MarketData"}},"type":"object","required":["data"],"title":"MarketResponse","examples":[{"data":{"available":["AR","CL","MX","US"],"chosen":"CL"}}]},"NutrientSchema":{"properties":{"id":{"type":"string","title":"Id","description":"Stable UPPER_SNAKE nutrient code (e.g. `ENERGY`, `PROTEIN`); the canonical identity and the `/v1/nutrients/{id}` handle."},"name":{"type":"string","title":"Name","description":"Human-readable nutrient name (e.g. `Protein`)."},"unit":{"anyOf":[{"type":"string","enum":["g","mg","mcg","kcal"]},{"type":"null"}],"title":"Unit","description":"Measurement unit — one of the closed canonical set `{g, mg, mcg, kcal}`, or `null` for a declared-dimensionless nutrient (e.g. Glycemic Load, Specific Gravity)."},"parent_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Parent Id","description":"Code of this nutrient's parent in the hierarchy (e.g. Saturated fat → `TOTAL_FAT`), or `null` for a top-level node. Exposed for hierarchy walking; not filterable or sortable."}},"type":"object","required":["id","name","unit","parent_id"],"title":"NutrientSchema","description":"A canonical nutrient in the catalog, with its measurement unit and\noptional position in the nutrient hierarchy.","examples":[{"id":"SATURATED_FAT","name":"Saturated fat","parent_id":"TOTAL_FAT","unit":"g"},{"id":"GLYCEMIC_LOAD","name":"Glycemic load"}]},"PageLinks":{"properties":{"self":{"type":"string","maxLength":2083,"minLength":1,"format":"uri","title":"Self","description":"Canonical URL of the current page, echoing the request's query parameters."},"next":{"anyOf":[{"type":"string","maxLength":2083,"minLength":1,"format":"uri"},{"type":"null"}],"title":"Next","description":"URL of the next page, or ``null`` when this is the last page. The ``cursor`` query parameter is advanced to the next keyset position; all other query parameters are preserved verbatim."}},"type":"object","required":["self"],"title":"PageLinks","examples":[{"next":"https://api.noms.sh/v1/foods?page_size=2&cursor=eyJ2IjpbeyJ0IjoicmF3IiwidiI6Ik1pbGsifV0sInMiOiIrbmFtZSJ9","self":"https://api.noms.sh/v1/foods?page_size=2"}]},"PageMeta":{"properties":{"has_more":{"type":"boolean","title":"Has More","description":"Whether more pages follow this one (equivalently, whether ``links.next`` is non-null)."},"page_size":{"type":"integer","minimum":1.0,"title":"Page Size","description":"Number of items requested per page: the effective ``page_size`` (the requested value, or the default when ``page_size`` is omitted)."}},"type":"object","required":["has_more","page_size"],"title":"PageMeta","examples":[{"has_more":true,"page_size":10}]},"Page_BrandDetailSchema_":{"properties":{"data":{"items":{"$ref":"#/components/schemas/BrandDetailSchema"},"type":"array","title":"Data"},"links":{"$ref":"#/components/schemas/PageLinks"},"meta":{"$ref":"#/components/schemas/PageMeta"}},"type":"object","required":["data","links","meta"],"title":"Page[BrandDetailSchema]"},"Page_FoodDetailSchema_":{"properties":{"data":{"items":{"$ref":"#/components/schemas/FoodDetailSchema"},"type":"array","title":"Data"},"links":{"$ref":"#/components/schemas/PageLinks"},"meta":{"$ref":"#/components/schemas/PageMeta"}},"type":"object","required":["data","links","meta"],"title":"Page[FoodDetailSchema]"},"Page_FoodGroupSchema_":{"properties":{"data":{"items":{"$ref":"#/components/schemas/FoodGroupSchema"},"type":"array","title":"Data"},"links":{"$ref":"#/components/schemas/PageLinks"},"meta":{"$ref":"#/components/schemas/PageMeta"}},"type":"object","required":["data","links","meta"],"title":"Page[FoodGroupSchema]"},"Page_NutrientSchema_":{"properties":{"data":{"items":{"$ref":"#/components/schemas/NutrientSchema"},"type":"array","title":"Data"},"links":{"$ref":"#/components/schemas/PageLinks"},"meta":{"$ref":"#/components/schemas/PageMeta"}},"type":"object","required":["data","links","meta"],"title":"Page[NutrientSchema]"},"QuotaWindow":{"type":"string","enum":["daily","monthly"],"title":"QuotaWindow","description":"The window over which the **account's** request quota is measured.\n\n``daily`` is a rolling 24-hour window. ``monthly`` is the account's billing\nperiod, starting from its renewal date."},"Tier":{"type":"string","enum":["taster","sous_chef","head_chef"],"title":"Tier","description":"The API access tier assigned to this **account**.\n\nOne of ``taster`` (free), ``sous_chef`` (paid), or ``head_chef`` (paid)."},"UsageData":{"properties":{"tier":{"$ref":"#/components/schemas/Tier","description":"The account's access tier."},"quota_window":{"$ref":"#/components/schemas/QuotaWindow","description":"The window the quota is measured over: `daily` (a rolling 24 hours) for the free tier, `monthly` (your billing period) for paid."},"request_quota":{"type":"integer","title":"Request Quota","description":"The maximum number of requests allowed in the window, across every API key on your account. Normally your tier's allowance; if support has granted your account a custom one, this reports that instead — it is always the number actually enforced."},"request_count":{"type":"integer","title":"Request Count","description":"Requests counted against the account in the current window so far, summed across all your keys. Reading this endpoint does not count."},"remaining":{"type":"integer","title":"Remaining","description":"Requests left in the current window (`request_quota - request_count`, floored at 0)."},"period_end":{"type":"string","format":"date-time","title":"Period End","description":"When allowance next frees up (ISO 8601, UTC). On the rolling free window this is the top of the next hour, as the oldest counted hour falls out of the trailing 24; on a paid tier it is the next billing-period boundary."}},"type":"object","required":["tier","quota_window","request_quota","request_count","remaining","period_end"],"title":"UsageData","description":"The calling **account's** usage against its quota for the current window."},"UsageResponse":{"properties":{"data":{"$ref":"#/components/schemas/UsageData"}},"type":"object","required":["data"],"title":"UsageResponse","examples":[{"data":{"period_end":"2026-06-17T10:00:00Z","quota_window":"daily","remaining":208,"request_count":42,"request_quota":250,"tier":"taster"}}]},"Problem":{"properties":{"title":{"type":"string","title":"Problem title","description":"Short, human-readable summary of the problem type."},"type":{"type":"string","title":"Problem type","description":"Stable problem-type URI under `/problems/<slug>`. Relative, so it resolves against the host that served the response. The slug is never renamed — branch on this, not on `title` or `detail`."},"status":{"type":"integer","title":"Status code","description":"The HTTP status code, repeated in the body."},"detail":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Problem detail","description":"Human-readable explanation of *this* occurrence. Free text: it may name concrete values and may change without notice."},"doc_url":{"type":"string","title":"Documentation URL","description":"Deep link to this problem's entry in these docs, stamped per request against the host that served it. Present on every problem body."}},"type":"object","required":["type","title","status"],"title":"Problem","description":"An **RFC 9457** problem document, served as `application/problem+json`. `type` is a stable, never-renamed URI you can branch on; the other core members are for humans.\n\nIndividual problems add **extension members** on top of these — `retry_after` on a `429`, `supported_columns` on an invalid sort, `errors` on a validation failure. They are documented on the problem itself, under Problems, and a client should ignore any member it does not recognise rather than reject the body."}},"securitySchemes":{"ApiKey":{"type":"apiKey","description":"Programmatic access key for the `/v1` resource endpoints. Send it as the `X-API-Key` header (`noms_sk_<env>_...`). Create one in your dashboard under **API Keys → Create key**; the raw key is shown once and never stored. Your account's tier governs which data you can read, and every key you own draws on the same request allowance.","in":"header","name":"X-API-Key"}}},"tags":[{"name":"foods","description":"The food catalog. Search, filter, sort, and embed related brand / food-group / nutrient detail via `include=`.","x-displayName":"Foods"},{"name":"brands","description":"The brand catalog — the names foods are sold under, and the `/v1/brands/{id}` handle each `brand_id` points at.","x-displayName":"Brands"},{"name":"nutrients","description":"The canonical nutrient catalog (codes, units, hierarchy).","x-displayName":"Nutrients"},{"name":"foodGroups","description":"The canonical food-group catalog.","x-displayName":"Food Groups"},{"name":"usage","description":"Your **account's** usage against its quota for the current window — the total across every key you hold, which is what enforcement compares against. Poll it to see where you stand before hitting the limit.","x-displayName":"Usage"},{"name":"market","description":"Read and set your account's market. Free-tier accounts serve one chosen market, shared by every key they own; set it before querying foods (paid plans serve all markets).","x-displayName":"Market"},{"name":"invalid-cursor","description":"**Status:** `400`\n\nThe pagination `cursor` could not be decoded: it was malformed or truncated.\n\n**Extension members:** `cursor`, `reason`\n\n**Example**\n\n```json\n{\n  \"type\": \"/problems/invalid-cursor\",\n  \"title\": \"Invalid pagination cursor\",\n  \"status\": 400,\n  \"cursor\": \"bm90LWEtY3Vyc29y\",\n  \"reason\": \"invalid base64 payload\",\n  \"detail\": \"Cursor could not be decoded: invalid base64 payload. Follow links.next from a fresh first page rather than constructing a cursor by hand.\",\n  \"doc_url\": \"https://api.noms.sh/docs#tag/invalid-cursor\"\n}\n```","x-displayName":"Invalid pagination cursor"},{"name":"invalid-filter","description":"**Status:** `400`\n\nA filter named an unknown field, used an operator the field does not support, or carried an unparseable value.\n\n**Extension members:** `field`, `op`, `value`, `supported_operators`, `supported_fields`\n\n**Example**\n\n```json\n{\n  \"type\": \"/problems/invalid-filter\",\n  \"title\": \"Invalid filter\",\n  \"status\": 400,\n  \"field\": \"name\",\n  \"op\": \"contains\",\n  \"value\": null,\n  \"supported_operators\": [\n    \"eq\",\n    \"in\"\n  ],\n  \"supported_fields\": [\n    \"id\",\n    \"name\",\n    \"barcode\"\n  ],\n  \"detail\": \"Filter 'name' is invalid: operator 'contains' is not supported for 'name'. Supported operators: eq, in.\",\n  \"doc_url\": \"https://api.noms.sh/docs#tag/invalid-filter\"\n}\n```","x-displayName":"Invalid filter"},{"name":"invalid-sort","description":"**Status:** `400`\n\nThe `sort` parameter named a column that is not sortable on this endpoint.\n\n**Extension members:** `column`, `supported_columns`\n\n**Example**\n\n```json\n{\n  \"type\": \"/problems/invalid-sort\",\n  \"title\": \"Invalid sort\",\n  \"status\": 400,\n  \"column\": \"created_at\",\n  \"supported_columns\": [\n    \"id\",\n    \"name\"\n  ],\n  \"detail\": \"Sort column 'created_at' is not sortable on this endpoint. Sortable columns: id, name.\",\n  \"doc_url\": \"https://api.noms.sh/docs#tag/invalid-sort\"\n}\n```","x-displayName":"Invalid sort"},{"name":"invalid-include","description":"**Status:** `400`\n\nThe `include` parameter named a relationship that cannot be embedded on this endpoint.\n\n**Extension members:** `relation`, `supported_relations`\n\n**Example**\n\n```json\n{\n  \"type\": \"/problems/invalid-include\",\n  \"title\": \"Invalid include\",\n  \"status\": 400,\n  \"relation\": \"manufacturer\",\n  \"supported_relations\": [\n    \"brand\",\n    \"food_group\",\n    \"nutrients\",\n    \"serving_sizes\",\n    \"images\",\n    \"market_countries\"\n  ],\n  \"detail\": \"Relationship 'manufacturer' cannot be included on this endpoint. Supported relationships: brand, food_group, nutrients, serving_sizes, images, market_countries.\",\n  \"doc_url\": \"https://api.noms.sh/docs#tag/invalid-include\"\n}\n```","x-displayName":"Invalid include"},{"name":"invalid-fields","description":"**Status:** `400`\n\nA `fields[<resource>]` sparse-fieldset selection named a field that is not exposed on that resource.\n\n**Extension members:** `resource`, `field`, `supported_fields`\n\n**Example**\n\n```json\n{\n  \"type\": \"/problems/invalid-fields\",\n  \"title\": \"Invalid sparse-fieldset selection\",\n  \"status\": 400,\n  \"resource\": \"foods\",\n  \"field\": \"colour\",\n  \"supported_fields\": [\n    \"id\",\n    \"name\",\n    \"barcode\"\n  ],\n  \"detail\": \"Field 'colour' is not exposed on resource 'foods'. Exposed fields: id, name, barcode.\",\n  \"doc_url\": \"https://api.noms.sh/docs#tag/invalid-fields\"\n}\n```","x-displayName":"Invalid sparse-fieldset selection"},{"name":"invalid-page-size","description":"**Status:** `400`\n\nThe requested `page_size` is outside the accepted range for this endpoint.\n\n**Extension members:** `requested`, `minimum`, `maximum`\n\n**Example**\n\n```json\n{\n  \"type\": \"/problems/invalid-page-size\",\n  \"title\": \"Invalid page size\",\n  \"status\": 400,\n  \"requested\": 0,\n  \"minimum\": 1,\n  \"maximum\": 20,\n  \"detail\": \"page_size=0 is below the minimum of 1\",\n  \"doc_url\": \"https://api.noms.sh/docs#tag/invalid-page-size\"\n}\n```","x-displayName":"Invalid page size"},{"name":"unknown-query-param","description":"**Status:** `400`\n\nThe request used a query parameter outside this endpoint's accepted vocabulary.\n\n**Extension members:** `param`, `supported_params`, `suggestion`\n\n**Example**\n\n```json\n{\n  \"type\": \"/problems/unknown-query-param\",\n  \"title\": \"Unknown query parameter\",\n  \"status\": 400,\n  \"param\": \"page[size]\",\n  \"supported_params\": [\n    \"cursor\",\n    \"page_size\",\n    \"sort\"\n  ],\n  \"suggestion\": \"page_size\",\n  \"detail\": \"'page[size]' is not a supported query parameter. Did you mean 'page_size'? Supported parameters: cursor, page_size, sort; plus LHS-bracket filters (e.g. name[eq]).\",\n  \"doc_url\": \"https://api.noms.sh/docs#tag/unknown-query-param\"\n}\n```","x-displayName":"Unknown query parameter"},{"name":"not-found","description":"**Status:** `404`\n\nNo resource matches the requested id.\n\n**Extension members:** `resource`, `id`\n\n**Example**\n\n```json\n{\n  \"type\": \"/problems/not-found\",\n  \"title\": \"Not found\",\n  \"status\": 404,\n  \"resource\": \"foods\",\n  \"id\": \"abc123def456\",\n  \"detail\": \"Food with id 'abc123def456' was not found.\",\n  \"doc_url\": \"https://api.noms.sh/docs#tag/not-found\"\n}\n```","x-displayName":"Not found"},{"name":"unauthorized","description":"**Status:** `401`\n\nValid authentication is missing or was rejected.\n\n**Example**\n\n```json\n{\n  \"type\": \"/problems/unauthorized\",\n  \"title\": \"Unauthorized\",\n  \"status\": 401,\n  \"detail\": \"Valid authentication is required to access this resource.\",\n  \"doc_url\": \"https://api.noms.sh/docs#tag/unauthorized\"\n}\n```","x-displayName":"Unauthorized"},{"name":"maintenance","description":"**Status:** `503`\n\nThe API is temporarily in maintenance and is not serving this request; retry shortly.\n\n**Example**\n\n```json\n{\n  \"type\": \"/problems/maintenance\",\n  \"title\": \"Service in maintenance\",\n  \"status\": 503,\n  \"detail\": \"The API is temporarily unavailable for maintenance; please retry shortly.\",\n  \"doc_url\": \"https://api.noms.sh/docs#tag/maintenance\"\n}\n```","x-displayName":"Service in maintenance"},{"name":"quota-exceeded","description":"**Status:** `429`\n\nYour account's request quota is exhausted for the current window; retry once it resets. Every key on an account draws on the same allowance.\n\n**Extension members:** `limit`, `window`, `retry_after`\n\n**Example**\n\n```json\n{\n  \"type\": \"/problems/quota-exceeded\",\n  \"title\": \"Quota exceeded\",\n  \"status\": 429,\n  \"limit\": 250,\n  \"window\": \"daily\",\n  \"retry_after\": 3600,\n  \"detail\": \"Request quota of 250 per day exceeded. Retry once the current window resets.\",\n  \"doc_url\": \"https://api.noms.sh/docs#tag/quota-exceeded\"\n}\n```","x-displayName":"Quota exceeded"},{"name":"rate-limited","description":"**Status:** `429`\n\nRequests arrived faster than your plan's burst rate; slow down and retry after `Retry-After`. The burst allowance is per account, so extra keys do not raise it.\n\n**Extension members:** `limit`, `window`, `retry_after`\n\n**Example**\n\n```json\n{\n  \"type\": \"/problems/rate-limited\",\n  \"title\": \"Rate limit exceeded\",\n  \"status\": 429,\n  \"limit\": 1,\n  \"window\": \"second\",\n  \"retry_after\": 1,\n  \"detail\": \"Burst rate limit of 1 requests per second exceeded. Slow down and retry after the Retry-After interval.\",\n  \"doc_url\": \"https://api.noms.sh/docs#tag/rate-limited\"\n}\n```","x-displayName":"Rate limit exceeded"},{"name":"enumeration-forbidden","description":"**Status:** `403`\n\nYour plan does not allow bulk listing; use a `q` search query or fetch items by id (or upgrade for full enumeration).\n\n**Example**\n\n```json\n{\n  \"type\": \"/problems/enumeration-forbidden\",\n  \"title\": \"Bulk enumeration not available on this tier\",\n  \"status\": 403,\n  \"detail\": \"This tier does not allow bulk listing. Add a `q` search query to this request, fetch a single item by id, or upgrade for full enumeration.\",\n  \"doc_url\": \"https://api.noms.sh/docs#tag/enumeration-forbidden\"\n}\n```","x-displayName":"Bulk enumeration not available on this tier"},{"name":"result-depth-exceeded","description":"**Status:** `403`\n\nYour plan limits how deep a query can be paged; narrow the search or upgrade for unlimited depth.\n\n**Extension members:** `cap`\n\n**Example**\n\n```json\n{\n  \"type\": \"/problems/result-depth-exceeded\",\n  \"title\": \"Result depth limit exceeded\",\n  \"status\": 403,\n  \"cap\": 1000,\n  \"detail\": \"This tier returns roughly the first 1000 results of a query. Narrow your search with `q` or filters, or upgrade for unlimited pagination depth.\",\n  \"doc_url\": \"https://api.noms.sh/docs#tag/result-depth-exceeded\"\n}\n```","x-displayName":"Result depth limit exceeded"},{"name":"market-not-selected","description":"**Status:** `403`\n\nYour plan serves one market and no market has been chosen yet; pick one in the dashboard or with `PUT /v1/market` (or upgrade for all-markets access). A market is an account setting, so every key on the account shares it.\n\n**Example**\n\n```json\n{\n  \"type\": \"/problems/market-not-selected\",\n  \"title\": \"No market selected for this tier\",\n  \"status\": 403,\n  \"detail\": \"This tier serves one market and your account has not chosen one yet. Set your market in the dashboard or with `PUT /v1/market`, then retry \\u2014 or upgrade for all-markets access.\",\n  \"doc_url\": \"https://api.noms.sh/docs#tag/market-not-selected\"\n}\n```","x-displayName":"No market selected for this tier"},{"name":"unknown-market","description":"**Status:** `422`\n\nThe market named in a market write is not one of the markets foods are available in; pick one from `available`.\n\n**Extension members:** `market`, `available`\n\n**Example**\n\n```json\n{\n  \"type\": \"/problems/unknown-market\",\n  \"title\": \"Unknown market\",\n  \"status\": 422,\n  \"market\": \"ZZ\",\n  \"available\": [\n    \"CL\",\n    \"US\"\n  ],\n  \"detail\": \"No foods are available in market 'ZZ'. Choose one of the available markets (see `available`, or GET /v1/market).\",\n  \"doc_url\": \"https://api.noms.sh/docs#tag/unknown-market\"\n}\n```","x-displayName":"Unknown market"},{"name":"market-not-applicable","description":"**Status:** `409`\n\nA market write was made on an account whose plan already serves all markets, so there is no single market to set; narrow an individual request with the `market_countries` filter instead.\n\n**Example**\n\n```json\n{\n  \"type\": \"/problems/market-not-applicable\",\n  \"title\": \"Market selection not applicable to this tier\",\n  \"status\": 409,\n  \"detail\": \"Your plan already serves all markets, so there is no single market to set. Narrow an individual request with the `market_countries` filter instead.\",\n  \"doc_url\": \"https://api.noms.sh/docs#tag/market-not-applicable\"\n}\n```","x-displayName":"Market selection not applicable to this tier"},{"name":"market-account-required","description":"**Status:** `409`\n\nA market is an account setting, and this key is not attached to an account. Mint a fresh key from your dashboard and use that one.\n\n**Example**\n\n```json\n{\n  \"type\": \"/problems/market-account-required\",\n  \"title\": \"No account to set a market on\",\n  \"status\": 409,\n  \"detail\": \"A market is an account setting, and this credential has no account to set it on. Sign in to the dashboard, or use a key that belongs to a registered account.\",\n  \"doc_url\": \"https://api.noms.sh/docs#tag/market-account-required\"\n}\n```","x-displayName":"No account to set a market on"},{"name":"key-revoked","description":"**Status:** `409`\n\nThe key you tried to rotate has already been revoked. Revocation is permanent, so create a new key from your dashboard instead.\n\n**Extension members:** `id`\n\n**Example**\n\n```json\n{\n  \"type\": \"/problems/key-revoked\",\n  \"title\": \"API key is revoked\",\n  \"status\": 409,\n  \"id\": \"k3Ym0pQrStUv\",\n  \"detail\": \"API key with id 'k3Ym0pQrStUv' is revoked and cannot be rotated. Create a new key instead.\",\n  \"doc_url\": \"https://api.noms.sh/docs#tag/key-revoked\"\n}\n```","x-displayName":"API key is revoked"},{"name":"key-limit-reached","description":"**Status:** `409`\n\nYour account already holds the maximum number of live API keys; revoke one from your dashboard before creating another. Extra keys never raise your quota — every key on an account shares one request allowance.\n\n**Extension members:** `limit`, `count`\n\n**Example**\n\n```json\n{\n  \"type\": \"/problems/key-limit-reached\",\n  \"title\": \"API key limit reached\",\n  \"status\": 409,\n  \"limit\": 5,\n  \"count\": 5,\n  \"detail\": \"You already have 5 of 5 allowed API keys. Revoke one before creating another. Every key on your account shares the same request allowance, so an extra key would not raise your quota.\",\n  \"doc_url\": \"https://api.noms.sh/docs#tag/key-limit-reached\"\n}\n```","x-displayName":"API key limit reached"},{"name":"forbidden","description":"**Status:** `403`\n\nThe request was rejected before it reached the API.\n\n**Example**\n\n```json\n{\n  \"type\": \"/problems/forbidden\",\n  \"title\": \"Forbidden\",\n  \"status\": 403,\n  \"detail\": \"The request was rejected before it reached the API.\",\n  \"doc_url\": \"https://api.noms.sh/docs#tag/forbidden\"\n}\n```","x-displayName":"Forbidden"},{"name":"service-unavailable","description":"**Status:** `503`\n\nThe service is temporarily unable to handle the request; retry shortly.\n\n**Example**\n\n```json\n{\n  \"type\": \"/problems/service-unavailable\",\n  \"title\": \"Service unavailable\",\n  \"status\": 503,\n  \"detail\": \"The service is briefly unavailable; retry shortly.\",\n  \"doc_url\": \"https://api.noms.sh/docs#tag/service-unavailable\"\n}\n```","x-displayName":"Service unavailable"},{"name":"search-timeout","description":"**Status:** `504`\n\nThe search outran its time budget and was abandoned; narrow the query and retry.\n\n**Extension members:** `timeout_ms`\n\n**Example**\n\n```json\n{\n  \"type\": \"/problems/search-timeout\",\n  \"title\": \"Search timed out\",\n  \"status\": 504,\n  \"timeout_ms\": 800,\n  \"detail\": \"The search exceeded its 800ms time limit and was abandoned. Narrow the query \\u2014 a more specific `q`, or additional filters \\u2014 and retry.\",\n  \"doc_url\": \"https://api.noms.sh/docs#tag/search-timeout\"\n}\n```","x-displayName":"Search timed out"},{"name":"search-unavailable","description":"**Status:** `503`\n\nThe search service is temporarily unavailable; requests without `q` or a nutrient sort are unaffected.\n\n**Example**\n\n```json\n{\n  \"type\": \"/problems/search-unavailable\",\n  \"title\": \"Search unavailable\",\n  \"status\": 503,\n  \"detail\": \"The search service is temporarily unavailable, so this request could not be answered. Retry shortly. Requests that do not use `q` or a nutrient sort are unaffected.\",\n  \"doc_url\": \"https://api.noms.sh/docs#tag/search-unavailable\"\n}\n```","x-displayName":"Search unavailable"},{"name":"api-version-removed","description":"**Status:** `410`\n\nThe requested API URL version has been removed; migrate to the current version.\n\n**Extension members:** `migration_guide`\n\n**Example**\n\n```json\n{\n  \"type\": \"/problems/api-version-removed\",\n  \"title\": \"API version removed\",\n  \"status\": 410,\n  \"migration_guide\": \"https://noms.sh/docs/api/v0-to-v1\",\n  \"detail\": \"API version v0 was removed on 2026-01-01. Migrate to v1.\",\n  \"doc_url\": \"https://api.noms.sh/docs#tag/api-version-removed\"\n}\n```","x-displayName":"API version removed"},{"name":"validation-error","description":"**Status:** `422`\n\nThe request failed schema validation; `errors` lists each offending location.\n\n**Extension members:** `errors`\n\n**Example**\n\n```json\n{\n  \"type\": \"/problems/validation-error\",\n  \"title\": \"Request validation error\",\n  \"status\": 422,\n  \"errors\": [\n    {\n      \"type\": \"int_parsing\",\n      \"loc\": [\n        \"query\",\n        \"page_size\"\n      ],\n      \"msg\": \"Input should be a valid integer\"\n    }\n  ],\n  \"detail\": \"Request validation failed.\",\n  \"doc_url\": \"https://api.noms.sh/docs#tag/validation-error\"\n}\n```","x-displayName":"Request validation error"},{"name":"internal-server-error","description":"**Status:** `500`\n\nAn unexpected server error occurred.\n\n**Example**\n\n```json\n{\n  \"type\": \"/problems/internal-server-error\",\n  \"title\": \"Internal server error\",\n  \"status\": 500,\n  \"detail\": \"An unexpected error occurred while processing the request.\",\n  \"doc_url\": \"https://api.noms.sh/docs#tag/internal-server-error\"\n}\n```","x-displayName":"Internal server error"}],"x-tagGroups":[{"name":"Resources","tags":["foods","brands","nutrients","foodGroups"]},{"name":"Usage","tags":["usage","market"]},{"name":"Problems","tags":["invalid-cursor","invalid-filter","invalid-sort","invalid-include","invalid-fields","invalid-page-size","unknown-query-param","not-found","unauthorized","maintenance","quota-exceeded","rate-limited","enumeration-forbidden","result-depth-exceeded","market-not-selected","unknown-market","market-not-applicable","market-account-required","key-revoked","key-limit-reached","forbidden","service-unavailable","search-timeout","search-unavailable","api-version-removed","validation-error","internal-server-error"]}]}