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

# Rate Limits

> API rate limits by plan tier

## Overview

Rate limits protect the API and ensure fair usage across all clients. Limits vary by plan tier and are designed to accommodate everything from development to high-scale production. For a full breakdown of plan tiers, rate limits per plan, and pricing details, see [Plans & Pricing](/documentation/plans-limits/plans-tiers).

***

## Rate Limit Headers

API responses include headers to help you track your rate limit status:

```
x-ratelimit-limit: 100
x-ratelimit-remaining: 95
x-ratelimit-reset: 1698765432
```

| Header                  | Description                               |
| ----------------------- | ----------------------------------------- |
| `x-ratelimit-limit`     | Maximum requests per second for your plan |
| `x-ratelimit-remaining` | Requests remaining in current window      |
| `x-ratelimit-reset`     | Unix timestamp when the limit resets      |

***

## Handling Rate Limits

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

```json theme={null}
{
  "statusCode": 429,
  "error": "Too Many Requests",
  "message": "Rate limit exceeded",
  "retryAfter": 30
}
```

### Retry Strategy

Implement exponential backoff when encountering rate limits:

```typescript theme={null}
async function fetchWithRetry(
  url: string, 
  options: RequestInit, 
  maxRetries = 3
): Promise<Response> {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const response = await fetch(url, options);
    
    if (response.status === 429) {
      const retryAfter = response.headers.get('Retry-After') || '5';
      const waitTime = parseInt(retryAfter) * 1000 * Math.pow(2, attempt);
      
      console.log(`Rate limited. Retrying in ${waitTime / 1000}s...`);
      await new Promise(r => setTimeout(r, waitTime));
      continue;
    }
    
    return response;
  }
  
  throw new Error('Max retries exceeded');
}
```

***

## Best Practices

<CardGroup cols={2}>
  <Card title="Cache responses" icon="database">
    Cache yield metadata, validators, and other stable data to reduce API calls
  </Card>

  <Card title="Batch requests" icon="layer-group">
    Use aggregate endpoints like `/yields/balances` instead of individual calls
  </Card>

  <Card title="Implement backoff" icon="clock">
    Use exponential backoff for retries to avoid cascading failures
  </Card>

  <Card title="Monitor usage" icon="chart-line">
    Track your rate limit usage via response headers
  </Card>
</CardGroup>

***

## Optimizing API Usage

<AccordionGroup>
  <Accordion title="Cache yield metadata">
    Yield metadata (APY, TVL, validators) changes infrequently. Cache for 5–15 minutes to reduce calls:

    ```typescript theme={null}
    const CACHE_TTL = 5 * 60 * 1000; // 5 minutes
    const cache = new Map();

    async function getYields() {
      const cached = cache.get('yields');
      if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
        return cached.data;
      }
      
      const response = await fetch('https://api.yield.xyz/v1/yields');
      const data = await response.json();
      cache.set('yields', { data, timestamp: Date.now() });
      return data;
    }
    ```
  </Accordion>

  <Accordion title="Use aggregate endpoints">
    Instead of calling `/yields/{yieldId}/balances` for each yield, use the aggregate endpoint:

    ```bash theme={null}
    # Instead of multiple calls:
    # GET /yields/eth-lido-staking/balances
    # GET /yields/eth-rocketpool-staking/balances

    # Use aggregate:
    POST /yields/balances
    {
      "addresses": ["0x..."],
      "yieldIds": ["eth-lido-staking", "eth-rocketpool-staking"]
    }
    ```
  </Accordion>

  <Accordion title="Implement request queuing">
    Queue requests to stay within rate limits during high-traffic periods:

    ```typescript theme={null}
    class RequestQueue {
      private queue: (() => Promise<any>)[] = [];
      private processing = false;
      private requestsPerSecond = 100;
      
      async add<T>(fn: () => Promise<T>): Promise<T> {
        return new Promise((resolve, reject) => {
          this.queue.push(async () => {
            try {
              resolve(await fn());
            } catch (e) {
              reject(e);
            }
          });
          this.process();
        });
      }
      
      private async process() {
        if (this.processing) return;
        this.processing = true;
        
        while (this.queue.length > 0) {
          const fn = this.queue.shift()!;
          await fn();
          await new Promise(r => 
            setTimeout(r, 1000 / this.requestsPerSecond)
          );
        }
        
        this.processing = false;
      }
    }
    ```
  </Accordion>
</AccordionGroup>

***

## Upgrade Your Plan

Need higher rate limits?

<CardGroup cols={2}>
  <Card title="Upgrade to Pro" icon="arrow-up" href="mailto:hello@yield.xyz">
    1,000+ requests/second for high-volume apps
  </Card>

  <Card title="Enterprise Inquiry" icon="building" href="mailto:hello@yield.xyz">
    Custom rate limits for institutional scale
  </Card>
</CardGroup>

***

## Next Steps

<CardGroup cols={2}>
  <Card title="Plans & Pricing" icon="layer-group" href="/documentation/plans-limits/plans-tiers">
    Compare all plan features
  </Card>

  <Card title="Compute Units" icon="calculator" href="/documentation/plans-limits/compute-unit-pricing">
    CU pricing and endpoint costs
  </Card>
</CardGroup>
