Rate limits
Token-bucket, with state in Postgres so buckets are shared across serverless instances.
Per-caller limits
Per-target-domain limit
This is separate from and additional to your own budget. It exists so the API cannot be used to hammer a third party's site — the audited domain gets a say in how often it is crawled, even though it is not the one holding the key.
The caller budget is checked first; the target-domain budget is only consumed if the
caller budget passed. Domain keys normalize scheme, trailing slash, and trailing dots,
so example.com. and example.com share one bucket.
When rate limited
{
"error": {
"code": "RATE_LIMITED",
"message": "Rate limit exceeded for tool \"run_audit\". Retry after 137s.",
"details": { "retry_after_seconds": 137 }
}
}
details.retry_after_seconds is always present. Back off for that long rather than
retrying immediately — a retry inside the window consumes nothing and returns the same
error.
The limiter fails closed
If the shared Postgres limiter is unavailable on a deployed environment, requests are
refused with retry_after_seconds: 5 rather than served unlimited. An unenforced
limit on a tool that spends money per call and fetches arbitrary URLs is worse than a
brief outage.