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

EndpointLimitScopeNotes
All API endpoints1,000 requests / hourPer API keyShared across all endpoints
POST /whatsapp/otp/send5 OTPs / 10 minPer phone per workspacePrevents spam to same number
POST /whatsapp/otp/send1,000 OTPs / hourPer workspaceUpgrade plan to increase
POST /whatsapp/otp/verify5 attemptsPer OTP requestRequest invalidated on 5th failure
POST /contacts500 / minPer workspaceFor bulk import, use batching
POST /webhooks10 webhooksPer workspaceDelete 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 retrying

Handling 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.

Related