Ingredient Form States Make Recipe APIs Safer to Compose
Recent Open Food Facts taxonomy changes around protein isolates, oat kernels, nutritional yeast flakes, mirin, sesame, and spice blends show why recipe APIs should model ingredient form, processing, aliases, and culinary role separately.
The useful signal in a week of small taxonomy edits
The most important food-data changes often look too small to justify a product meeting: one missing singular allergen term, one new protein isolate, one ingredient that should be a condiment rather than an alcoholic beverage, one ambiguous category renamed. For recipe, nutrition, grocery, and meal-planning APIs, those edits are exactly where brittle data models show up.
In the last two days, Open Food Facts merged several taxonomy changes that point in the same direction. The ingredient taxonomy added mung bean protein and mung bean protein isolate, added oat kernels, and added Swedish coverage for deactivated yeast flakes. The food taxonomy split rice wine from sake and moved mirin into condiments, added a sesame dressing category with Nordic translations, and renamed spice mixes to spice blends. The allergen taxonomy also added the Swedish singular form of sesame seed.
The thesis is not that every recipe API must mirror Open Food Facts. It is that ingredient identity is not enough. A practical food-data API needs to preserve ingredient form, processing state, language aliases, culinary role, and matching confidence as separate fields, because downstream products compose those fields differently.
That angle avoids the common but weaker response to taxonomy churn: "add more synonyms." Synonyms help search. They do not tell a meal planner whether mung bean protein isolate should behave like mung beans, whether nutritional yeast flakes can substitute for yeast, whether mirin belongs in an alcohol filter, or whether "sesam" in Swedish should trigger an allergen warning.
Why form is not a display detail
Many recipe systems start with a normalized ingredient table like this:
| Field | Example |
|---|---|
id |
mung-bean |
name |
Mung bean |
aliases |
green gram, moong |
category |
legume |
That model is too flat for modern food applications. The moment a product supports nutrition estimates, grocery matching, dietary filters, substitutions, or generated recipes, form becomes operational data.
Mung bean protein isolate is derived from mung beans, but it is not interchangeable with whole mung beans in a shopping list, nutrition calculation, recipe instruction, or allergen-style avoidance rule. Oat kernels are related to oats, rolled oats, oat flour, and oat milk, but they imply different preparation time, package matching, and substitution behavior. Deactivated yeast flakes may be sold as nutritional yeast and used as a flavoring; active baker's yeast has a different function entirely. Mirin is rice-wine-derived, but in a recipe context it behaves more like a sweet seasoning than a beverage.
A developer who collapses those distinctions into one canonical ingredient ID will get plausible-looking but wrong behavior:
- grocery lists that substitute a processed isolate for a whole food;
- generated recipes that ask users to bloom deactivated yeast;
- nutrition estimates that apply whole-food values to concentrated protein ingredients;
- alcohol filters that block cooking condiments too aggressively or miss them entirely;
- allergen warnings that fail when a label uses a singular or localized term;
- analytics that cannot distinguish category growth from better taxonomy coverage.
These are not rare edge cases. They are the normal cost of serving recipes across languages, cuisines, package labels, and AI-assisted extraction pipelines.
A better ingredient object: identity plus state
A more robust API contract separates stable identity from the observed form in a recipe, product label, or grocery offer. One possible sketch:
{
"ingredient_id": "off:ingredients:mung-bean-protein-isolate",
"base_food_id": "mung-bean",
"display_name": "mung bean protein isolate",
"observed_text": "mung bean protein isolate",
"language": "en",
"form": {
"physical": "powder",
"processing": ["isolated", "protein-concentrated"],
"activity_state": null,
"part": null
},
"culinary_role": ["protein-fortifier"],
"taxonomy_paths": [
"ingredients > plant proteins > legume proteins"
],
"dietary_flags": {
"contains_alcohol_as_ingredient": false,
"potential_allergens": []
},
"match": {
"confidence": 0.94,
"basis": ["exact-alias", "taxonomy-version:2026-08-02"],
"review_required": false
}
}
The exact values will vary by provider, but the separation matters. base_food_id supports rollups and broad filters. ingredient_id supports precise matching. form.processing captures the transformation that changes nutrition and substitutability. culinary_role explains how the ingredient functions in a recipe. observed_text lets clients audit extraction and localization decisions.
This is also a cleaner API design than trying to encode everything in names such as mung-bean-protein-isolate-powder-vegan. Names are not contracts. They change with language, spelling, taxonomy policy, and search optimization.
Product decisions that depend on form state
Different consuming products need different behavior from the same ingredient graph. That is why the API should expose enough structure for clients to choose.
| Product surface | Needs from the API | Failure if form is flattened |
|---|---|---|
| Nutrition estimator | Processing state, concentration, source dataset, uncertainty | Whole-food nutrient values applied to isolates, flakes, extracts, or powders |
| Grocery list builder | Package-match category, physical form, acceptable substitutes | Oat kernels matched to rolled oats or oat flour without warning |
| Meal planner | Culinary role, prep implications, dietary constraints | Recipes become impractical because form-specific cooking behavior is lost |
| Allergen and avoidance filters | Localized aliases, singular/plural terms, severity policy | Sesame terms in another language bypass warnings |
| Recipe search | Search aliases plus canonical IDs | Users find results, but filters and aggregations disagree |
| Food AI extraction | Observed text, confidence, review status | Model output silently rewrites ambiguous ingredients into overconfident IDs |
The mirin example is especially useful for API design. A hierarchy-only model asks whether mirin is under alcoholic beverages, rice wines, or condiments. Recipe products often need all three facts, but for different reasons. The source and production method may matter for dietary or regulatory labeling. The culinary role matters for search, substitution, and recipe generation. The grocery aisle may map to condiments. A single parent category cannot carry all of that without creating exceptions elsewhere.
Spice blends show the same issue from the opposite direction. Renaming an ambiguous category can improve clarity, but existing client integrations may already depend on the previous label. If an API exposes only display strings, the change is breaking. If it exposes stable IDs, deprecation metadata, and aliases, the display layer can improve while integrations remain stable.
Edge cases worth modeling explicitly
Ingredient-form modeling does not need to become an ontology project before it is useful. Start with the edge cases that create customer-visible errors.
1. Derived ingredients
Protein isolates, extracts, oils, starches, syrups, and flours should carry both a precise ingredient identity and a source relationship. A user avoiding mung beans may or may not intend to avoid mung bean protein isolate; the product should be able to ask, not guess.
Useful fields:
derived_from: source food IDs;processing: isolate, extract, fermented, refined, roasted, dried;concentration_basis: when nutrition is materially different from the base food;avoidance_policy: whether broad source-food filters include the derivative by default.
2. Activity state
Yeast is a simple example. Active dry yeast, instant yeast, brewer's yeast, and deactivated nutritional yeast flakes should not be treated as interchangeable just because they share a word. Recipe instructions and substitutions depend on activity state.
Useful fields:
activity_state: active, inactive, unknown;function: leavening, flavoring, fermentation, thickening;substitution_group: only where substitution is safe.
3. Culinary role versus origin
Mirin can be alcohol-related by origin and condiment-like by use. Sesame dressing can be a prepared sauce, an allergen carrier, and a recipe component. APIs should avoid forcing one category tree to answer all product questions.
Useful fields:
origin_category;culinary_role;grocery_department;regulatory_or_dietary_flags.
4. Language morphology
The Swedish singular sesame update is a reminder that allergen detection cannot depend only on English plural display names. Singular/plural forms, compounds, diacritics, and local label conventions affect safety-critical matching.
Useful fields:
- localized aliases with language codes;
- match rule type: exact, stemmed, compound, manually reviewed;
- safety scope: allergen, intolerance, preference, merchandising;
- taxonomy version used at match time.
Operational trade-offs
There is a real cost to richer ingredient-state contracts. They introduce migration work, client education, and confidence scoring. But the alternative is not simplicity; it is hidden complexity pushed into every downstream integration.
A practical rollout path is incremental:
- Keep existing ingredient IDs stable.
- Add optional form and processing fields without changing current responses.
- Expose a taxonomy version and changed-since endpoint.
- Mark high-risk matches with
review_requiredrather than blocking ingestion. - Add client-facing documentation for substitution, nutrition, grocery, and safety semantics.
- Track analytics on how often clients receive broader base-food matches versus precise form matches.
The API should also distinguish between taxonomy certainty and extraction certainty. A taxonomy may confidently define mung bean protein isolate. A parser may still be uncertain whether a messy label phrase refers to that ingredient. Combining those into one confidence score makes debugging harder.
Implementation checklist
Before adding another synonym table, ask these questions:
- Can the API represent a processed derivative separately from its base food?
- Can clients decide whether source-food avoidance includes derivatives?
- Are physical form and culinary role separate from category hierarchy?
- Can grocery matching prefer package-equivalent forms rather than broad ingredient families?
- Do nutrition estimates know when a processed form needs a different reference value?
- Are localized singular, plural, and compound allergen terms versioned and auditable?
- Does every match expose observed text, normalized ID, confidence, and taxonomy version?
- Can category renames preserve stable IDs and alias history?
- Is there a changed-since feed so clients can review taxonomy updates without scraping release notes?
For Recipe API-style products, the payoff is not academic purity. It is fewer false substitutions, clearer nutrition uncertainty, safer avoidance filters, better grocery conversion, and more predictable AI-generated recipe behavior.
Sources
Fresh primary sources from the last seven days:
- Open Food Facts, taxonomy(ingredients): Add mung bean protein [isolate], minor related fixes, merged 2026-08-01.
- Open Food Facts, taxonomy(ingredients): Add oat kernels, merged 2026-08-01.
- Open Food Facts, taxonomy(ingredients): Deactivated yeast [flakes] (sv), merged 2026-08-01.
- Open Food Facts, taxonomy(food): mirin/rice wine edits, merged 2026-08-01.
- Open Food Facts, taxonomy(food): salad dressing edits, merged 2026-08-02.
- Open Food Facts, taxonomy(food): spice blend category edits, merged 2026-08-02.
- Open Food Facts, taxonomy(allergens): Add Swedish singular of sesame seeds to allergens, merged 2026-08-01.
Start Building
One consistent schema on every response. Get a free key and ship in minutes.