killBottleneck is in public beta — cloud and self-host.killBottleneck is in beta.Beta on GitHub →
Skip to content

Error codes ​

Every error response is JSON with a single error field:

json
{ "error": "Invalid API key." }

The message arrives in the language of the account the key belongs to; the status code is the part to branch on.

Table ​

CodeWhenWhat to do
400A required field is missing or malformed — no title, no base_updated, an unknown status, a bad expiry date, more than 200 items in one call — or a field the endpoint does not know (the message lists the allowed ones and hints at the killBottleneck equivalent, e.g. priority → planned_on)Fix the request. Retrying it unchanged will not help.
401No Authorization header, a token that is not kb_user_… / fm_user_…, an unknown key, or an expired keyCheck the header, then check whether the key was rotated or has expired.
403A read key called a write endpointCreate or rotate the key with the read_write scope.
404The map, node, parent or task does not exist — or belongs to somebody elseThe API deliberately does not distinguish the two, so it cannot be used to probe which ids exist.
409base_updated does not match the map's current versionSomebody changed the map in the meantime. Re-read it, re-apply your change, send it again.
413Request body over 2 MBSplit the work into more calls; 200 nodes per call is the hard cap anyway.
429Over the rate limit — 120 reads or 30 writes per minute per keyBack off and retry. Reads and writes are counted separately.

The two that surprise people ​

404 instead of 403. Asking for someone else's map does not tell you it exists. This is on purpose: a 403 would confirm the id is real, which turns the API into an id oracle.

409 is normal, not a failure. Two clients editing one map — an assistant and a colleague with the editor open — will hit it. The correct handling is always: re-read the map, re-apply, retry. Never "force the write".

Do not retry a 400 or a 403

They mean the request is wrong, not that the moment was wrong. An automated retry loop on either one just burns your rate limit.

fair-code — self-hosting and internal use are free, reselling as a hosted service is not.