---
title: "Rate Limiting"
description: "Understanding and handling API rate limits in TraceRTM"
---

# Rate Limiting

TraceRTM applies rate limits to ensure fair usage and protect service stability. This guide covers rate limit tiers, headers, and handling strategies.

## Rate Limit Tiers

| Plan | Requests/Minute | Requests/Hour | Requests/Day | Burst |
|------|-----------------|---------------|--------------|-------|
| **Free** | 60 | 1,000 | 10,000 | 10 |
| **Pro** | 300 | 10,000 | 100,000 | 50 |
| **Team** | 600 | 30,000 | 500,000 | 100 |
| **Enterprise** | 1,200 | 100,000 | Unlimited | 200 |

---

## Rate Limit Headers

Every API response includes rate limit headers:

```http
HTTP/1.1 200 OK
X-RateLimit-Limit: 300
X-RateLimit-Remaining: 287
X-RateLimit-Reset: 1704067260
X-RateLimit-Used: 13
X-RateLimit-Resource: api
```

### Header Descriptions

| Header | Description |
|--------|-------------|
| `X-RateLimit-Limit` | Maximum requests allowed in the window |
| `X-RateLimit-Remaining` | Requests remaining in the current window |
| `X-RateLimit-Reset` | Unix timestamp when the window resets |
| `X-RateLimit-Used` | Requests used in the current window |
| `X-RateLimit-Resource` | Rate limit category (api, search, batch) |

---

## Rate Limit Categories

Different operations have separate rate limits:

| Category | Description | Typical Limit (Pro) |
|----------|-------------|---------------------|
| `api` | Standard API calls | 300/min |
| `search` | Search operations | 60/min |
| `batch` | Batch operations | 30/min |
| `export` | Data exports | 10/min |
| `webhook` | Webhook deliveries | 100/min |

### Check Category Limits

```bash
curl "https://api.tracertm.dev/v1/rate-limits" \
  -H "Authorization: Bearer rtm_live_sk_..."
```

```json
{
  "data": {
    "api": {
      "limit": 300,
      "remaining": 287,
      "reset": 1704067260
    },
    "search": {
      "limit": 60,
      "remaining": 58,
      "reset": 1704067260
    },
    "batch": {
      "limit": 30,
      "remaining": 30,
      "reset": 1704067260
    }
  }
}
```

---

## Rate Limit Exceeded Response

When rate limited, you receive a `429 Too Many Requests` response:

```http
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 300
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1704067260
Retry-After: 45
Content-Type: application/json

{
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "Rate limit exceeded. Please retry after 45 seconds.",
    "status": 429,
    "details": {
      "limit": 300,
      "reset_at": "2024-01-01T12:01:00Z",
      "retry_after": 45,
      "resource": "api"
    }
  }
}
```

---

## Handling Rate Limits

### Python

```python
import time
from tracertm import TraceRTMClient
from tracertm.exceptions import RateLimitError

client = TraceRTMClient(api_key="rtm_live_sk_...")

def fetch_with_retry(project_id: str, max_retries: int = 3):
    for attempt in range(max_retries):
        try:
            return client.items.list(project_id=project_id)
        except RateLimitError as e:
            if attempt == max_retries - 1:
                raise

            wait_time = e.retry_after or 60
            print(f"Rate limited. Waiting {wait_time}s...")
            time.sleep(wait_time)

# With exponential backoff
import random

def fetch_with_exponential_backoff(project_id: str, max_retries: int = 5):
    base_delay = 1

    for attempt in range(max_retries):
        try:
            return client.items.list(project_id=project_id)
        except RateLimitError as e:
            if attempt == max_retries - 1:
                raise

            # Use Retry-After if provided, otherwise exponential backoff
            if e.retry_after:
                delay = e.retry_after
            else:
                delay = base_delay * (2 ** attempt) + random.uniform(0, 1)

            print(f"Attempt {attempt + 1} rate limited. Waiting {delay:.1f}s...")
            time.sleep(delay)

# Built-in retry support
client = TraceRTMClient(
    api_key="rtm_live_sk_...",
    max_retries=3,
    retry_on_rate_limit=True
)

# Automatic retries on rate limit
items = client.items.list(project_id="proj_123")
```

### TypeScript

```typescript
import { TraceRTMClient, RateLimitError } from '@tracertm/sdk';

const client = new TraceRTMClient({ apiKey: 'rtm_live_sk_...' });

async function fetchWithRetry(projectId: string, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await client.items.list({ projectId });
    } catch (error) {
      if (error instanceof RateLimitError && attempt < maxRetries - 1) {
        const waitTime = error.retryAfter || 60;
        console.log(`Rate limited. Waiting ${waitTime}s...`);
        await new Promise(resolve => setTimeout(resolve, waitTime * 1000));
      } else {
        throw error;
      }
    }
  }
}

// With exponential backoff
async function fetchWithExponentialBackoff(projectId: string, maxRetries = 5) {
  const baseDelay = 1000;

  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await client.items.list({ projectId });
    } catch (error) {
      if (error instanceof RateLimitError && attempt < maxRetries - 1) {
        const delay = error.retryAfter
          ? error.retryAfter * 1000
          : baseDelay * Math.pow(2, attempt) + Math.random() * 1000;

        console.log(`Attempt ${attempt + 1} rate limited. Waiting ${delay}ms...`);
        await new Promise(resolve => setTimeout(resolve, delay));
      } else {
        throw error;
      }
    }
  }
}

// Built-in retry support
const clientWithRetry = new TraceRTMClient({
  apiKey: 'rtm_live_sk_...',
  maxRetries: 3,
  retryOnRateLimit: true
});
```

### Go

```go
import (
    "context"
    "time"
    "github.com/tracertm/tracertm-go"
)

func fetchWithRetry(ctx context.Context, client *tracertm.Client, projectID string, maxRetries int) (*tracertm.ItemList, error) {
    var lastErr error

    for attempt := 0; attempt < maxRetries; attempt++ {
        items, err := client.Items.List(ctx, projectID, nil)
        if err == nil {
            return items, nil
        }

        rateLimitErr, ok := err.(*tracertm.RateLimitError)
        if !ok || attempt == maxRetries-1 {
            return nil, err
        }

        lastErr = err
        waitTime := time.Duration(rateLimitErr.RetryAfter) * time.Second
        if waitTime == 0 {
            waitTime = time.Duration(1<<attempt) * time.Second
        }

        time.Sleep(waitTime)
    }

    return nil, lastErr
}

// With built-in retry
client := tracertm.NewClient("rtm_live_sk_...",
    tracertm.WithMaxRetries(3),
    tracertm.WithRetryOnRateLimit(true),
)
```

---

## Best Practices

### 1. Monitor Rate Limit Headers

```python
response = client.items.list(project_id="proj_123")

# Check remaining quota
remaining = response.rate_limit.remaining
if remaining < 10:
    print(f"Warning: Only {remaining} requests remaining")
```

### 2. Implement Request Queuing

```python
import asyncio
from collections import deque

class RateLimitedClient:
    def __init__(self, client, requests_per_second=5):
        self.client = client
        self.min_interval = 1.0 / requests_per_second
        self.last_request = 0
        self.queue = deque()

    async def request(self, method, *args, **kwargs):
        # Ensure minimum interval between requests
        now = time.time()
        elapsed = now - self.last_request
        if elapsed < self.min_interval:
            await asyncio.sleep(self.min_interval - elapsed)

        self.last_request = time.time()
        return await getattr(self.client, method)(*args, **kwargs)
```

### 3. Use Batch Operations

```python
# Instead of individual requests
for item_data in items_to_create:
    client.items.create(project_id="proj_123", **item_data)  # 100 requests

# Use batch operations
client.items.bulk_create(
    project_id="proj_123",
    items=items_to_create  # 1 request
)
```

### 4. Cache Responses

```python
from functools import lru_cache
import time

class CachedClient:
    def __init__(self, client, cache_ttl=300):
        self.client = client
        self.cache = {}
        self.cache_ttl = cache_ttl

    def get_project(self, project_id: str):
        cache_key = f"project:{project_id}"
        cached = self.cache.get(cache_key)

        if cached and time.time() - cached['time'] < self.cache_ttl:
            return cached['data']

        data = self.client.projects.get(project_id)
        self.cache[cache_key] = {'data': data, 'time': time.time()}
        return data
```

### 5. Respect Retry-After Header

```python
def make_request_with_backoff():
    while True:
        response = requests.get(url, headers=headers)

        if response.status_code == 429:
            retry_after = int(response.headers.get('Retry-After', 60))
            time.sleep(retry_after)
            continue

        return response
```

---

## Rate Limit Increase

To request a rate limit increase:

1. **Enterprise customers**: Contact your account manager
2. **Pro/Team customers**: Submit a request via support portal
3. **All plans**: Include:
   - Current usage patterns
   - Requested limit increase
   - Use case justification

---

## Webhook Rate Limits

Webhook deliveries have separate limits:

| Plan | Deliveries/Minute | Concurrent Connections |
|------|-------------------|------------------------|
| Free | 30 | 5 |
| Pro | 100 | 10 |
| Enterprise | 500 | 50 |

Failed deliveries are retried with exponential backoff.

---

## Error Responses

### Rate Limit Exceeded

```json
{
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "Too many requests",
    "status": 429,
    "details": {
      "limit": 300,
      "reset_at": "2024-01-01T12:01:00Z",
      "retry_after": 45
    }
  }
}
```

### Burst Limit Exceeded

```json
{
  "error": {
    "code": "BURST_LIMIT_EXCEEDED",
    "message": "Too many concurrent requests",
    "status": 429,
    "details": {
      "burst_limit": 50,
      "retry_after": 5
    }
  }
}
```

---

## Related Documentation

- [Error Handling](/docs/api-reference/rest-api/errors) - Error codes
- [Authentication](/docs/api-reference/authentication) - API keys
- [Best Practices](/docs/wiki/best-practices/api-usage) - Usage guidelines
