Food scan
Send a food photo, get back the identified products and their nutrition — the same unified scan Mouve uses in-app.
Send one photo of food or a packaged product and get back the identified items — ranked product candidates, portion estimate, and full macro + micronutrient breakdown. This is the same unified recognition pipeline the Mouve app uses (on-device signals, a vision model, OpenFoodFacts resolution, and confidence-weighted fusion), exposed as a single synchronous call.
Scope
Requires the nutrition.read scope on your token — a scan reads
an image and returns nutrition; it does not write to your account. This is a
health-data scope, so it needs the explicit acknowledgement described in
Authentication.
Request
curl -X POST https://api.mouve.it/public/v1/public/food/scan \
-H "Authorization: Bearer $MOUVE_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"image": {
"base64": "<base64 JPEG or PNG>",
"mime": "image/jpeg",
"meta": { "width": 1280, "height": 960, "capturedAt": "2026-08-30T12:00:00Z" }
},
"context": { "country": "EE", "locale": "en", "mealType": "lunch" }
}'context is optional. country is a ranking boost, never a filter — a scan
resolves worldwide regardless of it. Pass idempotencyKey to make a retried
request return the same scan instead of running the model twice.
Response
{
"sessionId": "986df63c-c9d3-4dcb-9170-fc7442c9d70e",
"status": "complete",
"items": [
{
"name": "Coca-Cola",
"category": "drink",
"status": "resolved",
"chosenCandidateId": "off:5449000000996",
"candidates": [ { "ref": { "source": "off", "id": "5449000000996", "name": "Coca-Cola" }, "score": 0.71 } ],
"portion": { "value": 330, "unit": "ml", "method": "net_quantity" },
"nutrients": { "calories": 139, "carbs_g": 35, "sugars_g": 35 },
"confidence": { "identity": 0.71, "portion": 0.9 }
}
],
"totals": { "calories": 139, "carbs_g": 35, "sugars_g": 35 },
"telemetry": { "latencyMs": { "total": 5506, "identify": 3917 }, "costUsd": 0.0023 }
}status—completewhen the scan finished,processingif it exceeded the 45s synchronous bound (rare; poll again with the sameidempotencyKey).items[].status—resolvedwhenever the item is priced: the scan always takes its best answer, so a resolved item can be logged as-is. When several candidates were plausible, the best one is chosen and the others stay incandidates; offering them is optional.needs_confirmationmeans nothing could price the item (nonutrients); leave it out or let the user look it up. Scans made before 2026-09-27 could also reportneeds_confirmationfor a priced item; treat any item withnutrientsas loggable.candidates— ranked product references with a per-source score. Absent or empty means nothing in the catalogue matched; the item still carries the model's name and a portion estimate.nutrients/totals— per the chosen candidate, per portion. Absent when nothing resolved.
Rate limits
30 scans per minute per token by default. See Rate limits.
Each scan runs a vision model; telemetry.costUsd reports the model spend for
that call.
Connect an AI assistant (MCP)
Use the Mouve MCP server to let your AI assistant read and log workouts, nutrition, and posts with your consent.
Set up a venue (agent guide)
The canonical guide for an AI agent that sets up a venue on Mouve for its owner. Connect, interview, draft, hand over the review link, verify, publish the widget.