Rate Limits
EmacronAI CRM enforces rate limits to ensure fair usage and platform stability. When you exceed a limit, the API returns HTTP 429 Too Many Requests with a Retry-After header.
Limits reference
| Endpoint | Limit | Scope | Notes |
|---|---|---|---|
| All API endpoints | 1,000 requests / hour | Per API key | Shared across all endpoints |
| POST /whatsapp/otp/send | 5 OTPs / 10 min | Per phone per workspace | Prevents spam to same number |
| POST /whatsapp/otp/send | 1,000 OTPs / hour | Per workspace | Upgrade plan to increase |
| POST /whatsapp/otp/verify | 5 attempts | Per OTP request | Request invalidated on 5th failure |
| POST /contacts | 500 / min | Per workspace | For bulk import, use batching |
| POST /webhooks | 10 webhooks | Per workspace | Delete old ones to add more |
Rate limit response headers
Every API response includes these headers so you can track your usage:
X-RateLimit-Limit: 1000 # requests allowed in window
X-RateLimit-Remaining: 847 # requests remaining this window
X-RateLimit-Reset: 1725350400 # Unix timestamp when window resets
# When rate limited (HTTP 429):
Retry-After: 60 # seconds to wait before retryingHandling rate limits — exponential backoff
When you hit a 429, always wait for Retry-After seconds before retrying. For repeated failures, use exponential backoff with jitter:
import { createClient, EmacronAI CRMError } from '@emacrontechnologies/outreachagent';
const oa = createClient(process.env.OUTREACHAGENT_API_KEY!);
async function sendOtpWithRetry(phone: string, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await oa.otp.send({ phone });
} catch (err) {
if (err instanceof EmacronAI CRMError && err.status === 429) {
const retryAfter = (err.raw as { retry_after?: number })?.retry_after ?? 60;
// Add jitter: wait retry_after ± 10%
const wait = retryAfter * 1000 * (1 + (Math.random() - 0.5) * 0.2);
console.log(`Rate limited. Retrying in ${(wait / 1000).toFixed(1)}s...`);
await new Promise(r => setTimeout(r, wait));
} else {
throw err; // non-rate-limit errors should not be retried silently
}
}
}
throw new Error(`Failed after ${maxRetries} retries`);
}OTP-specific limits explained
5 OTPs per phone per 10 minutes
Prevents a single phone from being flooded. If a user requests OTP more than 5 times in 10 minutes, subsequent requests return 429. Show the user a countdown timer in your UI.
1,000 OTPs per workspace per hour
Workspace-level cap. For high-volume use cases (10,000+ OTPs/hour), contact sales to upgrade your plan. The limit resets every rolling hour window.
5 verify attempts per OTP
After 5 failed verify attempts, the OTP request is permanently invalidated. Force a new sendOtp() call. This prevents brute-force attacks on 6-digit codes.
OTP TTL: 10 minutes
OTPs expire 10 minutes after they are sent. After expiry, verify() returns OTP_EXPIRED. You can customise this down to 5 minutes by setting expiry in the send request.