All documentation

Rate Limiting

Quota headers and rate-limit behavior.

Rate Limiting

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
Window
1 hour
3,600 seconds
Request Cost
1
request per API call

Quota Headers

Responses include quota information in headers:

X-Quota-Limit: 10000
X-Quota-Remaining: 8750
X-Quota-Reset: 1640995200
HeaderDescription
X-Quota-LimitTotal quota limit for the current window
X-Quota-RemainingRemaining requests in the current window
X-Quota-ResetUnix timestamp when the quota resets

Checking Quota

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

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:

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

Use whichever is available first: the Retry-After header, or error.details.retryAfter in the response body.

Retry Logic

Implement exponential backoff when receiving 429 responses:

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');
}

Best Practices

1. Monitor Quota Usage

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

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:

Multiple calls
// Instead of multiple calls
for (const item of items) {
  await createListItem(item);
}
Batch if supported
// Batch if the API supports it
await createListItems(items);

4. Cache Responses

Cache responses when appropriate to reduce API calls:

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

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:

SettingsUsage Quota

Programmatic Access

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

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

Response:

{
  "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