Skip to main content

Rate Limiting

Zavu implements rate limiting to ensure fair usage and protect the platform from abuse. All API requests to /v1/* endpoints are subject to rate limiting.

Default Limits

ParameterValueNotes
Default limit600 requests/minutePer project
ScopeAll /v1/* endpointsGlobal per project
AlgorithmFixed windowResets every minute
Need higher limits? Contact our sales team to discuss custom rate limits for your project.

Rate Limit Headers

Every API response includes headers to help you track your rate limit usage:
HeaderDescriptionExample
X-RateLimit-LimitMaximum requests per minute600
X-RateLimit-RemainingRequests remaining in current window542
X-RateLimit-ResetUnix timestamp (ms) when window resets1701234600000
Retry-AfterSeconds until reset (only on 429)45

Checking Rate Limits with the SDK

Use .withResponse() to access the rate limit headers:
import Zavudev from '@zavudev/sdk';

const zavu = new Zavudev({
  apiKey: process.env['ZAVUDEV_API_KEY'], // This is the default and can be omitted
});

// Use .withResponse() to get the full HTTP response
const { response, result } = await zavu.messages.send.withResponse({
  to: "+14155551234",
  text: "Hello from Zavu!",
});

// Access rate limit headers
const limit = response.headers.get("X-RateLimit-Limit");
const remaining = response.headers.get("X-RateLimit-Remaining");
const reset = response.headers.get("X-RateLimit-Reset");

console.log(`Rate limit: ${remaining}/${limit} requests remaining`);
console.log(`Window resets at: ${new Date(Number(reset)).toISOString()}`);

// Access the message result as usual
console.log("Message ID:", result.message.id);

Rate Limit Exceeded (429)

When you exceed the rate limit, the API returns a 429 Too Many Requests response:
{
  "code": "rate_limited",
  "message": "Rate limit exceeded. Retry after 2024-01-15T12:30:00.000Z",
  "details": {
    "retryAfter": 1705322400000
  }
}

Handling Rate Limits

Here’s how to properly handle rate limiting in your application:
import { Zavu } from "@zavudev/sdk";
import * as errors from "@zavudev/sdk/models/errors";

const zavu = new Zavu({
  bearerAuth: process.env.ZAVU_API_KEY,
});

async function sendWithRateLimitHandling(to: string, text: string) {
  try {
    const { response, result } = await zavu.messages.send.withResponse({
      to, text,
    });

    // Check remaining quota
    const remaining = Number(response.headers.get("X-RateLimit-Remaining"));
    if (remaining < 10) {
      console.warn(`Low rate limit: ${remaining} requests remaining`);
    }

    return result;
  } catch (error) {
    if (error instanceof errors.SDKError && error.statusCode === 429) {
      // Get retry time from error or headers
      const retryAfter = error.body?.details?.retryAfter;
      const waitMs = retryAfter ? retryAfter - Date.now() : 60000;

      console.log(`Rate limited. Waiting ${waitMs}ms before retry...`);
      await new Promise(resolve => setTimeout(resolve, waitMs));

      // Retry the request
      return sendWithRateLimitHandling(to, text);
    }
    throw error;
  }
}

How It Works

Zavu uses a fixed window algorithm for rate limiting:
Window: 12:00:00 - 12:00:59
├── Request 1: OK (remaining: 599)
├── Request 2: OK (remaining: 598)
├── ...
├── Request 600: OK (remaining: 0)
└── Request 601: 429 Rate Limited

Window: 12:01:00 - 12:01:59
└── Request 1: OK (remaining: 599) ← Counter resets!
The counter resets at the start of each minute (e.g., 12:00:00, 12:01:00, 12:02:00).

Best Practices

Monitor Headers

Always check X-RateLimit-Remaining to avoid hitting limits unexpectedly.

Implement Backoff

Use the Retry-After header or retryAfter field to wait the correct amount of time.

Queue Messages

For bulk sending, implement a queue that respects the rate limit.

Use Idempotency Keys

Include idempotencyKey in requests to safely retry without duplicates.

Bulk Sending Example

For sending many messages, implement a simple rate limiter:
import Zavudev from '@zavudev/sdk';

const zavu = new Zavudev({
  apiKey: process.env['ZAVUDEV_API_KEY'], // This is the default and can be omitted
});

async function sendBulkMessages(messages: { to: string; text: string }[]) {
  const results = [];
  let remaining = 600;

  for (const msg of messages) {
    // If running low, wait for next window
    if (remaining < 10) {
      console.log("Approaching rate limit, waiting for next window...");
      await new Promise(resolve => setTimeout(resolve, 60000));
      remaining = 600;
    }

    const { response, result } = await zavu.messages.send.withResponse(msg);

    remaining = Number(response.headers.get("X-RateLimit-Remaining"));
    results.push(result);
  }

  return results;
}

Next Steps