apyhub
DATA EXTRACTION · DATA VALIDATION

Ingredient Parser From Text API

What it does

Ingredient Parser turns a free-text ingredient list into structured ingredient intelligence. Send ingredient text and, optionally, a language hint, and get back additive detection, allergen classification, and dietary signals in a stable machine-readable format.

Use it when you need to normalise product labels, check ingredient declarations, or enrich food catalog data. The response includes additives with regulatory codes, names, optional Italian names, functional categories, confidence, and a recognition flag. It also returns confirmed allergens, separate allergens_traces for “may contain traces of” clauses, dietary flags for vegan, vegetarian, alcohol, palm oil, and added sugars, plus unrecognized_ingredients for fragments it could not classify.

Ingredient Parser is built for automation: field names and enum tokens stay stable, and the lang input only helps match dictionary terms in the source text. If the service reaches its spend cap, degraded tells you it switched to deterministic-only mode instead of failing. Use the cached flag to identify repeat parses and llm_assisted to see whether language-model assistance was used.

This is a good fit for ecommerce product ingestion, compliance workflows, recipe analysis, and internal search indexing where ingredient text needs to become structured data you can filter and audit.

▣ ENDPOINT 01 / 02
POST
Parse an ingredient list
http://localhost:8080/ingredients/parse-ingredients/v1/parse
QUICKSTARTGUIDE

Quickstart

Send the ingredient list text and language hint to parse common ingredients and allergens.

curl -X POST "http://localhost:8080/ingredients/parse-ingredients/v1/parse" \
  -H "apy-token: $APY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"lang":"auto","text":"Wheat flour, sugar, palm oil, E322 (soy lecithin), milk powder"}'

What you'll get back

Returns a JSON object with these top-level fields: cached (boolean or null), dietary (object), degraded (boolean), additives (array), allergens (array), llm_assisted (boolean), degraded_reason (string or null), allergens_traces (array), and unrecognized_ingredients (array).

Notes: A HINT about the language of the INPUT text, used only to pick which language's dictionary terms to match against. It has NO effect on the response: label values, enum tokens and field names are always in English/stable form regardless of lang. auto (the default) checks all five supported languages and is the right choice whenever the input language isn't reliably known ahead of time. Not implemented, and deliberately out of scope for now: a separate output_lang parameter for localizing label values. The current design (stable id + English label) makes that a pure additive change later — clients that key off id today will not break if output_lang is added.

{
  "cached": false,
  "dietary": {
    "vegan": "unlikely",
    "vegetarian": "unlikely",
    "contains_alcohol": false,
    "contains_palm_oil": true,
    "contains_added_sugars": true
  },
  "degraded": false,
  "additives": [],
  "allergens": [],
  "llm_assisted": false,
  "degraded_reason": null,
  "allergens_traces": [],
  "unrecognized_ingredients": []
}
TRY ITLIVE · 60 ATOMS
Loading your default key…
The full key is used to call the gateway and stays in this tab — never sent to orbit or saved.
body*
Free-text ingredient list. Maximum 5000 characters — generous for any real product label; longer requests are rejected with 413 before any parsing happens.

About this endpoint

What it does

Parses a free-text ingredient list sent in the request body and returns a structured JSON object with additive, allergen, dietary, and classification fields. The endpoint does not change server state; it analyzes the input text and reports the extracted results.

Request Body

ParameterTypeMandatoryDescription
langENUMNoHint about the language of the input text, used only to choose dictionary matching.<br>Allowed values: auto, it, en, fr, de, es.<br>Default: auto.<br>Does not affect response language; labels and tokens remain in English/stable form.
textStringYesFree-text ingredient list to parse.<br>Maximum length: 5000 characters.

Response

Returns a JSON object with these top-level fields: cached boolean or null, dietary object, degraded boolean, additives array of objects, allergens array of objects, llm_assisted boolean, degraded_reason string or null, allergens_traces array of objects, and unrecognized_ingredients array of strings.

ParameterTypeMandatoryDescription
cachedBooleanNoWhether the response was served from cache. Nullable.
dietaryObjectNoDietary summary object with stable tokens/booleans.<br>Fields: vegan (likely / unlikely), vegetarian (likely / unlikely), contains_alcohol (boolean), contains_palm_oil (boolean), contains_added_sugars (boolean).
dietary.veganENUMNoAllowed values: likely, unlikely.
dietary.vegetarianENUMNoAllowed values: likely, unlikely.
dietary.contains_alcoholBooleanNoBoolean flag.
dietary.contains_palm_oilBooleanNoBoolean flag.
dietary.contains_added_sugarsBooleanNoBoolean flag.
degradedBooleanNotrue if the applicative spend cap was reached and the service responded in deterministic-only mode.
additivesObject ArrayNoParsed additives found in the ingredient list. Each item may include code, name, name_it, category, confidence, and recognized.
additives[].codeStringNoAdditive code such as an E-number.
additives[].nameStringNoOfficial EU name in English. Nullable.
additives[].name_itStringNoOfficial EU name in Italian. Nullable.
additives[].categoryString ArrayNoFunctional class tokens from the regulatory vocabulary. Nullable. An additive can have more than one category.
additives[].confidenceNumberNoConfidence score.
additives[].recognizedBooleanNotrue if the code was found in the regulatory table.
allergensObject ArrayNoAllergens declared as present, not traces-only matches. Each item may include id, label, source, eu_index, and confidence.
allergens[].idStringNoStable language-independent identifier.
allergens[].labelStringNoCanonical EU allergen name in English.
allergens[].sourceENUMNoAllowed values: dictionary, llm.
allergens[].eu_indexIntegerNoPosition in Annex II of Reg. (EU) 1169/2011.
allergens[].confidenceNumberNoConfidence score between 0 and 1.
llm_assistedBooleanNoWhether the LLM layer was used in the parse.
degraded_reasonENUMNoAllowed values: lifetime_cap_reached, daily_cap_reached, null.
allergens_tracesObject ArrayNoAllergens mentioned only in a “may contain traces of” clause. Same item shape as allergens.
allergens_traces[].idStringNoStable language-independent identifier.
allergens_traces[].labelStringNoCanonical EU allergen name in English.
allergens_traces[].sourceENUMNoAllowed values: dictionary, llm.
allergens_traces[].eu_indexIntegerNoPosition in Annex II of Reg. (EU) 1169/2011.
allergens_traces[].confidenceNumberNoConfidence score between 0 and 1.
unrecognized_ingredientsString ArrayNoIngredient fragments that could not be classified, echoed back verbatim from the submitted text.

Notes

allergens contains only confirmed allergens, while allergens_traces is reserved for “may contain traces of” matches. If you want a conservative view, read both arrays; if you only care about confirmed presence, read allergens alone.

Body

Name
Type
Description
bodyREQUIRED
object
▣ ENDPOINT 02 / 02
GET
Direct E-number lookup (zero LLM)
http://localhost:8080/ingredients/parse-ingredients/v1/additives/:code
QUICKSTARTGUIDE

Quickstart

Look up an E-number by code, using the code in the URL path.

curl -X GET "http://localhost:8080/ingredients/parse-ingredients/v1/additives/:code" \
  -H "apy-token: $APY_TOKEN"

What you'll get back

Returns a JSON object with these top-level fields: code (string), name (string or null), name_it (string or null), category (array of strings or null), confidence (number), and recognized (boolean).

{
  "code": "E322",
  "name": "Lecithins",
  "name_it": "Lecitine",
  "category": ["emulsifier"],
  "confidence": 1,
  "recognized": true
}
TRY ITLIVE · 10 ATOMS
Loading your default key…
The full key is used to call the gateway and stays in this tab — never sent to orbit or saved.

About this endpoint

What it does

Looks up a single E-number by its code path parameter and returns the regulatory data we have for that code. The response includes the code itself, the official EU name, optional Italian name, functional category list, a confidence value, and whether the code was recognized.

Path Parameter(s)

AttributeTypeMandatoryDescription
codeStringYesThe E-number to look up, such as an E-prefixed code.

Response

Returns a JSON object with code as a string, name as a nullable string, name_it as a nullable string, category as a nullable array of strings, confidence as a number, and recognized as a boolean. This is the success response for the endpoint.

ParameterTypeMandatoryDescription
codeStringNoThe E-number code that was looked up.
nameStringNoOfficial EU name in English. Nullable.
name_itStringNoOfficial EU name in Italian. Nullable and provided for traceability only.
categoryString ArrayNoFunctional class(es) as stable snake_case tokens from the canonical Annex I vocabulary. Nullable.
confidenceNumberNoConfidence value returned by the service.
recognizedBooleanNoTrue if code was found in the regulatory table, otherwise False.

Path parameters

Name
Type
Description
codeREQUIRED
string
▣ COMMON ERRORS

Errors any endpoint can return

400bad_request

Required parameter missing or malformed body.

401unauthorized

API key missing, revoked, or not authorized for this service.

429rate_limited

Your plan's per-second rate exceeded. Retry with exponential backoff.

503upstream_busy

Backend temporarily unavailable. Try again in a few seconds.