Cap
An armed cap refuses the request before your provider is called and answers 402. The body names the window that was reached.
On this page
Cap is a dollar limit on your spend that the gateway enforces in the request
path. An armed cap refuses the request before it reaches your provider and
answers 402. An alert only warns; a cap refuses.
What a cap is
A cap is an amount on a scope over a window:
- the scope is your whole organization, or one API key;
- the window is a day, a week, a month, a year, or the lifetime of the cap, on UTC boundaries (Caps and windows);
- armed means its hard stop is on. A cap with the hard stop off counts spend and sends its alerts, and refuses nothing.
Caps are set in the platform, on Settings → Organization → Limits, and a key's cap also on that key's own row (Where a cap is set). The guide sets one up end to end.
What the request carries
Nothing. A cap is armed in the platform and pushed to every gateway node; the request carries only your key. The same request is served one minute and refused the next because the cap's counter moved, not because the request changed.
What the gateway does
Before your provider is contacted, the gateway runs admission in order: it authenticates the key, applies throttling, then checks every armed cap that covers the request, the organization's and the key's. For each cap it adds the least this request could cost to the spend already counted in the cap's current window, and refuses if the total reaches the cap. Where several caps cover a request, on one scope or on several, the tightest decides.
A refused request is never forwarded. There is no provider call, no token and no cost for it.
What comes back
Served
Your provider's response, with the kind and the window of the tightest armed cap the request was bound by:
x-recovea-request-id: req_0192f3a4b5c6d7e8f9a0b1c2d3e4f5a6
x-recovea-budget-scope: organization
x-recovea-budget-window: monthx-recovea-budget-scope is organization or key. No figure rides a served
response: what is left on a cap is in the platform, not on the wire. With no
armed cap over the request, neither header is sent.
Refused at the cap
HTTP/1.1 402 Payment Required
{
"error": {
"message": "Monthly budget reached. Traffic resumes at reset, or raise the cap.",
"type": "insufficient_quota",
"param": null,
"code": "budget_exceeded"
}
}The refusal carries the same two headers, naming the cap that refused:
x-recovea-budget-scope: organization and x-recovea-budget-window: month.
The status, error.type, error.code, the null param and the field order
message, type, param, code are the contract, and they are the same for every
cap and every window. Branch on them. The message is prose: it names the window
that was reached, and it may be reworded.
| Window | The message on an organization cap |
|---|---|
day |
Daily budget reached. Traffic resumes at reset, or raise the cap. |
week |
Weekly budget reached. Traffic resumes at reset, or raise the cap. |
month |
Monthly budget reached. Traffic resumes at reset, or raise the cap. |
year |
Yearly budget reached. Traffic resumes at reset, or raise the cap. |
lifetime |
Lifetime budget reached. This budget does not reset; raise the cap to continue. |
A cap on one API key says so after the window: "Monthly budget reached for this
API key. Traffic resumes at reset, or raise the cap." The sentence is built as
the window's opening, the scope's words, then the window's closing. No refusal
body names the cap, the key, the project or the run; the headers name the kind,
and x-recovea-request-id is the join to your own record.
Refused in pause mode
An armed cap refuses in one of two modes, chosen on the cap form under At the
cap: Block (402), the default, or Pause (429 with Retry-After). Pause
answers 429 with error.type rate_limit_error, the same error.code
budget_exceeded, and a Retry-After of the seconds until the window resets.
Branch on the code, not the status: nothing else separates a cap's 429 from a
throughput 429.
Refused because spend could not be confirmed
When an armed cap cannot confirm the spend it is counting, the request is
refused with 503, Retry-After: 1 and error.code budget_unverifiable,
never with the 402. The condition clears on its own.
Fail-open and fail-closed has the
body and the reason.
What a cap does not do
- It stops the next request, not the ones already running. The request that crosses the line was admitted and finishes, and so do requests running at the same moment. A scope's total can end above its cap by about the cost of the requests in flight when it was reached. If you need a hard stop at a number, set the cap below it by the cost of your largest request.
- It counts the least a request could cost, never more. The estimate added at admission is the lowest input rate on the price list applied to the size of the request, so a cap trips late rather than early.
- It bounds routed traffic only. A provider key used directly never enters the gateway, and no cap reaches it.
Warnings and notices
Recovea does both: the warning on the way up, the refusal at the line. Threshold alerts are Team and above; a refusal mails your owners and billing contacts on any plan, Free included, once per budget each month. Alert thresholds has the percentages.
Caps work on every plan. On Free a cap's amount has a ceiling; the plans and their figures are on the pricing page.