> ## Documentation Index
> Fetch the complete documentation index at: https://docs.zavu.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Rate Limiting

> Understanding API rate limits and how to handle them

# 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.

## Rate Limit Tiers

Your rate limit depends on your project's verification status:

| Tier                    | Limit              | Eligibility                                |
| ----------------------- | ------------------ | ------------------------------------------ |
| **Default**             | 600 requests/min   | All new projects                           |
| **Compliance Verified** | 1,200 requests/min | Projects with verified business compliance |
| **10DLC Verified**      | 2,400 requests/min | Projects with approved 10DLC campaign      |

<Info>
  Rate limits are applied **per project**. If you have multiple projects, each has its own independent rate limit.
</Info>

### How to Increase Your Limit

<Steps>
  <Step title="Complete Compliance Verification">
    Submit your business information and use case in the dashboard under **Settings > Compliance**. Once verified, your limit automatically increases to **1,200 requests/min** (2x).
  </Step>

  <Step title="Register for 10DLC (US Numbers)">
    If you're sending SMS to US recipients, register your brand and campaign for 10DLC compliance. Approved campaigns receive **2,400 requests/min** (4x).
  </Step>
</Steps>

<Tip>
  Verification is automatic - once approved, your rate limit increases immediately with no code changes needed.
</Tip>

## Rate Limit Headers

Every API response includes headers to help you track your rate limit usage:

| Header                  | Description                            | Example         |
| ----------------------- | -------------------------------------- | --------------- |
| `X-RateLimit-Limit`     | Maximum requests per minute            | `600`           |
| `X-RateLimit-Remaining` | Requests remaining in current window   | `542`           |
| `X-RateLimit-Reset`     | Unix timestamp (ms) when window resets | `1701234600000` |
| `Retry-After`           | Seconds until reset (only on 429)      | `45`            |

## Checking Rate Limits with the SDK

Use `.withResponse()` to access the rate limit headers:

<CodeGroup>
  ```typescript TypeScript theme={null}
  import Zavudev from '@zavudev/sdk';

  const zavu = new Zavudev({
    apiKey: process.env['ZAVUDEV_API_KEY'],
  });

  // 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);
  ```

  ```python Python theme={null}
  import os
  from zavudev import Zavudev

  zavu = Zavudev(
      api_key=os.environ.get("ZAVUDEV_API_KEY"),
  )

  response = zavu.messages.send.with_raw_response(
      to="+14155551234",
      text="Hello from Zavu!"
  )

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

  print(f"Rate limit: {remaining}/{limit} requests remaining")

  # Access the parsed result
  result = response.parse()
  print(f"Message ID: {result.message.id}")
  ```

  ```ruby Ruby theme={null}
  require 'zavudev'

  zavu = Zavudev::Client.new(
    api_key: ENV['ZAVUDEV_API_KEY']
  )

  response = zavu.messages.send_with_response(
    to: "+14155551234",
    text: "Hello from Zavu!"
  )

  # Access rate limit headers
  limit = response.headers["X-RateLimit-Limit"]
  remaining = response.headers["X-RateLimit-Remaining"]
  reset = response.headers["X-RateLimit-Reset"]

  puts "Rate limit: #{remaining}/#{limit} requests remaining"

  # Access the message result
  puts "Message ID: #{response.data.message.id}"
  ```

  ```go Go theme={null}
  package main

  import (
      "context"
      "fmt"
      "os"

      "github.com/zavudev/sdk-go"
  )

  func main() {
      client := zavudev.NewClient(
          zavudev.WithAPIKey(os.Getenv("ZAVUDEV_API_KEY")),
      )

      result, err := client.Messages.Send(context.TODO(), zavudev.MessageSendParams{
          To:   zavudev.String("+14155551234"),
          Text: zavudev.String("Hello from Zavu!"),
      })

      // Access rate limit headers from the response
      // Headers are available via the raw HTTP response
      fmt.Println("Message ID:", result.Message.ID)
  }
  ```

  ```php PHP theme={null}
  $zavu = new Zavudev\Client(['apiKey' => getenv('ZAVUDEV_API_KEY')]);

  $response = $zavu->messages->sendWithResponse([
      'to' => '+14155551234',
      'text' => 'Hello from Zavu!',
  ]);

  // Access rate limit headers
  $limit = $response->headers['X-RateLimit-Limit'];
  $remaining = $response->headers['X-RateLimit-Remaining'];
  $reset = $response->headers['X-RateLimit-Reset'];

  echo "Rate limit: {$remaining}/{$limit} requests remaining\n";
  echo "Message ID: " . $response->data->message->id . "\n";
  ```

  ```bash cURL theme={null}
  curl -i -X POST https://api.zavu.dev/v1/messages \
    -H "Authorization: Bearer $ZAVU_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"to": "+14155551234", "text": "Hello!"}'

  # Response headers will include:
  # X-RateLimit-Limit: 600
  # X-RateLimit-Remaining: 599
  # X-RateLimit-Reset: 1701234600000
  ```
</CodeGroup>

## Rate Limit Exceeded (429)

When you exceed the rate limit, the API returns a `429 Too Many Requests` response:

```json theme={null}
{
  "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:

<CodeGroup>
  ```typescript TypeScript theme={null}
  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;
    }
  }
  ```

  ```python Python theme={null}
  import os
  import time
  from zavu_sdk import Zavu
  from zavu_sdk.models import errors

  def send_with_rate_limit_handling(zavu, to: str, text: str):
      try:
          response = zavu.messages.send.with_raw_response(to=to, text=text)

          # Check remaining quota
          remaining = int(response.headers.get("X-RateLimit-Remaining", 0))
          if remaining < 10:
              print(f"Warning: Low rate limit - {remaining} requests remaining")

          return response.parse()
      except errors.SDKError as e:
          if e.status_code == 429:
              retry_after = e.body.get("details", {}).get("retryAfter")
              wait_seconds = (retry_after - time.time() * 1000) / 1000 if retry_after else 60

              print(f"Rate limited. Waiting {wait_seconds:.1f}s before retry...")
              time.sleep(max(wait_seconds, 0))

              return send_with_rate_limit_handling(zavu, to, text)
          raise
  ```

  ```ruby Ruby theme={null}
  require 'zavudev'

  def send_with_rate_limit_handling(zavu, to:, text:)
    response = zavu.messages.send_with_response(to: to, text: text)

    remaining = response.headers["X-RateLimit-Remaining"].to_i
    if remaining < 10
      puts "Warning: Low rate limit - #{remaining} requests remaining"
    end

    response.data
  rescue Zavudev::RateLimitError => e
    retry_after = e.retry_after || 60
    puts "Rate limited. Waiting #{retry_after}s before retry..."
    sleep(retry_after)
    send_with_rate_limit_handling(zavu, to: to, text: text)
  end
  ```

  ```go Go theme={null}
  package main

  import (
      "context"
      "fmt"
      "time"

      "github.com/zavudev/sdk-go"
  )

  func sendWithRateLimitHandling(client *zavudev.Client, to, text string) (*zavudev.MessageResponse, error) {
      result, err := client.Messages.Send(context.TODO(), zavudev.MessageSendParams{
          To:   zavudev.String(to),
          Text: zavudev.String(text),
      })

      if err != nil {
          if rateLimitErr, ok := err.(*zavudev.RateLimitError); ok {
              retryAfter := rateLimitErr.RetryAfter
              if retryAfter == 0 {
                  retryAfter = 60 * time.Second
              }
              fmt.Printf("Rate limited. Waiting %v before retry...\n", retryAfter)
              time.Sleep(retryAfter)
              return sendWithRateLimitHandling(client, to, text)
          }
          return nil, err
      }

      return result, nil
  }
  ```

  ```php PHP theme={null}
  function sendWithRateLimitHandling($client, string $to, string $text) {
      try {
          $response = $client->messages->sendWithResponse([
              'to' => $to,
              'text' => $text,
          ]);

          $remaining = (int) $response->headers['X-RateLimit-Remaining'];
          if ($remaining < 10) {
              echo "Warning: Low rate limit - {$remaining} requests remaining\n";
          }

          return $response->data;
      } catch (\Zavudev\Exceptions\RateLimitException $e) {
          $retryAfter = $e->retryAfter ?? 60;
          echo "Rate limited. Waiting {$retryAfter}s before retry...\n";
          sleep($retryAfter);
          return sendWithRateLimitHandling($client, $to, $text);
      }
  }
  ```
</CodeGroup>

## 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

<CardGroup cols={2}>
  <Card title="Monitor Headers" icon="gauge">
    Always check `X-RateLimit-Remaining` to avoid hitting limits unexpectedly.
  </Card>

  <Card title="Implement Backoff" icon="clock">
    Use the `Retry-After` header or `retryAfter` field to wait the correct amount of time.
  </Card>

  <Card title="Use Broadcasts" icon="tower-broadcast">
    For bulk sending, use our [Broadcasts API](/guides/broadcasts/overview) instead of individual requests.
  </Card>

  <Card title="Use Idempotency Keys" icon="key">
    Include `idempotencyKey` in requests to safely retry without duplicates.
  </Card>
</CardGroup>

## Bulk Sending Example

For sending many messages, implement a simple rate limiter:

```typescript theme={null}
import Zavudev from '@zavudev/sdk';

const zavu = new Zavudev({
  apiKey: process.env['ZAVUDEV_API_KEY'],
});

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;
}
```

<Warning>
  For sending to more than 100 recipients, we strongly recommend using the [Broadcasts API](/guides/broadcasts/overview) instead. Broadcasts handle rate limiting, retries, and progress tracking automatically.
</Warning>

## Summary

| Tier                | Limit     | How to Get                      |
| ------------------- | --------- | ------------------------------- |
| Default             | 600/min   | Automatic for all projects      |
| Compliance Verified | 1,200/min | Complete business verification  |
| 10DLC Verified      | 2,400/min | Register 10DLC brand & campaign |

## Next Steps

<CardGroup cols={2}>
  <Card title="Compliance Verification" icon="shield-check" href="/guides/phone-numbers/regulatory-requirements">
    Learn about compliance requirements
  </Card>

  <Card title="Broadcasts API" icon="tower-broadcast" href="/guides/broadcasts/overview">
    Send to many recipients efficiently
  </Card>
</CardGroup>
