Rate Limits
Limits
Section titled “Limits”| Limit | Default value |
|---|---|
| Sustained | 1,000 requests per minute per credential |
| Burst | 50 requests per second per credential |
- API keys are limited per key. SuiteOp can configure an individual key with a higher per-minute limit; its burst allowance then grows to match (the larger of 50 or the minute limit ÷ 60, rounded up). The
X-RateLimit-Limitheader always shows the minute limit that applies to your key. - OAuth tokens are limited per app and user pair, at the default limits. Acting in a second organization doesn’t give the pair a second budget.
The sustained limit uses a fixed window aligned to the clock minute, not a rolling window. The counter resets at the start of every minute (X-RateLimit-Reset).
Every authenticated request is counted, including requests that end in an error and requests to paths that don’t exist. The REST API and the MCP server draw from the same budget for the same credential.
Rate Limit Headers
Section titled “Rate Limit Headers”Every authenticated response includes rate limit headers, whether it succeeds or fails:
| Header | Description |
|---|---|
X-RateLimit-Limit | Requests allowed per minute for this credential |
X-RateLimit-Remaining | Requests remaining in the current minute |
X-RateLimit-Reset | Unix timestamp (seconds) when the current minute window ends |
These headers are not present on 401 responses (unauthenticated requests).
When You Hit the Limit
Section titled “When You Hit the Limit”A rate-limited response has status 429 and includes a Retry-After header in whole seconds:
HTTP/1.1 429 Too Many RequestsRetry-After: 14X-RateLimit-Limit: 1000X-RateLimit-Remaining: 0X-RateLimit-Reset: 1720000020{ "error": { "type": "rate_limit_error", "code": "RATE_LIMITED", "message": "API rate limit exceeded", "details": { "vendor": "public-api", "retryAfterSeconds": 14, "rateLimit": { "limit": 1000, "remaining": 0, "resetAtEpochSeconds": 1720000020 } } }, "meta": { "requestId": "3f1c9a52-…" }}The headers tell you which limit you hit:
| You exceeded | Retry-After | X-RateLimit-Remaining |
|---|---|---|
| The minute limit | Seconds until the next minute starts | 0 |
| The burst limit | 1 | What is left of the minute limit, which can still be in the hundreds |
A burst denial can therefore show Retry-After: 1 next to a large X-RateLimit-Remaining. That isn’t a contradiction: you still have minute budget, but you sent too many requests within one second.
If the rate limiter itself is unavailable, the API refuses requests with 429 rather than letting them through unmetered. Treat it like any other 429.
AI-backed operations
Section titled “AI-backed operations”A few operations call an AI model to produce their result: create_email_template, modify_email_template, initializeLanguageTranslations, retranslateGuideText, create_workflow and modify_workflow. On top of the per-credential limit, these share a separate per-organization budget (by default 10 calls per 60 seconds), no matter how many keys the organization uses. Going over it returns 429 rate_limit_error with the message Model-backed action rate limit exceeded and a Retry-After header. If that limiter is unavailable, the 429 comes without Retry-After; back off (for example 60 seconds) before retrying. This 429 usually leaves X-RateLimit-Remaining above 0, because that header reports your per-credential minute budget, so pace retries by Retry-After, not by Remaining.
Backoff Guidance
Section titled “Backoff Guidance”For automated clients:
- Check
X-RateLimit-Remainingon each response. If it is low, slow down before you hit0. - On a
429, waitRetry-Afterseconds before sending the next request. - Spread requests evenly across the minute rather than sending them all at once, so you don’t trip the per-second burst limit.
# Example: request, and on a 429 sleep for the Retry-After valuewhile true; do status=$(curl -s -o body.json -D headers.txt -w "%{http_code}" \ -H "Authorization: Bearer sk_live_your_key_here" \ https://api-us.suiteop.com/api/v1/tasks)
if [ "$status" = "429" ]; then retry_after=$(grep -i '^retry-after:' headers.txt | awk '{print $2}' | tr -d '\r') sleep "${retry_after:-1}" continue fi
# process body.json here sleep 1done