Operate

Limits & errors

What a key can spend, how long a request may take, and what comes back when something is refused.

Demo accounts

Tally has one owner account with no limits. Every other account is a demo account, and a key always carries its account's limits. Demo limits count per UTC day and reset at the next UTC midnight.

WhatDemo limitCounts
Log attempts1 a dayPOST /api/logs, POST /api/logs/from-food, MCP log_food, and every log made in the app, all together.
Re-resolutions10 a dayPOST /api/resolve, POST /api/resolve/tier3, a free-text POST /api/resolve/answer, and each row MCP retry_lookups looks up.
  • A retry with the same log id is answered from what was stored and costs nothing. Only a retry that has to re-run a log that died part-way counts again.
  • If the agent only asked you a question and staged nothing, the attempt is handed back so your answer can use it, at most 3 times a day.
  • During a log, a food Tally has never seen is looked up, and on a demo account that lookup also counts against the 10. Past the 10, those rows come back searching instead of failing the log.
  • Reads, commits, entry edits and deletes, weigh-ins, saved meals and settings are not counted.

GET /api/me (or the MCP tool whoami) shows where you stand: demo is {limit, used_today, resets_at}, or null for the owner. Over the limit, the API answers 429:

429 Too Many Requestsjson
{
  "detail": "Demo accounts can log once a day — resets at 2026-09-24T00:00:00+00:00",
  "resets_at": "2026-09-24T00:00:00+00:00",
  "used": 1,
  "limit": 1
}

Request time

Every request must finish inside the hosting platform's 120-second limit. Two kinds of call can come close. Logging (POST /api/logs, MCP log_food) and MCP retry_lookups run on their own 105-second clock, and a row whose lookup does not fit comes back searching rather than holding the request open. The row fixes (POST /api/resolve, POST /api/resolve/tier3 and a free-text POST /api/resolve/answer) can run a fresh lookup with no clock of its own, which often takes 15 seconds or more and can run up to the 120-second limit. Set your HTTP client's timeout (or your MCP client's tool timeout) to at least 120 seconds for all of these. Everything else answers in seconds.

A lost response is safe to retry: send POST /api/logs again with the same id (or call MCP log_food with the same id) and you get the stored result instead of a second log. Commits are safe to retry too.

Other limits

  • At most 10 active API keys per account, and 50 stored in all: creating a key deletes the oldest revoked or expired ones past 50 (API keys).
  • GET /api/weights takes days from 1 to 3660; a weigh-in must be between 30 and 300 kg.
  • History windows are fixed: 3d, w, m, y.
  • There is no separate per-key rate limit today. Tally is a one-person project, so please pace bulk jobs.

Errors

Errors are JSON with a detail field. For most errors it is a short sentence from the code; the table quotes some of them.

StatusMeaningExample detail
401No usable credentials. Revoked, expired, unknown and malformed keys look the same, and their 401 carries WWW-Authenticate: Bearer.missing bearer token, invalid API key
403Refused: a read-only key on a write, a key on /api/keys, an unverified email, or a signed-in app request from another site (API keys are not checked for Origin).this API key is read-only, API keys cannot do this; sign in to the app, email not verified, origin mismatch
404Not found, or it belongs to another account.log not found, entry not found, meal not found, unknown log item
409The thing is in the wrong state: the log was already committed or discarded, a row was already committed, a name is taken, or the 10-key cap is reached.log already committed, log is discarded, item already committed
422The request is malformed. Body validation gives a list (below); a route's own check gives a sentence.nothing to change, qty must be positive, empty query
429Demo quota spent. Also carries resets_at, used and limit (above).Demo accounts can log once a day — resets at …

A body that fails validation, here a log with no text:

422 Unprocessable Contentjson
{
  "detail": [
    {
      "type": "missing",
      "loc": ["body", "text"],
      "msg": "Field required",
      "input": {
        "id": "7c0e5a0e-2f4b-4f7e-9d0a-5b8f1f3c2a91",
        "captured_at": "2026-09-23T15:15:00Z",
        "tz": "UTC"
      }
    }
  ]
}

Errors over MCP

Authentication failures are plain HTTP: 401 with a JSON detail and WWW-Authenticate: Bearer. Once a request is in, a tool that fails returns a tool result marked as an error, with a readable message, such as the demo quota and when it resets. Your agent sees the message and can tell you.