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

# Rate Limits

> Understanding and handling API rate limits

## Default Limits

| Endpoint Category   | Limit       | Window  |
| ------------------- | ----------- | ------- |
| Order Creation      | 50 req/sec  | Rolling |
| Tracking Queries    | 100 req/sec | Rolling |
| Label Operations    | 20 req/sec  | Rolling |
| Manifest Submission | 10 req/sec  | Rolling |

Rate limits are applied per API key.

## Rate Limit Headers

Every response includes rate limit information:

```
X-RateLimit-Limit: 50
X-RateLimit-Remaining: 47
X-RateLimit-Reset: 1703155200
```

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

## Handling Rate Limits

### Check Before Hitting Limits

```javascript theme={null}
class RateLimitedClient {
  constructor(apiKey) {
    this.apiKey = apiKey;
    this.remaining = 50;
    this.resetAt = 0;
  }

  async request(url, options) {
    // Wait if we're out of quota
    if (this.remaining <= 0 && Date.now() < this.resetAt) {
      const delay = this.resetAt - Date.now();
      await sleep(delay);
    }

    const response = await fetch(url, {
      ...options,
      headers: {
        ...options.headers,
        'X-API-Key': this.apiKey
      }
    });

    // Update rate limit state
    this.remaining = parseInt(response.headers.get('X-RateLimit-Remaining'));
    this.resetAt = parseInt(response.headers.get('X-RateLimit-Reset')) * 1000;

    return response;
  }
}
```

### Handle 429 Responses

```javascript theme={null}
async function requestWithRetry(url, options, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const response = await fetch(url, options);
    
    if (response.status === 429) {
      const retryAfter = response.headers.get('Retry-After') || 
                         response.headers.get('X-RateLimit-Reset');
      const delay = retryAfter 
        ? parseInt(retryAfter) * 1000 
        : Math.pow(2, attempt) * 1000;
      
      console.log(`Rate limited. Waiting ${delay}ms...`);
      await sleep(delay);
      continue;
    }
    
    return response;
  }
  
  throw new Error('Max retries exceeded');
}
```

## Batch Operations

Reduce request count with batch endpoints:

### Instead of N tracking requests:

```javascript theme={null}
// ❌ Bad: N requests for N parcels
for (const trackingNumber of trackingNumbers) {
  await fetch(`/api/v1/tracking/${trackingNumber}`);
}
```

### Use batch endpoint:

```javascript theme={null}
// ✅ Good: 1 request for N parcels
await fetch('/api/v1/tracking/batch', {
  method: 'POST',
  body: JSON.stringify({ trackingNumbers })
});
```

## Higher Limits

Need higher rate limits? Contact us:

| Plan         | Order Creation | Tracking | Support   |
| ------------ | -------------- | -------- | --------- |
| Starter      | 50/sec         | 100/sec  | Email     |
| Professional | 200/sec        | 500/sec  | Priority  |
| Enterprise   | Custom         | Custom   | Dedicated |

<Card title="Request Higher Limits" icon="rocket" href="mailto:api-support@crbtrack.com">
  Contact us to discuss your volume requirements
</Card>

## Best Practices

<AccordionGroup>
  <Accordion title="Distribute Requests">
    Spread requests evenly rather than bursting. Use a queue or rate limiter on your side.
  </Accordion>

  <Accordion title="Cache Responses">
    Cache tracking results for 5-10 minutes. Status changes aren't instant.
  </Accordion>

  <Accordion title="Use Webhooks">
    For status updates, webhooks are more efficient than polling.
  </Accordion>

  <Accordion title="Off-Peak Processing">
    Schedule bulk operations during off-peak hours (2-6 AM EST).
  </Accordion>
</AccordionGroup>
