Skip to content

Rate limits

The API has a per-minute request budget. It belongs to the company, not to the key.

PlanAPIRequests per minuteActive keys
FreeNo00
StartYes603
PlenoYes18010
SupraYes60025

This table is generated from the code that actually decides: the same function the API calls on every request to know whether the call goes through.

The ceiling in force for your company right now comes in the GET /public/v1/me response, in limits.requests_per_minute. Read it from there instead of hard coding the number: the company can change plan, and the panel also allows adjusting the value of one company in particular.

A plan change takes up to a minute to take effect on the API, because the resolved plan is kept in memory for that long.

Every response that goes through the counter carries three headers:

HeaderWhat it isHow to use it
RateLimit-LimitThe per-minute ceiling of the companyCompare it with what you plan to fire
RateLimit-RemainingHow many requests still fit in the windowBelow a margin of your own, slow down before taking a 429
RateLimit-ResetIn how many seconds the next slot opensUse it as a minimum interval once RateLimit-Remaining reaches zero

On a 429 a fourth one shows up:

HeaderWhat it is
Retry-AfterHow many seconds to wait before trying again

The window slides: it looks at the last 60 seconds from now, not at the clock minute. That is why RateLimit-Reset drops gradually instead of resetting all at once.

A company with no application ceiling does not go through the counter, and then these headers do not show up. In that case limits.requests_per_minute comes back null in GET /public/v1/me.

{
"error": {
"type": "rate_limit_error",
"code": "rate_limit_exceeded",
"message": "Per-minute request limit reached. Try again shortly.",
"request_id": "3d81b6ac-2f45-4a0e-9c7b-1e5f8a2d4c60",
"doc_url": "https://docs.fatureihoje.com/en/errors#rate_limit_exceeded"
}
}
  1. Wait what Retry-After says. It comes in seconds and it is computed on the real window, it is not a guess.
  2. Back off progressively. If a second 429 comes right after, double the wait on every attempt, up to a ceiling of your own.
  3. Spread it out a little. Add a few random milliseconds to the wait. Without that, several queues that took a 429 at the same instant come back at the same instant.
  4. Never retry in a tight loop. Retrying right away does not help, and it delays the good calls of the same company, which share the same counter.

A call refused by permission is different: the 403 happens after the counter and does spend budget, on purpose. Probing a route the key cannot use is not free.

Terminal window
curl -sS -D - -o /dev/null https://api.fatureihoje.com/public/v1/me \
-H "Authorization: Bearer $FH_API_KEY"

-D - prints the headers and -o /dev/null throws the body away.

The per-minute budget is not the only counter. Authentication has a ceiling of its own for failures per IP: a wrong key repeated many times within the same minute starts getting a 429 even before the key is checked. That counter only counts what went wrong, so normal use never touches it. It is described in Authentication.

Both use the same code, rate_limit_exceeded, and both carry Retry-After. The difference shows in the context: if your calls are being accepted and suddenly a 429 arrives, it is the company budget; if they are being refused with 401 and then turn into 429, it is the failure ceiling.

The table above also shows how many active keys each plan allows. A revoked key and an expired key do not count; a key living through a rotation grace period does count, because it still authenticates.

Going past that number returns plan_limit_reached, with status 403. Rotating an existing key does not hit that limit; creating one more does.

  • Errors: the whole catalog, with rate_limit_exceeded and plan_limit_reached.
  • Pagination: sweeping a long list is what spends the most budget.