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

# Rate Limits

> Understanding API quotas, rate limits, and plan tiers for the Exchange Rates API

## Rate Limit Overview

The Exchange Rates API uses a **monthly quota system** with different limits based on your subscription plan. Rate limits ensure fair usage and optimal performance for all users.

<Note>
  All rate limits are calculated based on your **billing cycle** and reset on your monthly renewal date. Exception: the Free tier's 300 requests are a one-time trial allowance and do not reset - upgrade to a paid plan for a recurring monthly quota.
</Note>

## Plan Comparison

<Card title="Plan Tiers">
  | Plan             | Price (AUD/month) | Monthly Requests | API Keys | Historical Data | Timeseries Max Range | Support        |
  | ---------------- | ----------------- | ---------------- | -------- | --------------- | -------------------- | -------------- |
  | **Free**         | \$0               | 300 (one-time)   | 1        | None            | No access            | Community      |
  | **Starter**      | \$29              | 5,000            | 1        | Last 30 days    | 7 days               | Email (48h)    |
  | **Professional** | \$79              | 50,000           | 5        | Full (2018+)    | 90 days              | Email (24h)    |
  | **Business**     | \$299             | 500,000          | 10       | Full (2018+)    | 365 days             | Phone (4h)     |
  | **Enterprise**   | Custom            | 5,000,000        | 20       | Full (2018+)    | 365 days             | Dedicated (1h) |
</Card>

> The Free tier's 300 requests are a one-time trial allowance (no monthly reset). Paid plans reset monthly on your billing date.

### Free Tier Details

The free tier is perfect for:

* Testing and development
* Personal projects
* Small applications with low traffic
* Proof of concept implementations

**Limitations:**

* 300 requests total (one-time trial allowance, does not reset monthly)
* No historical data access
* No timeseries endpoint access
* Single API key only
* Community support only
* No SLA guarantees

### Paid Plans Benefits

All paid plans include:

* Higher monthly quotas
* Historical data access
* Priority support
* 99.9% uptime SLA
* Advanced features (webhooks, CSV exports)\*

\*Some features coming soon

## API Keys per Plan

Free and Starter are **single-key plans**: your account holds one active API key at a time, and generating a new key replaces the existing one. Professional and above support multiple concurrent keys, which lets you issue a separate key per team, application, or environment and revoke any one of them independently.

<Note>
  On a single-key plan, generating a new key immediately invalidates the old one. Plan for a brief cutover, or upgrade to Professional if you need overlapping keys for zero-downtime rotation. See [Key Rotation](/authentication#key-rotation).
</Note>

## Webhook Limits

Webhooks are available on Professional and above. Each plan allows a maximum number of active webhook subscriptions:

| Plan             | Webhook Subscriptions |
| ---------------- | --------------------- |
| **Free**         | Not available         |
| **Starter**      | Not available         |
| **Professional** | 3                     |
| **Business**     | 10                    |
| **Enterprise**   | 50                    |

See the [Webhooks overview](/api-reference/webhooks/overview) for setup and payload details.

## Rate Limit Headers

Every API response includes rate limit information in the headers:

```bash theme={null}
curl -i https://api.exchangeratesapi.com.au/latest \
  -H "Authorization: Bearer your_api_key_here"
```

```http theme={null}
HTTP/2 200
X-RateLimit-Limit-Monthly: 5000
X-RateLimit-Remaining-Monthly: 4847
X-RateLimit-Reset: 2025-10-01T00:00:00Z
X-Request-Id: 123e4567-e89b-12d3-a456-426614174000
```

| Header                          | Description                                |
| ------------------------------- | ------------------------------------------ |
| `X-RateLimit-Limit-Monthly`     | Total monthly quota for your plan          |
| `X-RateLimit-Remaining-Monthly` | Remaining requests this month              |
| `X-RateLimit-Reset`             | When the quota resets (next billing cycle) |
| `X-Request-Id`                  | Unique identifier for debugging            |

## Free Endpoint Rate Limits

Some endpoints don't require authentication but have IP-based rate limits:

| Endpoint       | Limit     | Reset Period |
| -------------- | --------- | ------------ |
| `GET /status`  | Unlimited | -            |
| `GET /symbols` | Unlimited | -            |

## Rate Limit Exceeded Responses

### Monthly Quota Exceeded (Authenticated)

```json theme={null}
{
  "success": false,
  "error": {
    "code": 429,
    "type": "rate_limit_exceeded",
    "info": "Monthly quota of 5000 requests exceeded. Quota resets on your next billing cycle."
  }
}
```

### IP Rate Limit Exceeded (Free Endpoints)

```json theme={null}
{
  "success": false,
  "error": {
    "code": 429,
    "type": "rate_limit_exceeded",
    "info": "Rate limit exceeded. Try again in 3456 seconds."
  }
}
```

## Monitoring Your Usage

### Dashboard Analytics

Track your API usage in the [dashboard](https://app.exchangeratesapi.com.au):

* **Monthly Usage**: Requests made this billing cycle
* **Historical Usage**: 12-month usage chart
* **Top Endpoints**: Most frequently used endpoints
* **Response Times**: Average API performance
* **Error Rates**: Success vs error rates

## Rate Limit Best Practices

### 1. Implement Client-Side Throttling

<CodeGroup>
  ```javascript JavaScript theme={null}
  class ExchangeRatesAPI {
    constructor(apiKey) {
      this.apiKey = apiKey;
      this.baseURL = 'https://api.exchangeratesapi.com.au';
      this.remainingRequests = null;
      this.quotaResetTime = null;
    }
    
    async makeRequest(endpoint) {
      // Check if we have quota remaining
      if (this.remainingRequests !== null && this.remainingRequests <= 0) {
        const now = new Date();
        if (now < new Date(this.quotaResetTime)) {
          throw new Error('Monthly quota exceeded. Try again after your billing cycle resets.');
        }
      }
      
      const response = await fetch(`${this.baseURL}${endpoint}`, {
        headers: { 'Authorization': `Bearer ${this.apiKey}` }
      });
      
      // Update rate limit info from headers
      this.remainingRequests = parseInt(
        response.headers.get('X-RateLimit-Remaining-Monthly')
      );
      this.quotaResetTime = response.headers.get('X-RateLimit-Reset');
      
      if (!response.ok) {
        const error = await response.json();
        throw new Error(error.error.info);
      }
      
      return response.json();
    }
  }
  ```

  ```python Python theme={null}
  import requests
  import time
  from datetime import datetime

  class ExchangeRatesAPI:
      def __init__(self, api_key):
          self.api_key = api_key
          self.base_url = 'https://api.exchangeratesapi.com.au'
          self.remaining_requests = None
          self.quota_reset_time = None
          self.headers = {'Authorization': f'Bearer {api_key}'}
      
      def make_request(self, endpoint):
          # Check quota before making request
          if (self.remaining_requests is not None and 
              self.remaining_requests <= 0):
              if datetime.now() < datetime.fromisoformat(
                  self.quota_reset_time.replace('Z', '+00:00')
              ):
                  raise Exception('Monthly quota exceeded. Try again after your billing cycle resets.')
          
          response = requests.get(f'{self.base_url}{endpoint}', headers=self.headers)
          
          # Update rate limit info
          self.remaining_requests = int(
              response.headers.get('X-RateLimit-Remaining-Monthly', 0)
          )
          self.quota_reset_time = response.headers.get('X-RateLimit-Reset')
          
          if not response.ok:
              error_data = response.json()
              raise Exception(error_data['error']['info'])
              
          return response.json()
  ```
</CodeGroup>

### 2. Cache Responses Intelligently

RBA data updates once daily at 4 PM AEST, so cache aggressively to minimize monthly quota usage:

```javascript theme={null}
class CachedExchangeRates {
  constructor(apiKey) {
    this.api = new ExchangeRatesAPI(apiKey);
    this.cache = new Map();
    this.cacheExpiry = new Map();
  }
  
  async getLatestRates() {
    const cacheKey = 'latest';
    const now = Date.now();
    
    // Check if cached data is still fresh
    if (this.cache.has(cacheKey) && 
        this.cacheExpiry.get(cacheKey) > now) {
      return this.cache.get(cacheKey);
    }
    
    // Fetch fresh data
    const data = await this.api.makeRequest('/latest');
    
    // Cache until next RBA update (4 PM AEST next day)
    const tomorrow4PM = new Date();
    tomorrow4PM.setDate(tomorrow4PM.getDate() + 1);
    tomorrow4PM.setHours(16, 0, 0, 0); // 4 PM
    
    this.cache.set(cacheKey, data);
    this.cacheExpiry.set(cacheKey, tomorrow4PM.getTime());
    
    return data;
  }
}
```

### 3. Handle Rate Limit Errors Gracefully

```javascript theme={null}
async function robustApiCall(apiClient, endpoint) {
  try {
    return await apiClient.makeRequest(endpoint);
  } catch (error) {
    if (error.message.includes('quota exceeded')) {
      // Log the issue and return cached data if available
      console.warn('Monthly API quota exceeded, using cached data');
      return getCachedData(endpoint);
    }
    
    if (error.message.includes('rate limit')) {
      // Wait and retry for temporary rate limits
      await sleep(60000); // Wait 1 minute
      return apiClient.makeRequest(endpoint);
    }
    
    throw error; // Re-throw other errors
  }
}
```

### 4. Batch Requests Efficiently

Instead of multiple single-currency requests, use batch endpoints:

```javascript theme={null}
// Bad - Multiple requests
const usdRate = await api.makeRequest('/latest/USD');    // 1 request
const eurRate = await api.makeRequest('/latest/EUR');    // 1 request  
const gbpRate = await api.makeRequest('/latest/GBP');    // 1 request
// Total: 3 requests

// Good - Single batch request
const allRates = await api.makeRequest('/latest');       // 1 request
const usdRate = allRates.rates.USD;
const eurRate = allRates.rates.EUR;
const gbpRate = allRates.rates.GBP;
// Total: 1 request
```

## Upgrade Options

### When to Upgrade

Consider upgrading when you:

* Consistently hit monthly quotas
* Need historical data access
* Require faster support response
* Want SLA guarantees
* Need advanced features

### Upgrade Process

<Steps>
  <Step title="Choose Plan">
    Compare plans on our [pricing page](https://www.exchangeratesapi.com.au/pricing)
  </Step>

  <Step title="Upgrade in Dashboard">
    Visit your [dashboard](https://app.exchangeratesapi.com.au) and click "Upgrade"
  </Step>

  <Step title="Immediate Effect">
    New quotas take effect immediately after payment
  </Step>

  <Step title="No Code Changes">
    Your existing API keys continue working with higher limits
  </Step>
</Steps>
