Die Foodlex-API
Ein Rezept als JSON hinein, die gerechneten Werte als JSON heraus: Nährwerte, Allergene und die fertige Zutatenliste. Gedacht für die Warenwirtschaft, den Shop oder ein eigenes Werkzeug — überall dort, wo die Zahlen gebraucht werden, ohne dass jemand sie in der App abtippt.
Drei Endpunkte
Alle unter https://app.foodlex.app/api/v1/, alle POST.
| Endpunkt | Antwort |
|---|---|
/v1/nutrition |
Nährwerte je 100 g, je Portion und roh — ungerundet zum Weiterrechnen, dazu die gerundeten Etikettwerte |
/v1/allergens |
Die vierzehn Allergengruppen nach Anhang II der Verordnung (EU) Nr. 1169/2011, mit der Fundstelle im Text |
/v1/ingredient-list |
Die Zutatenliste, absteigend nach Gewichtsanteil, mit Allergen-Hervorhebung und Mengenkennzeichnung (QUID) |
Was sie nicht tut
Die API ist zustandslos: Sie legt kein Rezept an, liest keines und ändert nichts an Deinen Daten. Jeder Aufruf steht für sich — was hineingeht, wird gerechnet, und danach bleibt nichts zurück.
Anmeldung
Ein Schlüssel, eine Kopfzeile:
Authorization: Bearer flx_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Der Schlüssel hat die Form flx_live_ plus 32 Zeichen. Er wird
genau einmal gezeigt, beim Anlegen — gespeichert ist nur sein
SHA-256, aus der Datenbank ist er nicht wiederherstellbar. Verloren heißt: sperren
und neu vergeben.
Ein Aufruf, vollständig
curl -sS https://app.foodlex.app/api/v1/nutrition \
-H "Authorization: Bearer $FOODLEX_KEY" \
-H 'Content-Type: application/json' \
-d '{
"market": "DE",
"language": "de",
"preparationLossPercent": 12,
"portions": 8,
"ingredients": [
{ "name": "Weizenmehl Type 405", "amountGrams": 500,
"nutrients": { "energyKj": 1443, "energyKcal": 341, "fat": 1.1,
"fatSaturated": 0.2, "carbs": 71.8, "sugar": 1.2,
"protein": 10.6, "salt": 0.01 } },
{ "name": "Butter", "amountGrams": 250,
"nutrients": { "energyKj": 3059, "energyKcal": 741, "fat": 82.0,
"fatSaturated": 52.0, "carbs": 0.6, "sugar": 0.6,
"protein": 0.7, "salt": 0.02 } }
]
}'Die Antwort, gekürzt:
{
"apiVersion": "1",
"market": "DE",
"language": "de",
"weights": { "rawGrams": 750, "finalGrams": 660, "portionGrams": 82.5 },
"per100g": { "energyKj": 2251.8939393939395, "energyKcal": 539.0151515151515, … },
"perPortion":{ "energyKj": 1857.8125, … },
"labelPer100g": { "energyKj": "2.252 kJ", "energyKcal": "539 kcal",
"fat": "32 g", "fatSaturated": "20 g", "carbs": "55 g",
"sugar": "1,1 g", "protein": "8,3 g", "salt": "0,02 g" },
"warnings": []
}per100g ist ungerundet, mit allen Stellen. Das ist
Absicht: Wer weiterrechnet — auf eine andere Portionsgröße, in eine eigene
Tabelle — soll nicht mit einem gerundeten Zwischenwert anfangen. Gerundet wird
einmal, am Ende, und das steht in labelPer100g.
"32 g" an einem gewöhnlichen Leerzeichen trennt, findet nichts.
Und labelPer100g gibt es nur für DE,
FR und GB, die drei unter EU-Recht: Australien rundet
auf drei signifikante Stellen, die USA stufenweise auf eine Portionsgröße nach
RACC. Dort fehlt das Feld, und labelNote sagt in Ihrer Sprache
warum — eine falsch gerundete Zahl sieht richtig aus. Die Werte in
per100g stimmen für jeden Markt.
Märkte und Sprachen
Beides gibt die Anfrage mit, und beides deckt den vollen Umfang der App ab:
market |
DE · FR · AU · US · GB — entscheidet über das Regelwerk. Vorgabe DE |
|---|---|
language |
de · en · fr — wirkt auf Meldungen, Bezeichnungen und das Zahlenformat. Vorgabe de |
Die Kennungen der Allergene bleiben dabei englisch, auch in einer deutschen Antwort: Sie sind Schlüssel, keine Anzeige. Der Klartext steht je Markt und Sprache daneben.
Grenzen und Kontingent
| Rumpf | 1 MB |
|---|---|
| Zutaten je Anfrage | 200 |
Text bei /v1/allergens | 20.000 Zeichen |
| Aufrufe je Monat | Free 0 · Starter 0 · Pro 2.000 · Team 20.000 |
Gezählt wird je Workspace, nicht je Schlüssel: Das Kontingent hängt am Tarif, und zwei Schlüssel desselben Betriebs sollen sich nicht gegenseitig Luft verschaffen.
Fehler
Eine Form, auf allen Endpunkten, bei jedem Status:
{ "apiVersion": "1",
"error": { "code": "bad_request",
"message": "Die Anfrage passt nicht zum Schema: …",
"field": "ingredients[2].amountGrams" } }| Status | code | Wann |
|---|---|---|
| 400 | bad_request | Rumpf ist kein JSON oder passt nicht zum Schema |
| 401 | unauthorized | Kein Schlüssel, unbekannt, gesperrt oder abgelaufen |
| 403 | forbidden | Der Tarif dieses Workspaces schließt die API aus |
| 405 | method_not_allowed | Alle Endpunkte sind POST |
| 413 | payload_too_large | Rumpf größer als 1 MB |
| 429 | quota_exceeded | Monatskontingent aufgebraucht |
| 500 | internal_error | Unerwartet |
| 503 | unavailable | Die Schlüsselprüfung ist nicht erreichbar |
Versionierung
Die Fassung steht im Pfad, nicht in einer Kopfzeile — so erscheint sie in jeder Fehlermeldung, in jedem Log und in jeder Zeile Beispielcode.
| Zugesagt | Ein Feld, das in v1 dokumentiert ist, verschwindet nicht und ändert seine Bedeutung nicht |
|---|---|
| Zugesagt | Ein unbekanntes Feld in der Anfrage wird ignoriert, nicht abgewiesen |
| Erlaubt | Neue Felder in der Antwort — ein Client muss unbekannte Felder überlesen |
| Nicht zugesagt | Die Reihenfolge der Schlüssel im JSON. Und die Zahlen selbst: Korrigiert sich eine Rundungsregel oder ein Allergenmuster, ändert sich das Ergebnis — das ist eine Fehlerbehebung, kein Vertragsbruch |
Soll sich ein Feld in seiner Bedeutung ändern, kommt /api/v2/… daneben.
Einen Schlüssel bekommen
Die API ist im Pro-Tarif enthalten und kostet nichts extra. Schreib mir, wofür Du sie einsetzen willst, dann lege ich den Schlüssel an und schicke ihn Dir über einen Weg, der ihn nicht im Postfach liegen lässt.
Die Rechnung bleibt Deine: Foodlex rechnet und setzt, und es sagt, woher jede Zahl kommt. Die fachliche und rechtliche Prüfung vor dem Druck bleibt bei dem, der das Etikett in Verkehr bringt.