Rate Limits
SendSeven limits request rates to keep the platform fast and fair. A request over a limit gets 429 Too Many Requests. Handle every 429 the same way: wait, then retry.
Limits
Per-endpoint limits
These endpoints have their own limits, counted per client IP address, whatever authentication you use:
| Endpoint | Limit |
|---|---|
POST /api/v1/knowledge-base/documents/crawl | 10 per minute |
POST /api/v1/knowledge-base/documents/recrawl | 10 per minute |
POST /api/v1/knowledge-base/documents/crawl/preview | 20 per minute |
POST /api/v1/knowledge-base/search | 60 per minute |
POST /api/v1/email-providers/{provider_id}/test | 10 per minute and 50 per hour (per workspace) |
General limits
Every other authenticated request counts against one general limit. Which one depends on how you authenticate:
| Credential | Limit | Counted per |
|---|---|---|
| API keys and OAuth tokens (your own integrations, OAuth apps, the MCP server, Zapier, Make, n8n, the HubSpot embed) | 3,000 requests per minute | Workspace |
| People signed in to the web or mobile app (Bearer JWT sessions, see Authentication) | 300 requests per minute sustained, bursts of up to 150 | Person |
- API keys and OAuth tokens share one limit per workspace: all keys and apps of a workspace count together. The workspace is the one the token belongs to, whether or not you send
X-Tenant-ID. A parent token withtenants:managethat addresses a sub-account withX-Tenant-ID(see Multi-Tenancy) counts against that sub-account. - People each have their own limit, whichever workspace they work in, so the size of your team never matters. A person's activity in the app never uses up your integrations' limit, and integration traffic never slows people down.
- The limit for people is a token bucket: it holds up to 150 requests, and 5 come back every second (300 per minute). Loading or reloading the app several times in a row fits in the bucket; only a sustained rate above 300 per minute is rejected.
An X-Tenant-ID value the credential may not act for never moves the request to another workspace's limit.
Workspace limits for sending endpoints
These endpoints count per workspace for every credential, instead of the general limit:
| Requests | Limit |
|---|---|
/api/v1/email-campaigns/..., /api/v1/invitations/... and POST /api/v1/email-integrations/email-messages/<id>/forward | 30 per minute per workspace (shared) |
/api/v1/double-opt-in/send and /resend | 10 per minute per workspace |
Other limits
- Some features have their own limits and answer
429with an endpoint-specific body, for example WhatsApp template edits (template_edit_limit_reached, see Templates) or AI features. - Delivery speed is also limited by each channel provider (for example WhatsApp throughput per number). That shows up in message status and campaign pacing, not as a
429on your request.
Limits can change. Don't rely on the absence of rate-limit headers: build your client to handle a 429 on any endpoint.
Rate limit headers
Responses counted against a general or workspace limit carry these headers. They describe the limit the request was counted against, so an API key sees its workspace's 3,000 and a signed-in person the size of their own bucket (150). Requests rejected before the credential is verified, for example with 401, carry none.
X-RateLimit-Limit: 3000
X-RateLimit-Remaining: 2987
X-RateLimit-Reset: 1706889600
| Header | Description |
|---|---|
X-RateLimit-Limit | Maximum requests per window (for a signed-in person: the bucket size) |
X-RateLimit-Remaining | Requests remaining in the current window (for a signed-in person: requests left in the bucket) |
X-RateLimit-Reset | Unix timestamp when the window resets (for a signed-in person: when the bucket is full again, or on a 429 when the next request is allowed) |
When you hit the limit
A general or workspace limit answers with headers and a JSON body:
HTTP/1.1 429 Too Many Requests
Retry-After: 12
X-RateLimit-Limit: 3000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1706889600
{
"detail": "Rate limit exceeded. Please slow down.",
"error": "rate_limit_exceeded",
"category": "api_token",
"retry_after": 12
}
category names the limit you hit: api_token (API keys and OAuth tokens, per workspace), ui_user (a signed-in person), email_send or double_opt_in. Treat it as informational and handle every 429 the same way.
A per-endpoint limit answers with a shorter body and no Retry-After header:
HTTP/1.1 429 Too Many Requests
{
"error": "Rate limit exceeded: 10 per 1 minute"
}
Use Retry-After when it is present, otherwise back off exponentially.
Handling rate limits
Exponential backoff (recommended)
import time
import requests
def request_with_backoff(url, headers, max_retries=5):
for attempt in range(max_retries):
response = requests.get(url, headers=headers)
if response.status_code != 429:
return response
retry_after = int(response.headers.get("Retry-After", 2 ** attempt))
time.sleep(retry_after)
raise Exception("Max retries exceeded")
Tips to avoid rate limits
- Batch requests where possible instead of making individual calls
- Cache responses for data that doesn't change frequently (contacts, tags)
- Use webhooks instead of polling for real-time updates
- Respect
Retry-Afterheaders and don't retry immediately