Zum Hauptinhalt springen

Products

A product is an article you buy or sell as a finished item: a packaged good, a ready-made component you buy in, an article from a supplier's catalogue. Its composition usually comes from the ingredient declaration on the packaging.

You store it once, under your own id, and read back its calculated footprint. A stored product can also be used in a recipe — reference it with type: "products" (see Ingredient Types).

Storing a product

PUT /products/{id}, with your own id in the URL. The response already contains the finished calculation — there is no second request to make.

PUT /products/carrot-puree-500g
{
"id": "carrot-puree-500g",
"gtin": "00123456789023",
"names": [{"language": "de", "value": "Karottenpüree"}],
"amount": 500,
"unit": "gram",
"producer": "ACME Foods",
"ingredients-declaration": "Karotten, Wasser, Salz",
"nutrient-values": {
"energy-kcal": 35,
"fat-gram": 0.2,
"protein-gram": 0.9,
"carbohydrates-gram": 7.5
}
}

The ingredients-declaration is what tells us what the product is really made of, so include it whenever the packaging has one. Without it, the product is identified from its name alone.

To upload a catalogue, use POST /products/batch. Each product is handled on its own and gets its own entry in the response, so one product that cannot be calculated does not hold up the rest. Match the entries to your request by product-id, or by the request-id you sent along.

Once uploaded, keep your local copy in step with GET /products?updated-since — see Keeping your data up to date.

Keeping your data up to date

The values we calculate are not frozen. They improve over time — an ingredient gets matched more precisely, background data is refreshed — so a product you uploaded months ago may have a better CO₂ value today.

GET /products returns the ids of your products. Add updated-since with the date of your last synchronisation, and you get back only the ones worth fetching again:

GET /products?updated-since=2026-07-01
{
"products": ["carrot-puree-500g", "tomato-sauce-400g"]
}

That is: the products you changed, the ones never calculated, and the ones whose values may have moved because something they rely on improved. Fetch each with GET /products/{id}. A product uploaded through POST /products/batch behaves in this feed exactly like one uploaded with a single PUT.

One id namespace

Products and ordinary ingredients share a single id namespace: an id is held by either a product or an ingredient, never both at once. GET /products/{id} returns whichever holds it, and the type field tells you which:

typeWhat holds the id
productsA product. It changes only through the product endpoints.
conceptual-ingredientsAn ordinary ingredient. A recipe or supply defines it, and any recipe or supply changes it.

A recipe or supply that sends an id modifies the ordinary ingredient at that id, never a product. To store a product under an id an ordinary ingredient already holds, set convert-ingredient-to-product=true.

Product Properties

Request Properties

PropertyTypeRequiredDescription
idStringYesYour unique product identifier
namesArrayYesProduct name as array of language objects: [{"language": "de", "value": "Karottenpüree"}]
ingredients-declarationStringBest practiceList of ingredients as declared on the packaging. Parsed to determine what the product is made of.
nutrient-valuesObjectBest practiceNutrient values per 100g as declared on the packaging (see Reference)
gtinStringBest practiceGlobal Trade Item Number (www.gtin.info)
amountFloatBest practiceAmount of the product in the specified unit. Default: 100
unitStringNoUnit: kg, gram, or liter. Default: gram
producerStringBest practiceThe producer or brand of the product
dateStringNoProduction date for seasonal calculations. Format: YYYY-MM-DD
originStringBest practiceProduction location: postal address or country
transportStringBest practiceMeans of transport: air or ground
productionStringBest practiceProduction method: standard, greenhouse, organic, fair-trade, farm, wild-caught, sustainable-fish
processingStringNoProcessing level: raw, unboned, boned, skinned, beheaded, fillet, cut, boiled, peeled
conservationStringBest practiceStorage method: fresh, frozen, dried, conserved, canned, boiled-down
packagingStringNoPackaging type: none, plastic, paper, pet, tin, alu, glas, cardboard, tetra

Response Properties

The product is returned under product, next to a statuscode that says whether it could be calculated.

PropertyTypeDescription
statuscodeInteger200 when the product was calculated. See Status Codes.
messageStringExplains the statuscode when it is not 200
product-idStringThe id of the product this entry belongs to
productObjectThe product and its calculated values (below)
PropertyTypeDescription
idStringProduct identifier
typeStringproducts (write-protected) or conceptual-ingredients (any recipe may overwrite it)
namesArrayProduct names as array of language objects
co2-valueIntegerCO₂ emissions in grams CO₂e for the specified amount (see the note below)
ratingStringCO₂ rating (A-E). A is best, E is worst.
eaternity-awardBooleantrue if the product is climate friendly
co2-value-improvement-percentageFloatComparison of this product's CO₂ footprint per food unit to the average
co2-value-reduction-valueFloatGrams of CO₂ saved compared to an average product providing the same nutritional value
food-unitFloatNutritional value of the product for the specified amount
amountFloatProduct amount, echoed back from your request
unitStringUnit of measurement
gtinStringGlobal Trade Item Number
dateStringProduction date
info-textStringNotes on the calculation (e.g. "No cooking date was provided.")
ingredientsArrayThe ingredients the product is made of, with their individual ratings and CO₂ values
ingredients-declarationStringThe original ingredient declaration text (when ingredients-declaration=true is set)
nutrient-values-estimated-per-specified-amountObjectBackend-estimated nutrient values scaled to the specified amount (when nutrient-values=true is set)
indicatorsObjectEnvironmental indicators (when indicators=true is set)
A product's footprint is for the amount you send

co2-value and food-unit are calculated for the amount (and unit) on your request — ten times the amount is ten times the footprint. 500 gram and 0.5 kilogram give the same result. If you send no amount, the footprint is for one kilogram.

When you instead reference the product from a recipe, it is the amount on that recipe line that scales it — the product's own amount does not carry over.

Response Example

{
"statuscode": 200,
"message": "",
"product-id": "carrot-puree-500g",
"product": {
"id": "carrot-puree-500g",
"type": "products",
"names": [{"language": "de", "value": "Karottenpüree"}],
"co2-value": 478,
"rating": "A",
"eaternity-award": true,
"co2-value-improvement-percentage": -41.0,
"co2-value-reduction-value": -140.0,
"food-unit": 0.3,
"amount": 500,
"unit": "gram",
"gtin": "00123456789023",
"info-text": "No cooking date was provided.",
"ingredients-declaration": "Karotten, Wasser, Salz",
"ingredients": [
{
"names": [{"language": "de", "value": "Karotten"}],
"amount": 100.0,
"unit": "gram",
"rating": "A",
"co2-value": 32,
"foodUnit": 0.11
}
]
}
}

Status Codes

Next to the usual status codes, products use:

CodeMeaningWhat to do
612The product you referenced does not existStore the product first, then send the recipe
613A write a product does not accept — either a recipe tried to define an ingredient under a product's id, or a product write would convert an existing ingredientReference the product with type: "products" instead of defining it. To convert an ingredient on purpose, add convert-ingredient-to-product=true.

Endpoints