# Rate limits

KrosAI applies rate limits to ensure fair usage and platform stability.

## Limits by Plan

| Plan       | Requests/Second | Requests/Day | Concurrent Calls |
| ---------- | --------------- | ------------ | ---------------- |
| Free       | 10              | 1,000        | 1                |
| Pro        | 50              | 50,000       | 10               |
| Business   | 200             | 500,000      | 50               |
| Enterprise | Custom          | Custom       | Unlimited        |

## Rate Limit Headers

Every API response includes rate limit information:

```
X-RateLimit-Limit: 50
X-RateLimit-Remaining: 45
X-RateLimit-Reset: 1704891600
```

| Header                  | Description                          |
| ----------------------- | ------------------------------------ |
| `X-RateLimit-Limit`     | Maximum requests per second          |
| `X-RateLimit-Remaining` | Requests remaining in current window |
| `X-RateLimit-Reset`     | Unix timestamp when limit resets     |

## Rate Limit Response

When you exceed the rate limit, you'll receive a `429 Too Many Requests` response:

```json
{
  "error": "Rate limit exceeded",
  "code": "RATE_LIMIT_EXCEEDED",
  "retry_after": 1.5
}
```

The `Retry-After` header indicates seconds to wait:

```
Retry-After: 1.5
```

## Endpoint-Specific Limits

Some endpoints have additional limits:

| Endpoint               | Limit   | Notes                  |
| ---------------------- | ------- | ---------------------- |
| `POST /outbound-calls` | 10/sec  | Per organization       |
| `POST /phone-numbers`  | 5/sec   | KYC required           |
| `GET /calls`           | 100/sec | Pagination recommended |
| `POST /webhooks`       | 10/sec  | Per organization       |

## Concurrent Call Limits

Outbound calls have concurrency limits:

| Plan       | Concurrent Outbound |
| ---------- | ------------------- |
| Free       | 1                   |
| Pro        | 10                  |
| Business   | 50                  |
| Enterprise | Unlimited           |

Exceeding the concurrent call limit returns:

```json
{
  "error": "Maximum concurrent calls reached",
  "code": "CONCURRENT_LIMIT_EXCEEDED",
  "current": 10,
  "limit": 10
}
```

## Best Practices

### Implement Exponential Backoff

{% code title="exponential-backoff.ts" %}

```typescript
async function makeRequestWithRetry(
  fn: () => Promise<Response>,
  maxRetries = 3
): Promise<Response> {
  let lastError: Error | null = null;
  
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      const response = await fn();
      
      if (response.status === 429) {
        const retryAfter = parseFloat(
          response.headers.get('Retry-After') || '1'
        );
        await sleep(retryAfter * 1000 * Math.pow(2, attempt));
        continue;
      }
      
      return response;
    } catch (error) {
      lastError = error as Error;
    }
  }
  
  throw lastError || new Error('Max retries exceeded');
}
```

{% endcode %}

### Use Batch Operations

{% code title="batch-requests.ts" %}

```typescript
// ❌ Avoid: Multiple individual requests
for (const number of numbers) {
  await fetch(`/phone-numbers/${number.id}`);
}

// ✅ Better: Single batch request
await fetch('/phone-numbers', {
  params: { ids: numbers.map(n => n.id).join(',') }
});
```

{% endcode %}

### Cache Responses

{% code title="cache-example.ts" %}

```typescript
const CACHE_TTL = 60 * 1000; // 1 minute

async function getPhoneNumbers() {
  const cached = cache.get('phone_numbers');
  if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
    return cached.data;
  }
  
  const response = await fetch('/phone-numbers');
  const data = await response.json();
  
  cache.set('phone_numbers', { data, timestamp: Date.now() });
  return data;
}
```

{% endcode %}

### Monitor Usage

{% code title="log-rate-limits.ts" %}

```typescript
function logRateLimits(response: Response) {
  const limit = response.headers.get('X-RateLimit-Limit');
  const remaining = response.headers.get('X-RateLimit-Remaining');
  const reset = response.headers.get('X-RateLimit-Reset');
  
  console.log(`Rate Limit: ${remaining}/${limit} (resets: ${reset})`);
  
  if (parseInt(remaining || '0') < 10) {
    console.warn('Approaching rate limit!');
  }
}
```

{% endcode %}

### Queue Outbound Calls

{% code title="call-queue.ts" %}

```typescript
class CallQueue {
  private queue: CallRequest[] = [];
  private active = 0;
  private maxConcurrent = 10;
  
  async add(request: CallRequest) {
    this.queue.push(request);
    this.process();
  }
  
  private async process() {
    while (this.queue.length > 0 && this.active < this.maxConcurrent) {
      const request = this.queue.shift()!;
      this.active++;
      
      try {
        await this.initiateCall(request);
      } finally {
        this.active--;
        this.process();
      }
    }
  }
}
```

{% endcode %}

## Requesting Higher Limits

Need higher limits? Contact us:

* **Pro/Business:** Upgrade to a higher plan
* **Enterprise:** Custom limits available

Email <support@krosai.com> with:

{% stepper %}
{% step %}

### Organization ID

Provide your organization ID.
{% endstep %}

{% step %}

### Current usage patterns

Describe your current usage patterns (traffic, peak times, typical requests).
{% endstep %}

{% step %}

### Required limits

Specify the limits you need (requests/sec, requests/day, concurrency).
{% endstep %}

{% step %}

### Use case description

Explain the use case and why higher limits are required.
{% endstep %}
{% endstepper %}

## Next Steps

* [Error Codes](file:///reference/error-codes) - Handle errors properly
* [SDKs](file:///sdks) - Use official SDKs with built-in retry logic


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.krosai.com/reference/rate-limits.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
