Skip to main content

OTP API Rate Limits

The OTP API implements intelligent rate limiting to balance security, user experience, and abuse prevention.

Rate Limit Overview

EndpointRate LimitWindowPurpose
POST /api/v1/otp/send100 requests1 minuteAllow high-concurrency legitimate use
POST /api/v1/otp/verify300 requests1 minuteSupport multiple concurrent verification attempts
POST /api/v1/otp/resend20 requests1 minuteAllow reasonable resend attempts

Detailed Breakdown

Send OTP - 100 requests/minute

Rationale:

  • Supports high-traffic applications with concurrent users
  • Allows bulk OTP sending for batch operations
  • Prevents spam and abuse while maintaining usability

Use Cases:

  • User onboarding flows with multiple steps
  • E-commerce checkout with phone/email verification
  • Banking apps with transaction confirmations
  • Bulk user verification campaigns

Example Calculation:

100 requests/minute = 1.67 requests/second
- Small app: ~100 users verifying per minute → ✅ Supported
- Medium app: ~5,000 users/hour → ✅ Supported
- Large app: ~100,000 users/hour → ✅ Supported (distributed over time)

Abuse Prevention:

  • Individual OTP sessions still have attempt limits (default: 3-5)
  • Expired OTPs automatically invalidate
  • Wallet balance prevents unlimited sending

Verify OTP - 300 requests/minute

Rationale:

  • Verification attempts are more frequent than sends
  • Users may mistype OTPs multiple times
  • Progressive verification allows multiple channels

Use Cases:

  • Users entering OTP codes from email/SMS
  • Progressive verification (email first, then SMS)
  • Retry after incorrect OTP entry
  • Multiple devices verifying simultaneously

Example Scenario:

User Journey:
1. Receives OTP via email + SMS
2. Attempts email OTP → Wrong code (1st attempt)
3. Attempts email OTP → Wrong code (2nd attempt)
4. Checks SMS, enters correct code → ✅ Success
5. Later verifies SMS channel → ✅ Success

Total API calls: 4 verify requests in ~2 minutes → ✅ Well within limits

Security Layers:

  • Per-session attempt limits (3-5 attempts per session)
  • OTP expiry (default: 10 minutes)
  • Account lockout after repeated failures
  • Rate limit still prevents brute-force at API level

Resend OTP - 20 requests/minute

Rationale:

  • Users occasionally need to resend OTPs
  • Delivery failures require resends
  • Progressive escalation (email → SMS → WhatsApp)

Use Cases:

  • Email delivery delayed
  • User didn't receive first OTP
  • OTP expired before user entered it
  • Channel switching (email → SMS)

Example Scenario:

Legitimate User:
1. Request OTP via email → Not received (1 minute wait)
2. Resend OTP → Still not received
3. Try SMS instead → Resend to SMS channel
4. Success! → Verify

Total resends: 3 in ~5 minutes → ✅ Within limits

Abusive Pattern (Blocked):
- 25 resends in 1 minute → ❌ Rate limited after 20

Rate Limit Headers

All OTP endpoints return rate limit information in response headers:

HTTP/1.1 200 OK
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 87
X-RateLimit-Reset: 1679520000
HeaderDescription
X-RateLimit-LimitMaximum requests allowed in window
X-RateLimit-RemainingRequests remaining in current window
X-RateLimit-ResetUnix timestamp when window resets

Rate Limit Exceeded Response

When rate limit is exceeded:

HTTP/1.1 429 Too Many Requests
Content-Type: application/json

{
"statusCode": 429,
"message": "ThrottlerException: Too Many Requests",
"error": "Too Many Requests"
}

Recommended Client Behavior:

  1. Check X-RateLimit-Reset header
  2. Calculate wait time: resetTime - currentTime
  3. Display user-friendly message
  4. Implement exponential backoff
  5. Retry after wait period

Multi-Layer Protection

Rate limits work alongside other security measures:

1. Per-Session Limits

// OTP session configuration
{
max_attempts: 3, // Max verification attempts per session
expiry_minutes: 10, // Session expires after 10 minutes
lockout_on_failure: true // Lock after max attempts
}

2. Per-Recipient Throttling

  • Same email/phone number: 5 OTPs per 5 minutes
  • Prevents harassment and spam
  • Independent of team rate limits

3. Wallet Balance

  • Each OTP costs credits
  • Prevents unlimited spam
  • Natural abuse prevention

4. IP-Based Tracking

  • Suspicious patterns flagged
  • Multiple failed attempts logged
  • Can trigger additional verification

Best Practices

For Developers

1. Implement Retry Logic

async function sendOTPWithRetry(payload, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await api.post('/v1/otp/send', payload);
} catch (error) {
if (error.response?.status === 429) {
const resetTime = error.response.headers['x-ratelimit-reset'];
const waitTime = (resetTime * 1000) - Date.now();

if (i < maxRetries - 1 && waitTime > 0) {
await sleep(waitTime);
continue;
}
}
throw error;
}
}
}

2. Display Remaining Limits

const response = await api.post('/v1/otp/send', payload);
const remaining = response.headers['x-ratelimit-remaining'];

if (remaining < 10) {
console.warn(`Low rate limit: ${remaining} requests remaining`);
}

3. Handle Rate Limit Errors Gracefully

try {
await api.post('/v1/otp/send', payload);
} catch (error) {
if (error.response?.status === 429) {
showUserMessage('Too many requests. Please try again in a moment.');
// Exponential backoff
}
}

For End Users

Clear Communication:

  • "We've sent a verification code to your email"
  • "Didn't receive it? Check spam folder first"
  • "Click 'Resend' to get a new code"
  • "You can resend up to 20 times per minute"

Progressive Disclosure:

Attempt 1: "Resend code"
Attempt 3: "Resend code (check spam folder)"
Attempt 5: "Resend code (try SMS instead)"

Monitoring and Alerts

Track Rate Limit Usage

Key Metrics:

  • Rate limit hit rate (% of requests throttled)
  • Average remaining quota per team
  • Peak usage times
  • Abusive patterns

Alert Thresholds:

alerts:
rate_limit_hit_rate_high:
condition: hit_rate > 5%
action: Review rate limits, may be too restrictive

rate_limit_hit_rate_low:
condition: hit_rate < 0.1%
action: Limits may be too permissive

suspicious_pattern:
condition: same_ip_multiple_teams > 10
action: Flag for abuse investigation

Comparison with Industry Standards

ServiceSend LimitVerify LimitNotes
Sendmator100/min300/minBalanced for high concurrency
Twilio Verify10/sec (600/min)No public limitEnterprise-focused
Firebase Auth100/min (default)Varies by planSimilar to Sendmator
Auth050/sec (3000/min)No public limitVery permissive
AWS SNS20/sec (1200/min)N/ASMS-only service

Sendmator's Position:

  • ✅ More generous than Firebase (same send limit, higher verify)
  • ✅ Suitable for small-to-medium applications
  • ✅ Can scale to large applications with proper distribution
  • ✅ Better than most competitors for verification attempts

Upgrading Limits

Need higher limits? Contact support with:

  • Current usage patterns
  • Expected growth
  • Use case description
  • Peak traffic times

Enterprise customers can get:

  • Custom rate limits per team
  • Dedicated infrastructure
  • Priority support
  • SLA guarantees

Testing Rate Limits

Use our load testing scripts:

# Test with 100 concurrent requests
npx ts-node scripts/test-otp-load.ts \
--total-requests 1000 \
--concurrency 100

# Should complete without rate limit errors

See Load Testing Documentation for details.

Frequently Asked Questions

Q: Why are limits per team, not per API key? A: Teams represent billing units. This prevents one API key from consuming all resources.

Q: Can I batch OTP sends? A: Yes! 100 requests/minute allows batching. Consider queuing large batches to stay within limits.

Q: What happens if I hit the limit? A: You'll receive a 429 response. Wait for the window to reset (check X-RateLimit-Reset header).

Q: Are sandbox requests rate limited? A: Yes, same limits apply to prevent abuse of sandbox mode.

Q: Can I request a temporary limit increase? A: Contact support for special events (product launches, marketing campaigns).

Changelog

2026-03-18

  • Increased send limit: 5/5min → 100/min (20x improvement)
  • Increased verify limit: 10/min → 300/min (30x improvement)
  • Increased resend limit: 3/5min → 20/min (33x improvement)
  • Rationale: Support high-concurrency use cases and improve UX

Previous (Before 2026-03-18)

  • Send: 5 requests per 5 minutes
  • Verify: 10 requests per minute
  • Resend: 3 requests per 5 minutes
  • Issue: Too restrictive for legitimate high-traffic use