Rate Limiting
The Ping API uses rate limiting to ensure fair usage and protect infrastructure from abuse. Understanding rate limits helps you build robust integrations that handle temporary restrictions gracefully.
Rate limit tiers
Rate limits vary by endpoint type and authentication method. All limits are per account (for API keys) or per user (for JWT tokens).
Authentication endpoints
- Name
POST /v1/auth/login- Description
5 requests per minute - Prevents brute force attacks
- Name
POST /v1/auth/refresh- Description
5 requests per minute - Prevents token abuse
- Name
POST /v1/auth/accept-invite- Description
10 requests per hour - Prevents invite abuse
Notification endpoints
- Name
POST /v1/notification/api/sms/send- Description
No hard limit - Throttled by provider capacity (~80-100/sec for bulk)
- Name
POST /v1/notification/api/whatsapp/send- Description
No hard limit - Subject to WhatsApp Business API limits
- Name
POST /v1/notification/api/email/send- Description
No hard limit - Throttled by email provider
Other endpoints
Most read endpoints (GET requests) have generous limits or no hard limits. Write endpoints (POST, PUT, DELETE) may have endpoint-specific limits to prevent abuse.
Rate limit scope:
- API key auth: Limits apply per API key (shared across all requests using that key)
- JWT auth: Limits apply per user account
- IP-based: Some endpoints have additional IP-based limits for unauthenticated requests
Handling rate limits
When you exceed a rate limit, the API returns a 429 Too Many Requests response.
429 Response format
{
"result": "failed",
"message": "Rate limit exceeded. Please retry after 60 seconds.",
"code": "RATE_LIMIT_EXCEEDED"
}
Response headers
Rate-limited responses include helpful headers:
Retry-After- Seconds to wait before retrying (e.g.,60)X-RateLimit-Limit- Maximum requests allowed in the windowX-RateLimit-Remaining- Requests remaining in current windowX-RateLimit-Reset- Unix timestamp when the limit resets
Exponential backoff
Implement exponential backoff to handle rate limits gracefully:
- First retry: Wait time from
Retry-Afterheader (or 1 second) - Second retry: Wait 2× the previous delay
- Third retry: Wait 4× the original delay
- Max retries: Stop after 3-5 attempts
Example: Python with backoff
Python
import requests
import time
def send_with_backoff(url, headers, data, max_retries=5):
"""Send request with exponential backoff on 429."""
delay = 1
for attempt in range(max_retries):
response = requests.post(url, headers=headers, json=data)
if response.status_code == 200:
return response.json()
if response.status_code == 429:
# Get retry delay from header or use exponential backoff
retry_after = int(response.headers.get('Retry-After', delay))
print(f"Rate limited. Waiting {retry_after}s...")
time.sleep(retry_after)
delay *= 2 # Exponential backoff
continue
# Other error
response.raise_for_status()
raise Exception(f"Failed after {max_retries} retries")
# Usage
result = send_with_backoff(
'https://api.ping.co.zw/v1/notification/api/sms/send',
headers={'X-Ping-Api-Key': api_key},
data={'to_phone': '+263771234567', 'message': 'Hello'}
)
Example: Node.js with backoff
Node.js
async function sendWithBackoff(url, options, maxRetries = 5) {
let delay = 1000 // Start with 1 second
for (let attempt = 0; attempt < maxRetries; attempt++) {
const response = await fetch(url, options)
if (response.ok) {
return await response.json()
}
if (response.status === 429) {
const retryAfter = response.headers.get('Retry-After')
const waitTime = retryAfter ? parseInt(retryAfter) * 1000 : delay
console.log(`Rate limited. Waiting ${waitTime/1000}s...`)
await new Promise(resolve => setTimeout(resolve, waitTime))
delay *= 2 // Exponential backoff
continue
}
throw new Error(`HTTP ${response.status}: ${response.statusText}`)
}
throw new Error(`Failed after ${maxRetries} retries`)
}
// Usage
const result = await sendWithBackoff(
'https://api.ping.co.zw/v1/notification/api/sms/send',
{
method: 'POST',
headers: {
'X-Ping-Api-Key': apiKey,
'Content-Type': 'application/json',
},
body: JSON.stringify({
to_phone: '+263771234567',
message: 'Hello'
}),
}
)
Best practices
1. Respect the Retry-After header
Always check the Retry-After header in 429 responses. This tells you exactly when it's safe to retry.
2. Implement exponential backoff
Don't retry immediately. Use exponential backoff to avoid overwhelming the API during recovery.
3. Cache authentication tokens
Don't call /v1/auth/login on every request. Cache JWT tokens and only refresh when they expire (after 30 days).
4. Batch operations when possible
Instead of making 100 separate SMS requests, use bulk operations:
# Bad - 100 separate requests (slow, may hit rate limits)
for phone in phones:
send_sms(phone, message)
# Good - 1 bulk request
send_sms_bulk(phones, message)
5. Use bulk endpoints for large sends
For sending to 10+ recipients, use bulk endpoints:
- SMS: Send array or recipient group to
/v1/notification/api/sms/send - Email: Send array of addresses to
/v1/notification/api/email/send
6. Monitor rate limit headers
Check X-RateLimit-Remaining on every response. If it's low, slow down your requests proactively.
7. Distribute load over time
For scheduled campaigns, spread sends over hours or days rather than sending everything at once.
8. Handle errors gracefully
Don't fail silently on rate limits. Log errors and implement proper retry logic so messages eventually get sent.
Rate limit monitoring
Monitor rate limits
import requests
response = requests.post(url, headers=headers, json=data)
# Check rate limit status
limit = response.headers.get('X-RateLimit-Limit')
remaining = response.headers.get('X-RateLimit-Remaining')
reset = response.headers.get('X-RateLimit-Reset')
if remaining:
remaining_count = int(remaining)
if remaining_count < 10:
print(f"⚠️ Only {remaining_count} requests remaining!")
# Slow down or pause
Bulk vs individual requests
When to use bulk:
- Sending to 10+ recipients
- Same message to multiple people
- Scheduled campaigns
When to use individual:
- Personalized messages
- Real-time notifications
- Fewer than 10 recipients
Pro tip: For very large sends (10,000+ recipients), contact support to discuss rate limit increases or dedicated sending infrastructure.
Common scenarios
Scenario 1: Authentication rate limit
Problem: Login endpoint returns 429 during development testing.
Solution:
- Cache tokens instead of logging in repeatedly
- Use long-lived API keys for automated testing
- Implement proper token refresh flow
Scenario 2: Bulk campaign rate limited
Problem: Large email campaign fails with 429 midway through.
Solution:
- Use bulk endpoints (
/v1/notification/api/email/sendwith array) - Bulk jobs run in background without hitting rate limits
- Track progress with bulk job status endpoint
Scenario 3: High-frequency notifications
Problem: Real-time notifications (OTPs, alerts) hit rate limits.
Solution:
- Notification endpoints generally don't have hard rate limits
- If hitting provider limits, contact support for increased capacity
- Consider message queuing on your side to smooth traffic
Need higher limits?
Contact Ping support if you need:
- Higher authentication rate limits for legitimate use cases
- Dedicated sending capacity for high-volume campaigns
- Custom rate limits for enterprise integrations
Email: [email protected]
