Skip to main content

Errors and limits

Error format​

An error has an HTTP status and this body:

{
"error": {
"code": "invalid_parameter",
"message": "status must be planned, spent or cancelled"
}
}
  • code is stable: your code acts on it.
  • message is for people: show it or log it, but don't parse it. Its text may change.

Error codes​

StatuscodeWhat happenedWhat to do
400invalid_limitlimit is not an integer from 1 to 200.Fix the request.
400invalid_cursorcursor is not a next_cursor from this API.Send the next_cursor you got, as is.
400invalid_parameterA query parameter can't be read. The message names it and says what it takes.Fix the request.
401missing_keyNo Authorization: Bearer gak_… header.Send your key in this header.
401invalid_keyThe key is unknown, expired or revoked, or its user left the organization.Create a new key. Retrying won't help.
403personal_key_requiredThe route reads budgets or expenses, and the key doesn't act as a person.Use a personal key.
404not_foundThe item doesn't exist, or isn't shared with you. The API doesn't say which.Check the id, and that the budget is shared with you in the app.
429rate_limitedToo many requests with this key.Wait for the seconds in the Retry-After header, then retry.
500internal_errorSomething failed on our side.Retry later, waiting longer each time.

The key is checked before anything else: a request with a bad key and a bad parameter gets the 401.

A path that doesn't exist, such as /api/v1/invoices, gets a 404 with a plain-text body, not the JSON above.

Rate limit​

Each key can make 120 requests per minute.

Over it, the API answers 429 rate_limited with a Retry-After header, in seconds. Wait that long, then retry. The API sends no header with your remaining quota.

A broader limit per IP address also protects the service. A normal integration never reaches it. Its 429 also has Retry-After, but its body is not the JSON above: rely on the status code.

To stay under the limit:

  • Read lists with limit=200, so you make fewer requests.
  • Filter on the server: ask /expenses for one budget or one period rather than reading everything.
  • Cache what changes rarely, such as members and vendors.

When to retry​

StatusRetry?
429Yes, after Retry-After.
500, 502, 503, 504Yes, a few times, waiting longer each time: 1 s, then 2 s, then 4 s.
Any other 4xxNo. Fix the request or the key first.

Every route is a GET that changes nothing, so retrying is always safe.