For app developers

Food photo in.
Nutrition out.

The meal scan behind the NutriScan app, as one HTTPS call. Send a photo, a pack label or a line of text. Get the dish, every item with its portion, 21 nutrients and a NutriScore from A to E.

  • 3input types
  • 21nutrients per item
  • 12languages
POST /platform/v1/scans  {"type": "meal", "image_url": "…"}

Grilled chicken with rice and salad

Lean protein and vegetables balance the rice; moderate sodium from the marinade.

B
515kcal
51.9gProtein
47.7gCarbs
11.1gFat
  • Grilled chicken breast
    150 g
    248 kcal
  • Steamed white rice
    1 cup (158 g)
    205 kcal
  • Green salad with olive oil
    80 g
    62 kcal
This is the result a test key returns. Same JSON shape as a live scan.

Three ways in, one result

Every input returns the same JSON: meal name, NutriScore with its reason, totals, and each item with portion, grams, macros and micros.

"type": "meal"

Meal photo

A plate, a bowl, a takeaway box. Items and portions come from the photo.

"type": "label"

Pack label

A photo of the nutrition table on a packet, read per pack or per serving.

"type": "text"

Text

"2 eggs and a slice of toast". Useful for voice input or a search box.

Pricing

Monthly plans in US dollars. Each plan is a credit balance (1 credit = $0.001). A scan uses credits for what it really took: the AI work, web lookups, and items matched from our food database.

Starter

$49 / month

49,000 credits

About 380 to 2,400 scans

  • 30 requests a minute
  • Test key included

Growth

$199 / month

199,000 credits

About 1,500 to 9,900 scans

  • 60 requests a minute
  • Test key included

Scale

$599 / month

599,000 credits

About 4,600 to 29,900 scans

  • 120 requests a minute
  • Test key included
  • What a scan uses: 20 credits minimum. A photo with no food about 20, a short text log about 60, a normal plate about 65, a big plate with 9 items about 130. Every result says credits_used.
  • No credits: failed scans and the test key.
  • Starting a scan needs 300 credits free. With less left, live scans return 402 until you upgrade or the next month starts. Unused credits don't carry over.
  • Upgrade any time: the new balance applies straight away.

Docs

Base URL https://server-api.nutriscan.app/platform/v1. JSON in, JSON out.

Keys

You get two keys by email. ns_test_… returns a fixed sample result for free, so you can build your integration first. ns_live_… runs real scans that use your plan's credits. Send the key as a bearer token.

Call the API from your server. A key inside a mobile app can be pulled out of the binary and used by anyone.

Header
Authorization: Bearer ns_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Create a scan

POST /scans waits for the result and returns it in the same response. Send image_base64 or image_url for meal and label, and text for text.

curl
curl https://server-api.nutriscan.app/platform/v1/scans \
  -H "Authorization: Bearer $NUTRISCAN_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 7b1c5e0a-order-4411" \
  -d '{
    "type": "meal",
    "image_url": "https://example.com/lunch.jpg",
    "language": "en",
    "context": { "meal_time": "lunch", "cooking_method": ["grilled"] }
  }'
FieldTypeNotes
typestringmeal (default), label or text
image_base64stringJPEG, PNG or WebP, up to 10 MB. Send this or image_url, not both.
image_urlstringPublic https URL. We fetch it once.
textstringUp to 2,000 characters. Only with type: text.
languagestringLanguage of names and the reason: en, de, fr, es, it, pt, nl, pl, ru, tr, hi, ar. Default en.
contextobjectOptional hints that improve accuracy: meal_time, food_type, cooking_method (list), oil_quantity, consumed_quantity, location, goal, food_preferences.

Send an Idempotency-Key header to retry safely. The same key with the same body returns the first result and uses credits once. If that first try failed (402, 500), the same key runs a fresh scan. A scan can take up to 95 seconds, so set your client timeout to at least 100.

The result

Units are in the field names. Each item says where its numbers came from: food_db (our food database), web or estimate.

200 OK
{
  "id": "scn_Q2mX8rT4vK1pLw9eZb3nYh6A",
  "status": "succeeded",
  "engine_version": "2026.11.0",
  "language": "en",
  "meal": {
    "name": "Grilled chicken with rice and salad",
    "nutri_score": "B",
    "nutri_score_reason": "Lean protein and vegetables balance the rice; moderate sodium from the marinade.",
    "totals": {
      "calories": 515, "protein_g": 51.9, "carbs_g": 47.7, "fat_g": 11.1,
      "fiber_g": 2.2, "sugar_g": 1.6, "saturated_fat_g": 2.3, "trans_fat_g": 0,
      "micros": { "sodium_mg": 457, "potassium_mg": 645, "calcium_mg": 69, "iron_mg": 4.2,
                  "vitamin_c_mg": 14, "vitamin_a_mcg": 204, "omega_3_mg": 100, "…": "13 in all" }
    }
  },
  "items": [
    {
      "name": "Grilled chicken breast",
      "portion": { "value": 150, "unit": "g", "display": "150 g" },
      "grams": 150,
      "calories": 248, "protein_g": 46.5, "carbs_g": 0, "fat_g": 5.4,
      "micros": { "sodium_mg": 410, "…": "…" },
      "source": "food_db"
    }
  ]
}

A live result also has "credits_used": 64 and ends with "usage": {"credits_used": 18240, "credits_left": 30760}. Test keys return the same shape with "test_mode": true and "credits_used": 0.

Other endpoints

EndpointReturns
GET /scans/{id}A past scan by its id, for scans made with your account.
GET /usageYour plan, status (active, or expired once the period has ended), period dates, credits_included, credits_used, credits_left and scans_charged.

Errors

Every error has the same body: {"error": {"code": "plan_limit_reached", "message": "…"}}. Branch on code; the message is for people.

StatusCodeWhat to do
400invalid_request, invalid_image, image_too_large, image_url_unreachableFix the request. The message names the field.
401invalid_api_keyCheck the key and the Bearer prefix.
402plan_limit_reached, subscription_inactiveUpgrade or renew. Test keys keep working.
403key_revoked, account_suspendedEmail support@nutriscan.app.
409idempotency_conflict, idempotency_mode_conflict, idempotency_in_progressUse a new Idempotency-Key for a new body or for the other key mode, or retry shortly.
413request_too_largeSend an image of 10 MB or less.
422no_food_detectedNothing to scan. Uses the credits it took (usually 20); the message says how many.
429rate_limited, platform_busyWait for the Retry-After seconds, then retry.
500scan_failedRetry. No credits used.
503platform_unavailableRetry later.

Limits

ImageJPEG, PNG or WebP, up to 10 MB. HEIC is not supported yet; convert it on the device.
Text2,000 characters
Requests30, 60 or 120 a minute per account, by plan (all keys together)
Scan timeUp to 95 seconds
KeysUp to 10 active keys per account

Your data

We keep the photos and results you send and use them to improve our food database. Do not send photos of people or anything that identifies your users. Results are not medical advice.

Request access

Tell us what you are building. We set up every account by hand for now.

  1. You send the formWe read every request.
  2. We email a payment linkFor the plan that fits your volume, billed monthly.
  3. You get your keysA test key and a live key, by email, once the payment goes through.
Two-letter code

We use these details only to reply to you.