Rate Limits & Retention
Two independent limits apply to your account, each with its own headers and its own 429 behavior:
- API request rate limits — how many API calls (of any kind) you can make per minute/hour, keyed to your credential.
- Send volume quotas — how many emails
POST /v1/sendwill accept, keyed to your Motor Block and your account plan.
Do not confuse the two: a 429 from one has no bearing on the other, and they use different response headers (X-RateLimit-* vs RateLimit-*, below).
API Request Rate Limits
Every authenticated request — Public Analytics API and HTTP Send API alike — is capped per credential (per Motor Block API key, per Account API key, or per Public API bearer token). Responses include:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 98
X-RateLimit-Reset: 23
| Header | Description |
|---|---|
X-RateLimit-Limit | Maximum requests allowed per window |
X-RateLimit-Remaining | Requests remaining in current window |
X-RateLimit-Reset | Seconds until the rate limit window resets |
Some unauthenticated utility endpoints and early validation failures may not include these headers. Treat them as rate-limit metadata for authenticated API calls, not as a replacement for handling 429 Too Many Requests.
On the rare path where the request limiter itself is unavailable, X-RateLimit-Remaining is the literal string unknown rather than a number — parse defensively.
Best practices:
- Check
X-RateLimit-Remainingbefore making requests - Implement exponential backoff on 429 responses
- Add delays between batch requests:
sleep 1in bash,time.sleep(1)in Python - Cache responses where possible to reduce API calls
Send Volume Quotas
Separately from the API request limiter above, POST /v1/send (and, on the SMTP side, mail submitted directly to mail.motorical.com) is capped against two send-volume ceilings that share the same burst/hourly/daily counters shown on your dashboard's Usage page:
| Ceiling | Scope | Where to check it |
|---|---|---|
| Motor Block limit | Burst, hourly, and daily caps on one Motor Block — either your configured limits, or a lower reputation-based throttle if one is active, whichever is lower | GET /motor-blocks/{id}/rate-limits |
| Account-wide limit | Combined hourly and daily caps across every Motor Block on your account, derived from your subscription plan | GET /account/rate-limits |
Both ceilings are evaluated on every POST /v1/send call and increment the same shared counters used by SMTP submission — sending through one path counts against the other. dryRun: true requests are validated but never charged against either ceiling.
:::note Account-wide enforcement is rolling out gradually
The Motor Block limit is enforced today: exceeding it returns 429 with error: "rate_limited_block". The account-wide limit is currently monitored, not yet enforced — GET /account/rate-limits already reports real usage against it so you can watch your standing ahead of time, but POST /v1/send will not refuse a message for exceeding it until account-wide enforcement is fully rolled out. Don't build logic that assumes today's behavior is permanent — build to handle rate_limited_account (below) now, ahead of enforcement.
:::
Response headers on /v1/send
A send that is accepted, and a send refused for a Motor Block or account ceiling, carry:
RateLimit-Limit: 10000
RateLimit-Remaining: 9999
RateLimit-Reset: 33953
These are distinct from X-RateLimit-* above (no X- prefix): they describe a send ceiling, not your API request budget. On an accepted send they report the Motor Block's daily ceiling. On a 429 they report whichever dimension actually bound — burst, hourly, daily, or (once account enforcement is on) the account-wide ceiling — with RateLimit-Remaining: 0 and a Retry-After header in seconds.
They are not present on:
dryRun: trueresponses — the request never reaches the quota check- validation, disallowed-header, auth, domain, DNS, or sandbox failures (
422,400,401,403,404), all refused before the quota check - the
rate_limited_warmuprefusal, which sets onlyRetry-After - any response where the quota check itself was unavailable — Motorical fails open and accepts the message rather than emitting placeholder numbers
Missing headers mean "unknown", never "unlimited". Poll GET /account/rate-limits when you need a reliable figure.
429 error slugs on /v1/send
{
"success": false,
"error": "rate_limited_block",
"message": "Rate limit exceeded (daily)",
"retryAfterSeconds": 33953
}
error slug | Meaning | Enforced today? |
|---|---|---|
rate_limited_block | The Motor Block's burst, hourly, or daily cap was exceeded | Yes |
rate_limited_account | The account-wide hourly or daily cap was exceeded | Not yet — rolling out (see above) |
rate_limited_warmup | The sending domain is still warming up and its send buffer is full | Not yet — rolling out |
All three use the same Retry-After / retryAfterSeconds contract. Treat any of them the same way you'd treat 429 from the API request limiter: back off and retry after the indicated window, don't hammer.
SMTP submission
SMTP submission on mail.motorical.com:2587 / :2465 shares these exact counters — a message sent over SMTP decrements the same burst/hourly/daily and account buckets as one sent over POST /v1/send. Over-quota SMTP submissions are refused with a temporary 451 4.7.1 reply naming the bound ceiling. See SMTP troubleshooting → Rate Limiting for the exact reply strings.
Handling Rate Limits
When you exceed either the API request limit or a send volume quota, you'll receive a 429 Too Many Requests response — check the response body's error field (above) to tell which one it was.
Best practices:
- Check
X-RateLimit-Remaining(API requests) andGET /account/rate-limits(send volume) before making requests - Implement exponential backoff on 429 responses
- Add delays between batch requests:
sleep 1in bash,time.sleep(1)in Python - Cache responses where possible to reduce API calls
Data Retention
Requests for data outside your plan's retention window are rejected with a 400 Bad Request response. Check your plan's retention limits in the dashboard under Settings → Usage.
Time Parameters
All time parameters use ISO 8601 format:
2024-01-15T14:30:00Z
Timestamps must fall within your plan's retention window.