Skip to content
opis.Nutrition API

Quickstart

Go from nothing to a nutrition result with a test key. Test mode runs the real workflow — statuses, confirmation, webhooks, usage — but returns deterministic fixtures instead of calling the live engine, and it is free.

1. Get a test key

  1. Sign in to the console with Google and create an organization.
  2. Open API keys, choose Create key, pick your project and the Test mode.
  3. Copy the secret (opis_test_…). It is shown once. Store it in your secrets manager or an environment variable:
Shell
export OPIS_API_KEY="opis_test_…"

New organizations start as pending: test keys work straight away, and live keys are enabled after opis activates your organization.

2. Upload a photo

Send the image as multipart/form-data (up to 15 MB; JPEG, PNG or WebP). The file is sanitised asynchronously — EXIF metadata is stripped — and becomes ready a moment later. You can reference it immediately: an analysis waits in queued until its files are ready.

curl https://platform.opis.health/v1/files \
  -H "Authorization: Bearer $OPIS_API_KEY" \
  -F file=@lunch.jpg

3. Create a meal analysis

Send an Idempotency-Key so a retried request can never create a second analysis. The response is 202 Accepted with the new resource, a Location header and a Retry-After hint.

curl https://platform.opis.health/v1/meal-analyses \
  -H "Authorization: Bearer $OPIS_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
        "images": [{ "file_id": "file_01j9…" }],
        "external_id": "meal-123",
        "description": "test:succeeded"
      }'

description is an optional text hint about the meal. In test mode, values such as test:no_food or test:failed_identify select a scenario — see Test mode.

4. Poll until it settles

An analysis is settled when its status is succeeded, failed, canceled or requires_confirmation (manual mode only). Honour Retry-After between polls; production integrations should use webhooks instead.

curl https://platform.opis.health/v1/meal-analyses/mna_01j9… \
  -H "Authorization: Bearer $OPIS_API_KEY"

Or skip the loop: send Prefer: wait=25 on the create or on a GET, and the API holds the response until the analysis settles (then 200 with Preference-Applied: wait) or 25 seconds pass (202).

5. Read the result

JSON
{
  "id": "mna_01j9…",
  "object": "meal_analysis",
  "status": "succeeded",
  "mode": "test",
  "confirmation": {
    "mode": "automatic",
    "method": "automatic",
    "action": "accept"
  },
  "identification": {
    "version": 1,
    "items": [
      {
        "id": "itm_01j9…",
        "name": "Grilled chicken breast",
        "item_type": "food",
        "confidence": 0.86,
        "portion": {
          "grams": 150,
          "source": "vision_estimate",
          "confidence": 0.7
        }
      }
    ],
    "flags": []
  },
  "nutrition": {
    "items": [
      {
        "id": "itm_01j9…",
        "origin": "identified",
        "name": "Grilled chicken breast",
        "grams": 150,
        "portion_source": "vision_estimate",
        "match": {
          "source": "cofid",
          "food_code": "13-123",
          "approximate": false,
          "reason": "matched"
        },
        "nutrients": {
          "energy_kcal": {
            "value": 248.1,
            "unit": "kcal",
            "derivation": "analysed"
          },
          "vitamin_d": null
        }
      }
    ],
    "totals": {
      "nutrients": { "energy_kcal": { "value": 248.1, "unit": "kcal" } },
      "completeness": { "energy_kcal": 1.0, "vitamin_d": 0 },
      "items_unresolved": 0
    },
    "flags": []
  },
  "error": null
}

Nutrient values are portion totals at the item's grams; null means "not measured", never zero. See Nutrients for the dictionary.

Next steps

  • Decide whether a person should review items before nutrition: Confirmation.
  • Receive results by webhook: Webhooks.
  • Already have item names and grams? Use standalone nutrition estimates.
  • When you go live, create a live key (opis_live_…) — nothing else in your code changes.