---
title: "Rate Limiting"
description: "Quota headers and rate-limit behavior."
collection: "api-reference"
slug: "rate-limiting"
url: "https://naiza.ai/docs/api-reference/rate-limiting"
markdown: "https://naiza.ai/docs/api-reference/rate-limiting.md"
full_docs: "https://naiza.ai/docs.md"
product: "Naiza"
base_url: "https://api.naiza.ai/api/v1"
---

# Rate Limiting

> Quota headers and rate-limit behavior.

## Table of contents

- [Overview](#overview)
- [Quota System](#quota-system)
  - [Default Quota](#default-quota)
  - [Quota Headers](#quota-headers)
  - [Checking Quota](#checking-quota)
- [Rate Limiting](#rate-limiting)
  - [Per-Endpoint Limits](#per-endpoint-limits)
  - [Rate Limit Headers](#rate-limit-headers)
- [Handling 429 Responses](#handling-429-responses)
  - [Retry Logic](#retry-logic)
  - [Python Example](#python-example)
- [Best Practices](#best-practices)
  - [1. Monitor Quota Usage](#1-monitor-quota-usage)
  - [2. Implement Request Throttling](#2-implement-request-throttling)
  - [3. Batch Operations](#3-batch-operations)
  - [4. Cache Responses](#4-cache-responses)
  - [5. Handle Quota Exhaustion Gracefully](#5-handle-quota-exhaustion-gracefully)
- [Quota Plans](#quota-plans)
  - [Default Plan](#default-plan)
  - [Enterprise Plans](#enterprise-plans)
- [Monitoring](#monitoring)
  - [Dashboard](#dashboard)
  - [Programmatic Access](#programmatic-access)
- [Troubleshooting](#troubleshooting)
  - ["Quota exceeded" errors](#quota-exceeded-errors)
  - [High quota usage](#high-quota-usage)

## Overview

Naiza enforces rate limits and quotas to ensure fair usage and system stability. All external API endpoints are subject to these limits.

## Quota System

### Default Quota

- **Default Limit**: 10,000 requests per hour (configurable per tenant)
- **Window**: 1 hour (3,600 seconds)
- **Request Cost**: 1 request per API call

### Quota Headers

Responses include quota information in headers:

```http
X-Quota-Limit: 10000
X-Quota-Remaining: 8750
X-Quota-Reset: 1640995200
```

- `X-Quota-Limit`: Total quota limit for the current window
- `X-Quota-Remaining`: Remaining requests in the current window
- `X-Quota-Reset`: Unix timestamp when the quota resets

### Checking Quota

```javascript
const response = await fetch(url, options);
const remaining = response.headers.get('X-Quota-Remaining');
const limit = response.headers.get('X-Quota-Limit');

console.log(`Quota: ${remaining}/${limit} remaining`);
```

## Rate Limiting

### Per-Endpoint Limits

Different endpoints may have different rate limits:

- **Write Operations** (POST, DELETE): Higher limits for critical operations
- **Read Operations** (GET): Standard limits
- **Current default throttle in controllers**: 300 requests / 60 seconds per key for most public endpoints

### Rate Limit Headers

```http
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1640995200
Retry-After: 60
```

## Handling 429 Responses

When you exceed rate limits or quotas, you'll receive a `429 Too Many Requests` response:

```json
{
  "error": {
    "code": "QUOTA_EXCEEDED",
    "message": "Quota exceeded",
    "details": {
      "limit": 10000,
      "window": "1 hour",
      "retryAfter": 3600
    }
  }
}
```

Use whichever is available first:

- `Retry-After` header
- `error.details.retryAfter` in response body

### Retry Logic

Implement exponential backoff when receiving 429 responses:

```javascript
async function makeRequestWithBackoff(url, options, maxRetries = 5) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const response = await fetch(url, options);
    
    if (response.ok) {
      return response;
    }
    
    if (response.status === 429) {
      const error = await response.json();
      const retryAfter = error.error.details.retryAfter || 
                        parseInt(response.headers.get('Retry-After')) || 
                        Math.pow(2, attempt);
      
      console.log(`Rate limited. Retrying after ${retryAfter} seconds`);
      await sleep(retryAfter * 1000);
      continue;
    }
    
    // Don't retry other errors
    throw new Error(`Request failed: ${response.status}`);
  }
  
  throw new Error('Max retries exceeded');
}
```

### Python Example

```python
import time
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

def make_request_with_backoff(url, headers, data, max_retries=5):
    session = requests.Session()
    
    retry_strategy = Retry(
        total=max_retries,
        backoff_factor=1,
        status_forcelist=[429],
        allowed_methods=["GET", "POST", "DELETE"]
    )
    
    adapter = HTTPAdapter(max_retries=retry_strategy)
    session.mount("https://", adapter)
    
    return session.post(url, headers=headers, json=data)
```

## Best Practices

### 1. Monitor Quota Usage

```javascript
function checkQuotaBeforeRequest() {
  // Store quota info from last response
  const quotaRemaining = getStoredQuotaRemaining();
  
  if (quotaRemaining < 100) {
    console.warn('Low quota remaining. Consider throttling requests.');
  }
}
```

### 2. Implement Request Throttling

```javascript
class RateLimiter {
  constructor(maxRequests, windowMs) {
    this.maxRequests = maxRequests;
    this.windowMs = windowMs;
    this.requests = [];
  }
  
  async waitIfNeeded() {
    const now = Date.now();
    
    // Remove old requests outside the window
    this.requests = this.requests.filter(
      time => now - time < this.windowMs
    );
    
    if (this.requests.length >= this.maxRequests) {
      const oldestRequest = this.requests[0];
      const waitTime = this.windowMs - (now - oldestRequest);
      await sleep(waitTime);
      return this.waitIfNeeded();
    }
    
    this.requests.push(now);
  }
}

const limiter = new RateLimiter(100, 60000); // 100 requests per minute

async function makeThrottledRequest(url, options) {
  await limiter.waitIfNeeded();
  return fetch(url, options);
}
```

### 3. Batch Operations

When possible, batch multiple operations to reduce API calls:

```javascript
// Instead of multiple calls
for (const item of items) {
  await createListItem(item);
}

// Batch if the API supports it
await createListItems(items);
```

### 4. Cache Responses

Cache responses when appropriate to reduce API calls:

```javascript
const cache = new Map();

async function getCachedDecision(decisionId) {
  const cacheKey = `decision:${decisionId}`;
  
  if (cache.has(cacheKey)) {
    const cached = cache.get(cacheKey);
    if (Date.now() - cached.timestamp < 60000) { // 1 minute cache
      return cached.data;
    }
  }
  
  const response = await fetch(`/api/v1/decisions/${decisionId}`, options);
  const data = await response.json();
  
  cache.set(cacheKey, {
    data,
    timestamp: Date.now(),
  });
  
  return data;
}
```

### 5. Handle Quota Exhaustion Gracefully

```javascript
async function handleQuotaExhaustion() {
  // Option 1: Queue requests for later
  await queueRequest(request);
  
  // Option 2: Notify user
  notifyUser('API quota exhausted. Requests will be processed when quota resets.');
  
  // Option 3: Fallback behavior
  return getCachedDecision(decisionId);
}
```

## Quota Plans

Quota limits vary by subscription plan. Contact support to upgrade your plan for higher limits.

### Default Plan
- **Quota**: 10,000 requests/hour
- **Window**: 1 hour

### Enterprise Plans
- Custom quotas available
- Dedicated rate limits
- Priority support

## Monitoring

### Dashboard

Monitor your quota usage in the Naiza dashboard:
- **Settings** → **Usage** → **Quota**

### Programmatic Access

Check usage via the dashboard API (JWT + tenant context — not the external `x-api-key` `/api/v1` surface):

```http
GET /v1/usage/summary
Authorization: Bearer <jwt-token>
```

Response:
```json
{
  "periodStart": "2026-04-01T00:00:00.000Z",
  "periodEnd": "2026-05-01T00:00:00.000Z",
  "apiRequestsCount": 125000,
  "eventsCount": 98000,
  "distinctCustomersCount": 12000,
  "asOf": "2026-04-16T10:30:00.000Z"
}
```

## Troubleshooting

### "Quota exceeded" errors

1. **Check current usage**: Review quota headers in responses
2. **Wait for reset**: Quota resets at the start of each window
3. **Optimize requests**: Reduce unnecessary API calls
4. **Upgrade plan**: Contact support for higher limits

### High quota usage

1. **Review request patterns**: Identify unnecessary calls
2. **Implement caching**: Cache responses when appropriate
3. **Batch operations**: Combine multiple operations
4. **Monitor headers**: Track quota usage via response headers

## Related documentation

- [API Overview](https://naiza.ai/docs/api-reference/overview.md) — Base URL, versioning, and high-level API surface.
- [Authentication](https://naiza.ai/docs/api-reference/authentication.md) — API keys, Web SDK tokens, and secure key handling.
- [Web SDK API](https://naiza.ai/docs/api-reference/websdk.md) — Browser SDK endpoints and device signal collection.
- [Events API](https://naiza.ai/docs/api-reference/events.md) — Submit and query product events for risk evaluation.
- [Sessions API](https://naiza.ai/docs/api-reference/sessions.md) — Session grouping and timeline endpoints.
- [Error Handling](https://naiza.ai/docs/api-reference/errors.md) — Error shapes, status codes, and retry guidance.
- [Decisions API](https://naiza.ai/docs/api-reference/decisions.md) — approve / deny / review evaluation and decision payloads (Events API uses ALLOW/REVIEW/BLOCK).
- [Lists API](https://naiza.ai/docs/api-reference/lists.md) — Allowlists, blocklists, and list membership management.

---

*Source: [https://naiza.ai/docs/api-reference/rate-limiting](https://naiza.ai/docs/api-reference/rate-limiting) · Full docs: [https://naiza.ai/docs.md](https://naiza.ai/docs.md)*
