Endpoint
POST
/functions/v1/decodeContract
Send mymolecules-api-version with 2026-07-10 to use the documented response contract.
{
"data": {},
"meta": {
"request_id": "req_...",
"api_version": "2026-07-10"
}
}Headers
Request
x-api-keyrequired - Server-side API key for your integration.mymolecules-api-versionrequired2026-07-10- Use the latest stable API version: 2026-07-10.Content-Typerequiredapplication/json- Required for JSON request bodies.
Response
x-request-idrequired - Request identifier. Log this value when contacting support.mymolecules-api-versionrequired2026-07-10- The API contract version used for the response.x-quota-limitoptional - Monthly quota limit for the current API key.x-quota-remainingoptional - Unreserved monthly quota remaining.x-quota-resetoptional - Timestamp when the monthly quota resets.x-operational-ratelimit-limitoptional - Current operational rate limit when available.x-operational-ratelimit-remainingoptional - Remaining operational requests when available.retry-afteroptional - Seconds to wait before retrying, only when the server provides a meaningful delay.
Inputs
- Required input_mode: unstructured or structured
- Unstructured input with foodText, foodImage, foodVoice, foodImageRemaining or attachments; media fields accept data URLs or HTTP(S) URLs
- Structured input with 1–25 items; each item requires canonical_name, a positive numeric quantity and unit (mg, g or kg). brand and attributes are optional.
Returns
- 202 queued response with job_id and status_url
- Status response with queued, processing, completed or failed
- Completed food_detected result with normalized items and meal-wide molecular totals
- Completed no_food_detected result with an empty items array when the input contains no food
Async pattern
Create the job with POST, then poll the same endpoint with job_id until the status is completed or failed.
- Poll parameter:
job_id - Status values: queued, processing, completed, failed
Error codes
missing_api_keyinvalid_api_keyscope_not_allowedquota_exceededrate_limitedinvalid_jsoninvalid_requestmethod_not_allowedunsupported_api_versionjob_not_foundbacklog_highservice_unavailableinternal_errorinput_too_largeinvalid_compound_identifiercompound_not_allowed_for_decode_scopedecode_input_mode_not_alloweddecode_scope_not_configuredstructured_items_limit_exceededfood_name_too_longfood_attributes_limit_exceededfood_attribute_too_longinvalid_food_quantityfood_quantity_exceededfood_text_too_longattachments_limit_exceededimage_inputs_limit_exceededaudio_inputs_limit_exceededunsupported_attachment_typeimage_too_largeaudio_too_largeinline_media_total_exceededmedia_total_exceededaudio_duration_exceededaudio_duration_unavailable
Request details
- Use exactly one input mode. Structured requests use items; unstructured requests use food text, images, voice or attachments.
- Structured items are not rewritten by the language model. Output item_id values are 1-based request-local positions; quantity_g is the normalized amount in grams.
- Describe textual remaining information in foodText. Use foodImageRemaining only for a separate photo of what remained.
- Decode derives the output tier from the API key. Do not send decode_profile. Send input_mode as structured or unstructured; the key must permit that mode.
- The edge:decode scope grants access to Decode. Each key has one content tier: macros returns classified macros, micros includes macros and classified micros, and molecular permits any existing compound identifier. A permitted compound is returned only when the selected food or its evidence donors provide evidence for it.
- Optional compound_identifier_ids (positive integers or numeric strings) and compound_identifier_names (exact, case-sensitive catalog names) narrow the result. Both arrays are combined and deduplicated; an empty array does not filter. If your key also has access to Decode Compound ID, use it to resolve a name before filtering.
- Without a filter, the lower tiers use their complete configured classification; Molecular uses all available direct and donor evidence. A filter never expands the key tier.
- foodText is limited to 1,500 characters; structured requests accept at most 25 items. Large media requests have separate size and attachment limits.
Response details
- POST returns HTTP 202 with job_id, queued status and status_url. It does not return the completed analysis. Poll GET /functions/v1/decode?job_id=... with the same API key until status is completed or failed.
- Status polling always uses HTTP 200 for a found job, including status: failed. Its job error is separate from the HTTP error envelope; result is returned when available.
- Every completed result has type: food_detected or type: no_food_detected. No-food is a completed domain result with result.items: [] and a null job error.
- For food_detected, each input item has status matched or unresolved. molecular_analysis contains one row per compound for the entire meal, without an item_id. If an item has no evidence for a compound or is unresolved, its contribution is assumed zero for the displayed total and that compound has incomplete: true. This is not proof of actual zero.
- measurement_value and measurement_value_min/max refer to the whole meal, not per 100 g. Interpret each number using its matching measurement_value_type and measurement_unit; null means no numeric value or bound. Min/max describe evidence spread or valid one-sided bounds, not confidence intervals.
- Evidence types are quantified (one source point), calculated (derived or combined value), less_than, greater_than, trace and unquantified (present without a numeric amount). Do not treat trace or unquantified as zero.
- provenance is interpolation if any included evidence was interpolated; otherwise it is source. Aggregated confidence is always null. references, references_min and references_max contain { id, citation } for the corresponding value or bound; incomplete totals have no min/max.
- Use include_result=false or result=none on status requests when you only need the job state.
Operational notes
- Requires x-api-key with edge:decode, one assigned content tier and at least one allowed input mode. A key may permit structured, unstructured or both.
- Job status is restricted to the creating API key and its current Decode permissions. After a tier downgrade or input-mode revocation, affected earlier jobs are no longer readable.
- An input_mode not permitted for the key returns 403 decode_input_mode_not_allowed before a job is created.
- Malformed compound filters return 400 invalid_request with details.field. Unknown compound names or IDs return 422 invalid_compound_identifier.
- If any existing requested compound is outside the API key tier, the complete request returns 403 compound_not_allowed_for_decode_scope.
- Macro tiers return 503 decode_scope_not_configured until their compound classification is configured. Internal scope configuration faults return only 503 service_unavailable without configuration details.
- 429 rate_limited is an operational limit; 429 backlog_high means Decode is overloaded; 429 quota_exceeded means the monthly quota is exhausted. Follow Retry-After only when present. A quota error is not a request to retry shortly.
- Temporary dependency failures return 503 service_unavailable; unexpected server failures return 500 internal_error. A job hidden after a key downgrade returns 404 job_not_found.
- Decode input size and count limits have specific error codes listed below; correct the request before retrying.
- POST usage is counted; polling does not create another job. Rate-limit headers are returned when quota data is available.
- Large media payloads should be sent only when needed; URLs or attachments are preferable for repeated workflows.
Examples
Request JSON
{
"input_mode": "unstructured",
"foodText": "200 g Greek yogurt",
"compound_identifier_ids": [12057]
}Structured request JSON
{
"input_mode": "structured",
"items": [
{
"canonical_name": "greek yogurt",
"brand": null,
"attributes": [],
"quantity": 200,
"unit": "g"
}
],
"compound_identifier_ids": [12057]
}Accepted POST response (HTTP 202)
{
"data": {
"job_id": "8a93f4a0-2222-4a10-8c93-bca31ae9d48b",
"status": "queued",
"status_url": "https://api.mymolecules.de/functions/v1/decode?job_id=8a93f4a0-2222-4a10-8c93-bca31ae9d48b"
},
"meta": {
"request_id": "req_...",
"api_version": "2026-07-10"
}
}Completed poll response (HTTP 200)
{
"data": {
"job_id": "8a93f4a0-2222-4a10-8c93-bca31ae9d48b",
"status": "completed",
"result": {
"type": "food_detected",
"items": [
{
"item_id": 1,
"canonical_name": "greek yogurt",
"brand": null,
"attributes": [],
"quantity_g": 200,
"status": "matched"
}
],
"molecular_analysis": [
{
"compound_identifier_id": 12057,
"compound_identifier_name": "protein",
"compound_identifier_value": "CHEBI_36080",
"measurement_value": 20000,
"measurement_value_min": 20000,
"measurement_value_max": 20000,
"measurement_value_type": "quantified",
"measurement_value_min_type": "quantified",
"measurement_value_max_type": "quantified",
"measurement_unit": "mg",
"provenance": "source",
"confidence": null,
"incomplete": false,
"references": [{ "id": 42, "citation": "Example food composition source" }],
"references_min": [{ "id": 42, "citation": "Example food composition source" }],
"references_max": [{ "id": 42, "citation": "Example food composition source" }]
}
]
},
"error": null
},
"meta": {
"request_id": "req_...",
"api_version": "2026-07-10"
}
}Code examples
curl -X POST 'https://api.mymolecules.de/functions/v1/decode' \
-H "x-api-key: $MYMOLECULES_API_KEY" \
-H 'mymolecules-api-version: 2026-07-10' \
-H 'Content-Type: application/json' \
--data '{ "input_mode": "unstructured", "foodText": "200 g Greek yogurt", "compound_identifier_ids": [12057]}'


