Rate limits
Per-key request limits, the separate agent-run caps, and the auto-apply ceiling.
Three independent limits, for three different reasons.
Per-key request limits
| Limit | |
|---|---|
Reads (GET) | 120 / minute |
| Writes (everything else) | 30 / minute |
Keyed by API key, so one key hitting its limit does not affect another. Every successful response carries the current state:
RateLimit-Limit: 120
RateLimit-Remaining: 118
RateLimit-Policy: 120;w=60Exceeding it returns 429 with Retry-After and retry_after_ms in the body.
These are a runaway-loop backstop, not a quota — they are held in memory per server instance, so the effective ceiling across a scaled deployment is higher. Do not build a system that depends on the exact number.
Agent-run caps
Runs cost real money, so their limits live in the database and hold across every instance:
| Limit | Why | |
|---|---|---|
| Concurrent runs | 1 per account | Two agents optimizing one account would propose against each other's stale reads. |
| Runs started | 20 / hour per workspace | A loop that starts runs is expensive in a way a loop that reads is not. |
Starting a run while one is in flight returns 409 — wait, or cancel the queued one.
The auto-apply ceiling
Separate from rate limiting, and the one worth knowing about: at most 20 changes per account per
24 hours may be applied automatically. Past that the policy queues everything for approval,
regardless of auto_apply.
There is also a workspace-wide kill switch in Settings → Workspace. With it off, nothing auto-applies from any surface — API, MCP or dashboard.
Staying under
- Reach for the widest endpoint. One
/campaigns?days=30beats a loop of per-campaign calls. - Cache. The warehouse refreshes on a sync cadence, so polling a read every minute returns the same
numbers — check
data_freshness.data_as_ofinstead. - Back off on 429 rather than retrying immediately;
retry_after_mssays exactly how long.