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

# Rate Limits

> Understand how Gale enforces rate limits and how to handle throttled requests.

# Rate Limits

Gale enforces rate limits to ensure consistent performance and prevent abuse.

## Current Limits

| API Key Type             | Requests per Minute |
| ------------------------ | ------------------- |
| Test keys (`glm_test_*`) | 100                 |
| Live keys (`glm_live_*`) | 1000                |

Limits are enforced per API key across all endpoints. All requests count toward your limit regardless of HTTP method (GET, POST, etc.).

## Rate Limit Headers

Every API response includes rate limit information:

```
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 995
X-RateLimit-Reset: 1698345600
```

* `X-RateLimit-Limit`: Total requests allowed per minute
* `X-RateLimit-Remaining`: Requests remaining in current window
* `X-RateLimit-Reset`: Unix timestamp when the limit resets

## Handling Rate Limits

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

```json theme={null}
{
  "error": {
    "code": "rate_limit_exceeded",
    "message": "Too many requests. Please try again in 30 seconds."
  }
}
```

Response includes:

```
HTTP/1.1 429 Too Many Requests
Retry-After: 30
```

The `Retry-After` header indicates seconds to wait before retrying.

## Best Practices

### 1. Implement Exponential Backoff

```javascript theme={null}
async function makeRequestWithRetry(url, options, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    const response = await fetch(url, options);

    if (response.status === 429) {
      const retryAfter = response.headers.get('Retry-After') || (2 ** i);
      await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
      continue;
    }

    return response;
  }

  throw new Error('Max retries exceeded');
}
```

### 2. Use Webhooks Instead of Polling

Don't poll for order status - use webhooks:

**Bad:**

```javascript theme={null}
// Polling every second
setInterval(async () => {
  const order = await getOrder(orderId);
}, 1000);
```

**Good:**

```javascript theme={null}
// Use webhooks
app.post('/webhooks/gale', (req, res) => {
  if (req.body.type === 'order.created') {
    handleOrder(req.body.data);
  }
});
```

### 3. Batch Operations

Use batch endpoints when available:

```javascript theme={null}
// Instead of multiple requests
for (const product of products) {
  await checkEligibility(product); // 100 requests
}

// Use batch endpoint
await batchCheckEligibility(products); // 1 request
```

### 4. Cache Results

Cache eligibility checks and product data:

```javascript theme={null}
const cache = new Map();

async function getProductEligibility(productId) {
  if (cache.has(productId)) {
    return cache.get(productId);
  }

  const result = await checkEligibility(productId);
  cache.set(productId, result);
  return result;
}
```

## Request Higher Limits

Need higher limits for your production use case?

Contact [support@withgale.com](mailto:support@withgale.com) with:

* Your use case description
* Expected request volume
* Current API key

## Related

* [Authentication](/developer-manual/authentication) - API key setup
* [Errors](/developer-manual/errors) - Error handling
