> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://developers.brevo.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://developers.brevo.com/_mcp/server.

# Rate limit headers

## Overview

When you exceed a rate limit, the Brevo API returns a `429 Too Many Requests` status code along with response headers describing the current rate limit status. Use these headers to monitor API usage and implement retry logic.

Use rate limit headers to:

* Track remaining request capacity
* Determine when rate limits reset
* Implement exponential backoff
* Prevent unnecessary API calls

## Rate limit headers

All rate-limited endpoints include the following headers in their responses:

| Header                      | Description                                                                                        |
| --------------------------- | -------------------------------------------------------------------------------------------------- |
| `x-sib-ratelimit-limit`     | Maximum number of requests allowed in the current time window before receiving a `429` status code |
| `x-sib-ratelimit-remaining` | Number of requests remaining in the current time window                                            |
| `x-sib-ratelimit-reset`     | Time remaining until the rate limit resets, expressed in the granularity unit (typically seconds)  |

![Rate limit headers example](https://fdr-prod-docs-files-public.s3.us-east-1.amazonaws.com/brevo.docs.buildwithfern.com/bf8f6435a70dcb84d98b5bd9fbff8e1bcba6d2b4414268c6c03170591734939c/docs/assets/images/716bbf7-image.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIA6KXJSKKNFOCF7G4B%2F20260914%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260914T131350Z&X-Amz-Expires=604800&X-Amz-Signature=99f3fb3f6d19d0f215d7ed53e5ccc2b9684da62ab22bdfcd2ab1579aa7171db8&X-Amz-SignedHeaders=host&x-amz-checksum-mode=ENABLED&x-id=GetObject)

## Reading rate limit headers

### Limit header

The `x-sib-ratelimit-limit` header indicates the maximum number of requests you can make in the current time window. This value depends on your account plan and the specific endpoint.

Example:

```
x-sib-ratelimit-limit: 1000
```

This means you can make up to 1,000 requests in the current time window.

### Remaining header

The `x-sib-ratelimit-remaining` header shows how many requests you have left before hitting the rate limit. Monitor this value to avoid `429` errors.

Example:

```
x-sib-ratelimit-remaining: 750
```

This indicates 750 requests remain in the current time window.

### Reset header

The `x-sib-ratelimit-reset` header specifies when the rate limit counter resets. The value is expressed in the same granularity unit as the rate limit (typically seconds).

Example:

```
x-sib-ratelimit-reset: 45
```

This means the rate limit resets in 45 seconds.

## Implementing retry logic

Use rate limit headers to implement retry mechanisms:

### Check remaining capacity

Before making requests, check the `x-sib-ratelimit-remaining` header to confirm you have capacity:

#### Node.js

```javascript
const response = await fetch('https://api.brevo.com/v3/account', {
  headers: { 'api-key': 'YOUR_API_KEY' }
});
const remaining = parseInt(response.headers.get('x-sib-ratelimit-remaining') || '0', 10);

if (remaining < 10) {
  // Wait before making more requests
  await new Promise(resolve => setTimeout(resolve, 60000));
}
```

#### PHP

```php
$remaining = 0;
$ch = curl_init('https://api.brevo.com/v3/account');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['api-key: YOUR_API_KEY']);
curl_setopt($ch, CURLOPT_HEADERFUNCTION, function($curl, $header) use (&$remaining) {
    if (preg_match('/^x-sib-ratelimit-remaining:\s*(.+)$/i', $header, $matches)) {
        $remaining = (int) $matches[1];
    }
    return strlen($header);
});

curl_exec($ch);
curl_close($ch);

if ($remaining < 10) {
    // Wait before making more requests
    sleep(60);
}
```

#### Python

```python
import requests
import time

response = requests.get('https://api.brevo.com/v3/account', headers={'api-key': 'YOUR_API_KEY'})
remaining = int(response.headers.get('x-sib-ratelimit-remaining', 0))

if remaining < 10:
    # Wait before making more requests
    time.sleep(60)
```

### Handle 429 responses

When you receive a `429` response, read the reset header to determine how long to wait:

#### Node.js

```javascript
if (response.status === 429) {
  const resetTime = parseInt(response.headers.get('x-sib-ratelimit-reset') || '60', 10);
  await new Promise(resolve => setTimeout(resolve, resetTime * 1000));
  // Retry the request
}
```

#### PHP

```php
if ($httpCode === 429) {
    $resetTime = 60;
    if (isset($responseHeaders['x-sib-ratelimit-reset'])) {
        $resetTime = (int) $responseHeaders['x-sib-ratelimit-reset'];
    }
    sleep($resetTime);
    // Retry the request
}
```

#### Python

```python
if response.status_code == 429:
    reset_time = int(response.headers.get('x-sib-ratelimit-reset', 60))
    time.sleep(reset_time)
    # Retry the request
```

### Exponential backoff

Combine rate limit headers with exponential backoff for resilient error handling:

#### Node.js

```javascript
async function makeRequestWithBackoff(url, headers, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const response = await fetch(url, { headers });
    
    if (response.status === 429) {
      const resetTime = parseInt(response.headers.get('x-sib-ratelimit-reset') || '60', 10);
      const waitTime = (resetTime + Math.pow(2, attempt) + Math.random()) * 1000;
      await new Promise(resolve => setTimeout(resolve, waitTime));
      continue;
    }
    
    return response;
  }
  
  throw new Error('Max retries exceeded');
}
```

#### PHP

```php
function makeRequestWithBackoff($url, $headers, $maxRetries = 3) {
    for ($attempt = 0; $attempt < $maxRetries; $attempt++) {
        $responseHeaders = [];
        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($ch, CURLOPT_HEADERFUNCTION, function($curl, $header) use (&$responseHeaders) {
            $len = strlen($header);
            $header = explode(':', $header, 2);
            if (count($header) === 2) {
                $responseHeaders[strtolower(trim($header[0]))] = trim($header[1]);
            }
            return $len;
        });
        
        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
        
        if ($httpCode === 429) {
            $resetTime = isset($responseHeaders['x-sib-ratelimit-reset']) 
                ? (int) $responseHeaders['x-sib-ratelimit-reset'] 
                : 60;
            $waitTime = $resetTime + pow(2, $attempt) + mt_rand(0, 1000) / 1000;
            sleep((int) $waitTime);
            continue;
        }
        
        return $response;
    }
    
    throw new Exception('Max retries exceeded');
}
```

#### Python

```python
import time
import random

def make_request_with_backoff(url, headers, max_retries=3):
    for attempt in range(max_retries):
        response = requests.get(url, headers=headers)
        
        if response.status_code == 429:
            reset_time = int(response.headers.get('x-sib-ratelimit-reset', 60))
            wait_time = reset_time + (2 ** attempt) + random.uniform(0, 1)
            time.sleep(wait_time)
            continue
        
        return response
    
    raise Exception("Max retries exceeded")
```

## Best practices

### Monitor headers proactively

Check `x-sib-ratelimit-remaining` before making bulk requests to avoid hitting limits:

#### Node.js

```javascript
const remaining = parseInt(response.headers.get('x-sib-ratelimit-remaining') || '0', 10);
if (remaining < batchSize) {
  // Delay or split the batch
}
```

#### PHP

```php
$remaining = isset($responseHeaders['x-sib-ratelimit-remaining']) 
    ? (int) $responseHeaders['x-sib-ratelimit-remaining'] 
    : 0;

if ($remaining < $batchSize) {
    // Delay or split the batch
}
```

#### Python

```python
remaining = int(response.headers.get('x-sib-ratelimit-remaining', 0))
if remaining < batch_size:
    # Delay or split the batch
    pass
```

### Cache header values

Rate limit headers are consistent for the same endpoint and account. Cache them to reduce unnecessary API calls when checking limits.

### Use webhooks for high-volume operations

For high-volume operations like fetching statistics, use [webhooks](/docs/how-to-use-webhooks) instead of polling. This reduces API calls and eliminates rate limit concerns.

Rate limit headers are included in all responses, not just `429` errors. Monitor them proactively to prevent rate limit issues.