Docs
Open app

Getting started

  • What Kallo is
  • Log your first meal
  • Profile and targets

Logging

  • Describing a meal
  • Cheat mode
  • Manual logging
  • Barcode scanning
  • Re-logging past meals

Tracking

  • Dashboard
  • Weight
  • Nutrition

Circle

  • Friends and feed
  • Groups and chats
  • Sharing and splits

Account

  • Settings
  • Kallo Premium
  • Your data

Understanding estimates

  • How estimates work
  • Accuracy and limits

Developers and agents

  • The Kallo API
  • Kallo for AI agents

About Kallo

  • About Kallo
  • Contact Kallo

Legal

  • Terms of service
  • Privacy policy

© 2026 Kallo. All rights reserved.

PricingLinkedIn

On this page

  • Base URL
  • Meal analysis and the macro card
  • Authentication
  • Errors
  • Rate limits
  • What you may rely on
  • Cost
  • Related
  1. Docs
  2. ·
  3. Developers and agents

The Kallo API

Last updated September 28, 2026


Kallo has an HTTP API. It is the same API the web app and the mobile app use — there is no separate public one — and this page describes it honestly, including the parts that are not ready for you to build on.

The machine-readable description is at https://kallo.fit/openapi.json. It is OpenAPI 3.1, and every operation has a unique operationId and a description. Where a route validates its request body with a schema, the documented body is generated from that same schema. A CI test also compares the spec with the route handlers, so it fails if a documented body, success status or query parameter disagrees with what the handler actually parses and returns.

Base URL#

https://kallo.fit

Endpoints live under /api/v1/. The exceptions are /api/healthz, which is unversioned because it is a liveness probe rather than part of the surface, and two authenticated routes, POST /api/analyze-meal and GET /api/og/macro-card/{shareId} (both described below).

Successful responses are 200 (the waitlist confirmation link is a 307 redirect). No endpoint answers 201, including the ones that create something. Date-bucketed reads take a required tz query parameter: the offset in minutes as Date.getTimezoneOffset() reports it, from -840 to 720. If it is missing, you get a 400; the server never falls back to a stored timezone. The one exception is the nutrition overview, where tz is optional and a missing value buckets days in UTC.

Meal analysis and the macro card#

POST /api/analyze-meal turns a sentence into an estimate. It answers with a text/event-stream of server-sent events: stage progress, item_name and item_macros as dishes are found, then result (or cheat_estimate). The stream ends with analysis_complete, which carries an analysisId, or with error. Nothing is saved until you pass that id to POST /api/v1/meals/confirm. Authentication, body validation, plan and rate-limit checks all run before the stream opens and return the ordinary JSON error below. Once the stream is open, a failure can only arrive as an error event. The body is plain text made of frames (event: <type>, then data: <json>); the OpenAPI component AnalyzeMealStreamEvent describes one data payload.

GET /api/og/macro-card/{shareId} renders a shared meal as a PNG card. It needs a session and applies the same visibility rule as reading the shared meal, so a share you cannot see returns 404, the same as one that does not exist.

Authentication#

Every endpoint except the ones tagged Public needs a Supabase-issued access token for a Kallo user:

Authorization: Bearer <jwt>

There is no API key programme

You cannot sign up for a key. The only way to get a token today is to sign in as a Kallo user, which means an integration can act for exactly one account — the one whose credentials it holds. If you need something else, write to us; it is not built because nobody has needed it yet, not because it is refused.

The token carries no scopes. A bearer holds the account's full authority: it can read every meal, change the profile, and delete the account. There is no way to issue a narrower one. That is why the protected-resource metadata declares no scopes_supported — publishing a scope list nothing enforces would be worse than publishing none.

Protected-resource metadata follows RFC 9728 and names the Supabase Auth issuer as the authorization server. Kallo itself is not an authorization server: there is no /oauth/authorize, no /oauth/token, and no dynamic client registration.

Errors#

Every documented API error returns the same envelope:

{
  "error": {
    "code": "VALIDATION_FAILED",
    "status": 400,
    "retryable": false,
    "message": "date must be YYYY-MM-DD.",
    "resolution": "Correct the request using the published schema, then retry."
  }
}

code is stable and meant to be branched on. message is for a human and may be translated. retryable says whether repeating the identical request can succeed. resolution gives an agent the next action without asking it to parse the human prose. Redirect-only operations and the health probe document their own response shapes in OpenAPI.

CodeStatusRetryableMeans
VALIDATION_FAILED400noThe request body or query is wrong, including a body that is not valid JSON. Fix it.
NOT_AUTHENTICATED401noMissing, expired or invalid token.
feature_locked402noThe account's plan does not include this.
NOT_FOUND404noNo such resource, or it is not yours. The two are deliberately indistinguishable.
PROFILE_NOT_FOUND404noThe signed-in user has no profile yet. Sign in again or finish onboarding.
BARCODE_NOT_FOUND404noNo product is known for this barcode.
BARCODE_NOT_CACHED404noThe barcode was never looked up before being logged. Rescan it.
OCR_INVALID_IMAGE400noThe image could not be read as a label photo.
OCR_NO_LABEL_DETECTED422noNo nutrition table was found in the photo.
CONFLICT409noThe resource changed under you, or a client-generated id you sent (for example a meal's mealId / newMealId) is already taken. Generate a new id.
CIRCLE_LIMIT_REACHED409noSomeone else's group or friend limit is full.
PAYLOAD_TOO_LARGE413noThe body is over the endpoint's cap (64 KB for most JSON endpoints). Send less.
RATE_LIMITED429yesBack off. Honour Retry-After when it is present.
RATE_LIMITER_UNAVAILABLE503yesThe limiter could not decide on a paid route. Honour Retry-After.
PIPELINE_TIMEOUT504yesMeal analysis took too long.
INTERNAL500yesOur fault.

Authentication is checked before the body is read. A request without a valid token gets NOT_AUTHENTICATED, whatever its body looks like, so a 400 or 413 means the token was accepted. A collision on a client-generated id is always the same CONFLICT, whether the id is yours or another account's; retrying the same id will never succeed. If a save timed out and you are unsure whether it landed, refetch the day before retrying with a fresh id.

Any /api/ path with no handler returns a 404 in this same JSON shape, never an HTML page — so a mistyped endpoint is something your client can parse rather than something it has to guess at.

Rate limits#

There is no published per-endpoint quota. Limits exist and are applied per user and, for the unauthenticated endpoints, per IP address. When you hit one you get RATE_LIMITED with a Retry-After header where the wait is known. Treat that header as authoritative rather than backing off on a fixed schedule.

What you may rely on#

Operations tagged Public in the spec are safe to depend on:

  • GET /api/healthz
  • POST /api/v1/waitlist
  • GET /api/v1/waitlist/confirm
  • GET /api/v1/groups/invite/{slug}
  • GET /api/v1/barcode/image/{code}

Everything else is marked x-internal: true. Those endpoints exist to serve Kallo's own clients. They are versioned under /v1, but that version is not a promise to third parties — a shape can change to suit the apps without a bump. If you build on one, pin your expectations loosely and read the spec again before you upgrade.

Cost#

The free tier is real, needs no card, and is enough to use the API for anything you can do in the app on a free account. There is no separate API price and no sandbox environment — you develop against production, with your own account and your own data.

Related#

  • How an agent should use Kallo
  • OpenAPI specification
  • Your data — what is stored, and what reaches the model
  • Contact
PreviousAccuracy and limitsNextKallo for AI agents