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

# Rate Limits

> Atlas API rate limits, headers, and how to handle 429 responses.

## Rate Limit Summary

| Endpoint                         | Limit       | Window        |
| -------------------------------- | ----------- | ------------- |
| `POST /api/v1/*` (single events) | 1,000 req/s | Per API key   |
| `POST /api/v1/*/batch`           | 100 req/s   | Per API key   |
| `POST /v1/events`                | 500 req/s   | Per JWT token |
| `POST /v1/events/batch`          | 50 req/s    | Per JWT token |
| Backend API (`/v1/*`, `/auth/*`) | 60 req/min  | Per user      |

## Rate Limit Headers

Every API response includes rate limit information:

```http theme={null}
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 987
X-RateLimit-Reset: 1710161400
Retry-After: 1     # Only present on 429 responses
```

| Header                  | Description                                   |
| ----------------------- | --------------------------------------------- |
| `X-RateLimit-Limit`     | Maximum requests allowed in the window        |
| `X-RateLimit-Remaining` | Requests remaining in current window          |
| `X-RateLimit-Reset`     | Unix timestamp when the window resets         |
| `Retry-After`           | Seconds to wait before retrying (on 429 only) |

## Handling 429 Responses

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

```json theme={null}
{
  "error": {
    "code": "RATE_LIMITED",
    "message": "Rate limit exceeded. Retry after 1 second.",
    "retry_after": 1
  }
}
```

### Exponential Backoff Pattern

```javascript theme={null}
async function callWithBackoff(fn, maxRetries = 5) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const response = await fn();

    if (response.status !== 429) {
      return response;
    }

    const retryAfter = parseInt(response.headers.get('Retry-After') || '1');
    const jitter = Math.random() * 1000; // Add jitter to avoid thundering herd
    const delay = (retryAfter * 1000) * Math.pow(2, attempt) + jitter;

    console.warn(`Rate limited. Retrying in ${delay}ms (attempt ${attempt + 1})`);
    await new Promise(resolve => setTimeout(resolve, delay));
  }

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

## High-Volume Integration Patterns

### Pattern 1 — Use Batch Endpoints

If you're hitting single-event rate limits, switch to batch:

```
Individual calls: 1,000 events → 1,000 requests (hits limit)
Batch calls:      1,000 events → 2 requests (well within limit)
```

### Pattern 2 — Queue and Flush

Buffer events in memory and flush in batches:

```javascript theme={null}
class AtlasBatcher {
  constructor(apiKey, flushInterval = 1000, maxBatchSize = 500) {
    this.queue = [];
    this.apiKey = apiKey;
    setInterval(() => this.flush(), flushInterval);
    this.maxBatchSize = maxBatchSize;
  }

  track(event) {
    this.queue.push(event);
    if (this.queue.length >= this.maxBatchSize) {
      this.flush();
    }
  }

  async flush() {
    if (this.queue.length === 0) return;
    const batch = this.queue.splice(0, this.maxBatchSize);
    await fetch('https://events.atlas.io/api/v1/sport/batch', {
      method: 'POST',
      headers: { 'X-API-Key': this.apiKey, 'Content-Type': 'application/json' },
      body: JSON.stringify({ events: batch }),
    });
  }
}
```

### Pattern 3 — Multiple API Keys

For very high-volume operations, you can create multiple API keys (one per service/worker) to distribute load across separate rate limit buckets.

<Note>
  Contact [platform@atlas.io](mailto:platform@atlas.io) if you need higher rate limits for your use case.
  Enterprise plans include custom limits.
</Note>
