openapi: 3.2.0 info: title: Noms Foods 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** and **food groups**.' 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 tags: - name: foods description: The food catalog. Search, filter, sort, and embed related brand / food-group / nutrient detail via `include=`. x-displayName: Foods paths: /v1/foods: get: tags: - foods summary: List foods description: 'Return a keyset-paginated page of the food catalog — the primary endpoint. Embed related resources with `include=` (`brand`, `food_group`, `nutrients`, `serving_sizes`, `images`, `market_countries`); filter on columns, relations (`brand_name`, `market_countries`), and per-nutrient values (`nutrient[CODE][op]`); sort on columns or a nutrient value (`sort=-nutrient[PROTEIN]`); trim with `fields[]`; 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[]` 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. Embed related resources with `include=` and trim with `fields[]`, the same way the list endpoint does. A **retired** id (one merged into another food) forwards to the surviving food at `200` with an added `canonical_id` field, 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[]` 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.' components: schemas: 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 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] 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 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 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, milliliters, is_default}``. ``quantity`` is the count of ``unit``s in one serving (e.g. ``1`` cup, ``30`` g); ``grams`` and ``milliliters`` are that serving''s absolute size in each dimension. Multiply a nutrient''s per-100 ``value`` by ``serving[food.basis_unit] / 100`` to get its amount in this serving — the field the food''s ``basis_unit`` names is **never ``null``**, so that lookup always resolves; the other dimension is best-effort and may be ``null`` (a volume-anchored food has no per-serving mass, a mass-only solid no volume — neither dimension is invented from the other). ``is_default`` marks the food''s primary (reference) serving; exactly one serving per food is the default. ``descriptor`` is a free-text qualifier preserved from the source (e.g. ``chopped``, ``bar, each 2.4 oz``); null when the canonical unit already fully describes the serving. ``unit`` is one of the closed 9-value serving set and is never null; it is ``each`` when the source names no unit.' examples: - descriptor: chopped grams: '170' is_default: true milliliters: '236.5882' quantity: '1' unit: cup - is_default: true milliliters: '250' quantity: '250' unit: ml 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/`. 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. Individual 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.' Item_FoodDetailSchema_: properties: data: $ref: '#/components/schemas/FoodDetailSchema' type: object required: - data title: Item[FoodDetailSchema] 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**. The flat food resource is always present; each relation below appears in the ``data`` object only when the client requests it via ``include=`` (e.g. ``?include=brand,nutrients``), so every relation is optional and absent by default. The keys present are exactly those the client selected via ``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 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 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 index conveys sequence. ``type`` categorizes the image so a consumer can separate believed-real food photos from generic representations: ``photo`` is a photo believed to show the actual food; ``icon`` is a generic stand-in (a source icon, or the food''s food-group icon, appended as a fallback only when the food has no real images of its own).' examples: - type: photo url: https://images.noms.sh/60f440137489aed9240588a68ffebf455102b257021bfa1cde4d6f6b0b28ac6e 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, value, unit}``. ``id`` is the nutrient''s own code (e.g. ``PROTEIN``) and ``name`` its display name, making the embedded object symmetric with the standalone ``/v1/nutrients`` resource and the ``brand`` / ``food_group`` embeds; ``value`` is the measurement specific to this food, expressed **per 100** of the food''s ``basis_unit`` (directly comparable across foods; multiply by ``serving[basis_unit] / 100`` to get the amount in that serving). ``unit`` is one of the closed set ``{g, mg, mcg, kcal}``, or ``null`` for a dimensionless nutrient. ``name`` is trimmable via ``fields[nutrients]``.' examples: - id: PROTEIN name: Protein unit: g value: '3.2000' 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 securitySchemes: ApiKey: type: apiKey description: Programmatic access key for the `/v1` resource endpoints. Send it as the `X-API-Key` header (`noms_sk__...`). 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 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 x-provenance: method: harvested publisher: Noms source: https://api.noms.sh/openapi.json note: split by refine-openapis from the provider's own published document, verified by fetch (openapi/_original/*.fetch.json); stamped by stamp-openapi-provenance.py (roadmap#793)