Abrova
☰ Docs menu · Rate limits
Coming soon. Abrova Push is coming soon. This page describes how it will work; it can’t be used yet.

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.

Last updated: 27 Oct 2026

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 TypeValueNotes
Per second10 requestsOverridden per endpoint in the tables below
Per minute100 requestsApplies to every endpoint (broadcast and A/B test send: 10/min)
Per hour1,000 requestsApplies to every endpoint
Action buttons3 per notificationactions array
Message size4KBAPNs / 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)

EndpointLimitNotes
POST /push/send30/secSend to one device
POST /push/send-to-user20/secSend to all of a user's devices
POST /push/send-to-devices10/secSend to multiple devices
POST /push/send-to-topic5/secSend to a topic
POST /push/send-to-segment5/secSend to a segment
POST /push/broadcast1/sec, 10/minSend to every device

Device Management

EndpointLimitNotes
POST /devices/register10/secDevice registration
POST /devices/:id/topics/:topic20/secTopic subscription
POST /devices/:id/user10/secUser ID updates

Delivery Receipts

EndpointLimitNotes
POST /receipts/*100/secAll receipt tracking (delivered, opened, clicked, dismissed)

Inbox

EndpointLimitNotes
POST /inbox/messages30/secFetch inbox messages
PUT /inbox/messages/:id50/secUpdate message status
POST /inbox/messages/mark-multiple20/secBulk mark operations
POST /inbox/messages/mark-all-read10/secMark all as read
POST /inbox/sdk/send-to-device20/secSend to device inbox (client SDK)
POST /inbox/api/send-to-user50/secServer inbox send to a user
POST /inbox/api/send-to-device50/secServer inbox send to a device
POST /inbox/api/send-to-users20/secServer inbox send to many users
POST /inbox/api/broadcast5/secServer inbox broadcast

In-App Messages

EndpointLimitNotes
POST /in-app/fetch30/secFetch in-app messages
POST /in-app/impression100/secTrack impressions

A/B Testing

EndpointLimitNotes
POST /ab-tests/sdk/assignment50/secGet variant assignment
POST /ab-tests/sdk/track/*100/secTrack 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:

HeaderDescription
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:

json
{
  "statusCode": 429,
  "message": "Too Many Requests"
}

Best Practices#

Implement Exponential Backoff

When rate limited, wait and retry with increasing delays (1s, 2s, 4s, etc.)

Use Bulk Endpoints

Send to multiple devices in a single request instead of individual calls

Monitor Usage Headers

Track X-RateLimit-Remaining-* to proactively slow down before hitting limits

Queue Non-Urgent Messages

Use scheduled messages for non-time-sensitive notifications to spread load

Handling Rate Limits in Code#

typescript
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;
    }
  }
}
Was this page helpful?
Report a problem