API
Rate limits
Per-account limits reported on every response, and why a 429 on the decision endpoint is a hold rather than a delay.
The headers
Every response carries the current state of your window.
| Header | Meaning |
|---|---|
RateLimit-Limit | Requests permitted in the current window for this account. |
RateLimit-Remaining | Requests remaining in the current window. |
RateLimit-Reset | Seconds until the current window resets. |
Read them on success, not only on failure. A client that first learns about the limit from a 429 is a client that will hit it during an incident, which is exactly when it can least afford to.
When you exceed it
429 with type: .../rate-limited and a Retry-After header in seconds. Back off for at least that long, with jitter, and prefer a token bucket on your side over retry storms.
On the decision endpoint, a 429 is a hold
POST /v1/decide/deposit returns a distinct 429 shape carrying fail_closed_action: HOLD.
{
"type": "https://foreshock.org/problems/rate-limited",
"title": "Too many requests",
"status": 429,
"detail": "Rate limit exceeded. No verdict was produced; treat as HOLD.",
"request_id": "req_EXAMPLE0000000008",
"fail_closed_action": "HOLD"
}
No verdict was produced. Waiting is not a substitute for a verdict: the deposit stays on HOLD until a 200 is received, however long the back-off runs.
Keeping under the limit
Screening is cheaper than deciding, and layer A is never billed — but neither is free of rate limit. Two habits keep the pressure down:
- Decide once per deposit, with a stable
Idempotency-Key. Replays are cheap and are not billed again, but a fresh key per attempt turns one deposit into many decisions. - Run the exposure cursor loop on a steady cadence rather than polling in a tight loop.
next_cursor: nullmeans you are caught up; sleep before asking again.