Skip to content
Recipe API

Food API Clients Need Version Negotiation, Not Just Endpoints

Recent Open Food Facts server and Dart client changes show why recipe and nutrition APIs should expose explicit API-version, field-alias, and capability contracts across SDKs before schema drift reaches production apps.

api-designdeveloper-experiencenutritiondata-modeling

The thesis

Food-data APIs should treat client version negotiation as a product contract: when the server adds fields, renames concepts, or changes derived nutrition semantics, SDKs need explicit version and capability metadata so recipe, grocery, and meal-planning apps can upgrade without silent data drift.

That is the useful signal in two fresh Open Food Facts changes from the last week. On July 10, the Dart SDK added support for API 3.1, including a new product_query_version.dart, tests for API 3.1 product and search queries, new vitamin D2 and D3 nutrient fields, support for environmental% fields alongside eco% fields, and a fixed ECOSCORE sort value. On July 16, the Open Food Facts server release 2.98.0 shipped server-side API and data-contract changes such as generated JSON files for product statistics, the unwanted-ingredients attribute, CORS behavior fixes, facet URL fixes, and a Nutri-Score rule fix for coconut water.

None of those changes is only a wrapper-maintenance story. They are the same class of problem recipe API teams face when a mobile meal-planner, a web grocery workflow, an LLM recipe assistant, and a backend nutrition calculator all consume the same food objects through different SDKs and release cadences. If the only contract is “call /products and parse JSON,” each client gradually invents its own interpretation of fields, aliases, missing nutrients, and quality fixes.

The repeated angle this post avoids

Recent posts here already covered individual data-domain changes: unwanted ingredients, facets, CORS, salt/sodium consistency, taxonomy drift, sustainability provenance, recall matching, and AI-assisted taxonomy review. This article is not another argument that one new field needs a better model. The new angle is the upgrade surface between server schemas and client SDKs: how an API provider should let builders request, detect, test, and migrate food-data capabilities as versions change.

Why versioning is sharper in recipe and nutrition APIs

Version negotiation matters in any API, but food data adds several failure modes that are easy to underestimate.

First, a field can be technically present while semantically different. A recipe app may have an eco_score column because an older upstream API exposed eco%, while a newer client also reads environmental%. If those are aliases, the API should say so. If one is deprecated or mapped through a newer methodology, the API should say that too. Otherwise downstream analytics may count the same signal twice, or worse, compare old and new scores as if they were independent measures.

Second, nutrient vocabularies are not static. The Dart SDK change adding vitamin D2 and D3 is a small example with large implications. A nutrition API that previously modeled only total vitamin D may later expose individual vitamers. A meal-planning product could use that extra detail for supplement-aware recommendations, clinical nutrition workflows, or warning copy. But if one SDK exposes the split fields and another client still collapses them, the product can show inconsistent nutrition panels across platforms.

Third, derived fields depend on policy and algorithm updates. The server release note that coconut water should not count as fruit for Nutri-Score is a data-quality and rule interpretation issue, not just a bug fix. A nutrition score returned yesterday and a nutrition score returned tomorrow may differ for reasons that are valid but invisible. If recipe APIs cache derived scores, precompute search facets, or rank recipes by health attributes, they need a way to know which scoring rule version produced a value.

Fourth, search and facets turn version drift into user-visible UX drift. Generated JSON files for product statistics and fixes to facet URLs sound operational, but they affect counts, filters, and discovery experiences. A client that renders “high protein,” “low sodium,” “vegan,” “contains unwanted ingredient,” or “available near me” facets needs a stable contract for which facets exist, how counts are generated, and whether the server considers a facet experimental.

A practical contract for food API version negotiation

For recipe and nutrition APIs, endpoint versioning alone is too coarse. A better contract has three layers: transport version, schema capability, and domain-rule version.

Layer What it answers Example food-data consequence
Transport/API version Which request and response shape can this client use? Product search supports API 3.1 fields and pagination semantics.
Schema capability Which fields, aliases, and enum values are available? environmental_score is available; eco_score is a deprecated alias.
Domain-rule version Which calculation, taxonomy, or moderation rule produced a value? Nutri-Score calculation excludes coconut water as a fruit contribution.

A client should be able to ask for all three. For example:

GET /v3/products/search?query=oatmeal&fields=id,name,nutrients,environmental_score
Accept: application/json
X-Recipe-API-Version: 2026-07-17
X-Recipe-API-Capabilities: nutrient.vitamin_d_split,score.environmental_aliases

And the response should identify what was actually served:

{
  "api_version": "2026-07-17",
  "schema_version": "food-product-2026-07-17",
  "capabilities": {
    "nutrient.vitamin_d_split": true,
    "score.environmental_aliases": true,
    "facet.generated_statistics": true
  },
  "domain_rules": {
    "nutrition_score": "nutri-score-rules-2026-07-16",
    "ingredient_taxonomy": "ingredients-2026-07-17"
  },
  "warnings": [
    {
      "code": "deprecated_field_alias",
      "field": "eco_score",
      "replacement": "environmental_score"
    }
  ],
  "data": []
}

This is not bureaucracy. It gives product teams a way to run old and new clients at the same time, compare results, and decide when a schema change is safe to expose.

SDKs should surface capabilities, not hide them

The SDK is where many food API contracts become real. A backend API can document version 3.1, but if the JavaScript, Dart, Swift, Python, and Kotlin clients hide version choice behind defaults, production apps cannot reason about compatibility.

A builder-grade SDK should expose at least four things.

  1. Requested version: the version the app asked for.
  2. Resolved version: the version the server actually served.
  3. Known capabilities: typed booleans or enums for fields and features.
  4. Unknown-field behavior: whether the SDK preserves, drops, logs, or rejects fields it does not understand.

That last point is especially important for food data. Dropping unknown fields can be convenient, but it erases early signals that a new nutrient, allergen, sustainability attribute, or ingredient-taxonomy relationship has appeared. Preserving unknown fields is messier, but it lets advanced clients opt into migration testing before the SDK catches up.

A TypeScript-style pattern might look like this:

type FoodApiCapabilities = {
  apiVersion: string;
  schemaVersion: string;
  nutrients: {
    vitaminDTotal: boolean;
    vitaminD2: boolean;
    vitaminD3: boolean;
  };
  scores: {
    environmentalScore: boolean;
    ecoScoreAlias: "absent" | "alias" | "deprecated";
    nutritionScoreRuleVersion: string;
  };
  facets: {
    generatedStatistics: boolean;
    stableFacetUrls: boolean;
  };
};

Then client code can make explicit choices:

if (capabilities.nutrients.vitaminD2 && capabilities.nutrients.vitaminD3) {
  renderVitaminDSplit(product.nutrients);
} else {
  renderTotalVitaminD(product.nutrients.vitaminDTotal);
}

That is safer than assuming every client receives the same nutrient structure because the server documentation changed.

Operational trade-offs for API providers

Version negotiation has costs. It adds headers, metadata, tests, support burden, and release discipline. But the alternative is usually hidden complexity in customer apps.

The main trade-off is granularity. A date-stamped API version is easy to understand but may force clients to upgrade unrelated features together. Capability flags are more flexible but can become chaotic if every field has a flag. The best middle ground is to version coarse schemas and expose capabilities for high-risk food-data domains: nutrients, allergens, ingredient taxonomy, scoring rules, search facets, grocery availability, price evidence, and AI-generated fields.

Another trade-off is backward compatibility versus data correction. If a nutrition score was wrong because a rule counted an ingredient incorrectly, should old API versions preserve the old behavior? Usually no for safety- or quality-sensitive data, but the response should disclose the rule version and update time. That allows downstream systems to invalidate caches and explain why numbers changed.

A third trade-off is SDK lag. Server teams can ship daily; mobile apps may update monthly. SDKs should therefore be able to pass through unknown fields and expose raw metadata even before typed models are updated. Otherwise every server-side food-data improvement waits for the slowest client release cycle.

Migration checklist for recipe and nutrition APIs

Use this checklist when adding a field, score, facet, taxonomy term, or derived nutrition rule.

  • Classify the change: additive field, renamed field, alias, enum expansion, calculation-rule change, taxonomy update, moderation-state change, or breaking shape change.
  • Declare the domain version: record the nutrient table, scoring rule, ingredient taxonomy, or facet-statistics generation version that produced the value.
  • Expose capability metadata: let clients detect support without trial-and-error requests.
  • Define alias behavior: say whether old names are exact aliases, lossy mappings, deprecated fields, or independent measures.
  • Test at the SDK layer: add product and search tests in every supported client, not only server tests.
  • Preserve unknowns where possible: advanced clients should be able to inspect emerging fields before typed SDK releases land.
  • Publish migration examples: show how to render old and new nutrient or score fields side by side.
  • Instrument adoption: measure which API versions and capabilities active clients request before removing old behavior.

Product decisions this unlocks

For a meal-planning app, version negotiation lets the team roll out a new nutrient field only to clients that can explain it. For a grocery product, it lets price, product-statistics, and facet changes be tested without corrupting search analytics. For a food AI feature, it lets generated recipe attributes carry model and schema versions so human review can compare outputs over time. For a B2B recipe API buyer, it provides the confidence that the vendor can improve data quality without breaking production integrations.

This is also where Recipe API positioning should be concrete rather than promotional. The API value is not just having recipe and nutrition fields. It is making those fields stable enough for developers to build on, explicit enough for product managers to plan migrations, and traceable enough for technical buyers to assess risk. Version negotiation is one of the clearest signals that a food-data provider understands that job.

Sources

Start Building

One consistent schema on every response. Get a free key and ship in minutes.