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.
| What | Demo limit | Counts |
|---|---|---|
| Log attempts | 1 a day | POST /api/logs, POST /api/logs/from-food, MCP log_food, and every log made in the app, all together. |
| Re-resolutions | 10 a day | POST /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
idis 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
searchinginstead 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:
{
"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/weightstakesdaysfrom 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.
| Status | Meaning | Example detail |
|---|---|---|
| 401 | No usable credentials. Revoked, expired, unknown and malformed keys look the same, and their 401 carries WWW-Authenticate: Bearer. | missing bearer token, invalid API key |
| 403 | Refused: 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 |
| 404 | Not found, or it belongs to another account. | log not found, entry not found, meal not found, unknown log item |
| 409 | The 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 |
| 422 | The 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 |
| 429 | Demo 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:
{
"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.