Rate limits protect the gateway and your wallet. They are separate from spend caps: a limit refuses a request, a cap refuses a charge.
What is metered#
| Limit | Keyed on | Refusal |
|---|---|---|
| Requests per key | The verified key | 429 Rate_Limited with Retry-After |
| Authentication attempts | Client IP, before any hashing | 401. This is what makes brute-forcing a key infeasible |
| Catalog reads | Client IP | 429. GET /v1/tools needs no key, so it needs its own window |
| Concurrent calls per creator | The creator's host | 502 after a short wait, if one creator is saturated |
Backing off#
Honour Retry-After. It is in seconds and it is not a suggestion: retrying faster meets the same window and wastes the attempt.
async function call(body, attempt = 0) {
const response = await fetch(GATEWAY, { method: "POST", headers, body });
if (response.status !== 429) return response;
// Retry-After is authoritative. Fall back to exponential backoff only
// if it is absent, and give up rather than hammering.
const after = Number(response.headers.get("Retry-After") ?? 2 ** attempt);
if (attempt >= 5) throw new Error("rate limited: giving up");
await new Promise((r) => setTimeout(r, after * 1000));
return call(body, attempt + 1);
}Concurrency#
There is no published per-account concurrency limit. The practical bound is the per-creator connection ceiling: a single creator host accepts up to 64 concurrent calls, so a burst aimed at one tool queues briefly and then returns 502 rather than waiting indefinitely.
If you are driving real volume at one tool, spread it over time rather than firing it all at once — and tell us, because that is a conversation about capacity rather than a limit to work around.