Lumail enforces rate limits to ensure fair usage and protect service quality for all users. This page explains how rate limiting works and how to handle rate-limited requests.

## Rate Limits by Plan

API requests are rate-limited per organization based on your subscription plan:

| Plan       | Requests per Minute |
| ---------- | ------------------- |
| Free       | 100                 |
| Premium    | 700                 |
| Enterprise | 2,000               |

Additionally, all requests are subject to an IP-based rate limit of **500 requests per second** to prevent abuse.

## Rate Limit Headers

Authenticated REST API responses include the current organization quota. Authentication failures still advertise the minimum API policy so clients can plan before presenting a token.

| Header                | Description                                      |
| --------------------- | ------------------------------------------------ |
| `RateLimit-Policy`    | Named quota and 60-second window                 |
| `RateLimit`           | Remaining requests and seconds until reset       |
| `RateLimit-Limit`     | Maximum requests allowed per minute              |
| `RateLimit-Remaining` | Remaining requests in the current window         |
| `RateLimit-Reset`     | Seconds until the current window resets          |
| `X-RateLimit-*`       | Legacy compatibility values for existing clients |

**Example headers:**

```http
RateLimit-Policy: "lumail-api";q=100;w=60
RateLimit: "lumail-api";r=95;t=42
RateLimit-Limit: 100
RateLimit-Remaining: 95
RateLimit-Reset: 42
```

## Rate Limit Exceeded Response

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

```json
{
  "message": "Too many requests",
  "docs": "https://lumail.io/docs/api-reference/api-limits"
}
```

**Additional headers on 429 responses:**

| Header        | Description                     |
| ------------- | ------------------------------- |
| `Retry-After` | Seconds to wait before retrying |

## Handling Rate Limits

### Best Practices

1. **Monitor headers** - Track `RateLimit-Remaining` to know when you're approaching the limit
2. **Implement backoff** - When rate limited, wait for the `Retry-After` duration before retrying
3. **Batch requests** - Combine multiple operations into fewer API calls where possible
4. **Cache responses** - Store frequently accessed data locally to reduce API calls

### Exponential Backoff Example

```javascript
async function fetchWithRetry(url, options, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    const response = await fetch(url, options);

    if (response.ok) {
      return response;
    }

    if (response.status === 429) {
      // Get retry delay from header or calculate exponentially
      const retryAfter = response.headers.get("Retry-After");
      const waitTime = retryAfter
        ? parseInt(retryAfter, 10) * 1000
        : Math.pow(2, i) * 1000;

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

    throw new Error(`HTTP ${response.status}: ${await response.text()}`);
  }

  throw new Error("Max retries exceeded");
}
```

### Checking Rate Limit Status

Before making requests, you can check your remaining quota:

```javascript
const response = await fetch("https://lumail.io/api/v1/tags", {
  headers: {
    Authorization: "Bearer YOUR_API_TOKEN",
  },
});

const limit = response.headers.get("RateLimit-Limit");
const remaining = response.headers.get("RateLimit-Remaining");
const resetAfter = response.headers.get("RateLimit-Reset");

console.log(`${remaining}/${limit} requests remaining`);
console.log(`Resets in ${resetAfter} seconds`);
```

## Increasing Your Rate Limit

If you need higher rate limits:

1. **Upgrade your plan** - Premium and Enterprise plans include higher limits
2. **Contact support** - For custom enterprise needs, we can discuss tailored solutions

## Common Scenarios

### Bulk Operations

For bulk imports or updates, consider:

- Using the `POST /api/v1/subscribers` endpoint which handles upserts
- Spreading requests over time instead of bursting
- Processing in batches with delays between batches

```javascript
async function bulkImport(subscribers, batchSize = 50, delayMs = 1000) {
  for (let i = 0; i < subscribers.length; i += batchSize) {
    const batch = subscribers.slice(i, i + batchSize);

    // Process batch in parallel
    await Promise.all(
      batch.map((sub) =>
        fetch("https://lumail.io/api/v1/subscribers", {
          method: "POST",
          headers: {
            Authorization: "Bearer YOUR_API_TOKEN",
            "Content-Type": "application/json",
          },
          body: JSON.stringify(sub),
        }),
      ),
    );

    // Wait between batches to stay under rate limit
    if (i + batchSize < subscribers.length) {
      await new Promise((resolve) => setTimeout(resolve, delayMs));
    }
  }
}
```

### Webhooks

When receiving webhooks and calling the API:

- Process webhook events asynchronously
- Implement a queue to control request rate
- Use `triggerWorkflows: false` when updating subscribers to avoid cascading API calls

## Related Documentation

- [API Tokens](/docs/api-reference/api-tokens) - Authentication setup
- [Create Subscriber](/docs/api-reference/api-subscribers-post) - Bulk import best practices
- [Workflows](/docs/workflows) - Automate without API calls
