Rate Limiting
The MCP server accepts 600 requests per minute per person and
organization — the token's sub and org, not the token itself.
Switching tokens does not reset the budget: a new token, from a new OAuth authorization or a new personal token, of the same person in the same organization lands in the same bucket. The same person in another organization has a budget of their own.
A tool call is never cheaper than the route it mirrors: the request is
counted once in the MCP's own bucket and, when it is a tools/call, again in
the v1 API bucket (600 per minute), with the same person-and-organization key.
Routes with a limit of their own apply it too: kadu_executions_search has
another 30 per minute, like GET /v1/executions/search. This is deliberate —
an agent sweeping listings in a loop must not get around the limit the same
sweep would pay through the API.
Attempts with a refused token are throttled per IP, not per token: a token that does not verify cannot have a bucket of its own. A request with no token at all — every client's first step, before OAuth — does not count as an attempt.
When the limit is reached​
When the MCP bucket runs out, the server answers 429 — the JSON-RPC message
is never processed:
HTTP/1.1 429 Too Many Requests
Retry-After: 37
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1793280037
Cache-Control: no-store
Content-Type: application/json
{ "error": { "code": "TOO_MANY_REQUESTS", "message": "Rate limit exceeded." } }
Wait the Retry-After seconds before the next request. X-RateLimit-Limit
and X-RateLimit-Remaining give the bucket size and what is left;
X-RateLimit-Reset is when it renews, in seconds since the Unix epoch.
When the route bucket runs out, the HTTP request is a 200 and the tool result
comes back isError with the API error, RATE_LIMITED — see
Errors. Wait a few seconds before calling again. The API limits
are in Rate limits.
Spending less​
An agent spends budget like your backend does, and also pays context for every row it brings back. Three habits solve almost everything:
- Filter before paginating.
kadu_executions_list { status: "failed", started_at_gte: "2026-10-01" }costs one request; sweeping every execution to filter in the model costs dozens. - Read only what is new. To follow a running execution, pass
after_sequencetokadu_executions_events_listandafter_idtokadu_executions_logs_list, instead of re-reading everything on each round. - Do not poll in a loop. An approval by link is checked when the person says they decided, not every few seconds.