Rate Limits
API rate limits for SDK endpoints, dashboard endpoints, push sends, devices, receipts, inbox, in-app, and A/B testing. Rate limit headers, 429 response, exponential backoff.
Understand API rate limits to ensure fair usage and service stability.
General Limits#
Limits are counted per endpoint and per client IP. Unless an endpoint below sets its own per-second limit, these defaults apply to every API request:
| Limit Type | Value | Notes |
|---|---|---|
| Per second | 10 requests | Overridden per endpoint in the tables below |
| Per minute | 100 requests | Applies to every endpoint (broadcast and A/B test send: 10/min) |
| Per hour | 1,000 requests | Applies to every endpoint |
| Action buttons | 3 per notification | actions array |
| Message size | 4KB | APNs / FCM payload limit, including custom data |
SDK Endpoint Rate Limits#
Rate limits for SDK endpoints (using API key authentication).
Push Notifications (server: API key + server secret)
| Endpoint | Limit | Notes |
|---|---|---|
| POST /push/send | 30/sec | Send to one device |
| POST /push/send-to-user | 20/sec | Send to all of a user's devices |
| POST /push/send-to-devices | 10/sec | Send to multiple devices |
| POST /push/send-to-topic | 5/sec | Send to a topic |
| POST /push/send-to-segment | 5/sec | Send to a segment |
| POST /push/broadcast | 1/sec, 10/min | Send to every device |
Device Management
| Endpoint | Limit | Notes |
|---|---|---|
| POST /devices/register | 10/sec | Device registration |
| POST /devices/:id/topics/:topic | 20/sec | Topic subscription |
| POST /devices/:id/user | 10/sec | User ID updates |
Delivery Receipts
| Endpoint | Limit | Notes |
|---|---|---|
| POST /receipts/* | 100/sec | All receipt tracking (delivered, opened, clicked, dismissed) |
Inbox
| Endpoint | Limit | Notes |
|---|---|---|
| POST /inbox/messages | 30/sec | Fetch inbox messages |
| PUT /inbox/messages/:id | 50/sec | Update message status |
| POST /inbox/messages/mark-multiple | 20/sec | Bulk mark operations |
| POST /inbox/messages/mark-all-read | 10/sec | Mark all as read |
| POST /inbox/sdk/send-to-device | 20/sec | Send to device inbox (client SDK) |
| POST /inbox/api/send-to-user | 50/sec | Server inbox send to a user |
| POST /inbox/api/send-to-device | 50/sec | Server inbox send to a device |
| POST /inbox/api/send-to-users | 20/sec | Server inbox send to many users |
| POST /inbox/api/broadcast | 5/sec | Server inbox broadcast |
In-App Messages
| Endpoint | Limit | Notes |
|---|---|---|
| POST /in-app/fetch | 30/sec | Fetch in-app messages |
| POST /in-app/impression | 100/sec | Track impressions |
A/B Testing
| Endpoint | Limit | Notes |
|---|---|---|
| POST /ab-tests/sdk/assignment | 50/sec | Get variant assignment |
| POST /ab-tests/sdk/track/* | 100/sec | Track impressions, opens, clicks, conversions |
Rate Limit Headers#
Every API response includes headers for each window, suffixed with the window name (-short = per second, -medium = per minute, -long = per hour), e.g. X-RateLimit-Remaining-short:
| Header | Description |
|---|---|
| X-RateLimit-Limit-{window} | Maximum requests allowed per window |
| X-RateLimit-Remaining-{window} | Requests remaining in current window |
| X-RateLimit-Reset-{window} | Seconds until the window resets |
| Retry-After-{window} | Sent with a 429: seconds to wait before retrying |
Rate Limit Exceeded Response#
When you exceed the rate limit, you will receive a 429 Too Many Requests response:
{
"statusCode": 429,
"message": "Too Many Requests"
}Best Practices#
When rate limited, wait and retry with increasing delays (1s, 2s, 4s, etc.)
Send to multiple devices in a single request instead of individual calls
Track X-RateLimit-Remaining-* to proactively slow down before hitting limits
Use scheduled messages for non-time-sensitive notifications to spread load
Handling Rate Limits in Code#
async function sendWithRetry(payload: any, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const response = await fetch('https://push-api.abrova.ir/push/send', {
method: 'POST',
headers: {
'x-api-key': 'ab_live_your_api_key',
'x-server-secret': 'ab_srv_your_server_secret',
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
});
if (response.status === 429) {
const waitTime = Math.pow(2, attempt) * 1000; // Exponential backoff
await new Promise(resolve => setTimeout(resolve, waitTime));
continue;
}
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return await response.json();
} catch (error) {
if (attempt === maxRetries - 1) throw error;
}
}
}