# API Rate Limiting Cheat Sheet: Headers, Patterns & Best Practices

**Quick Answer:** API rate limiting controls how many requests a client can make to an API within a specific time window (e.g., 100 requests per minute). Essential response headers include `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `Retry-After`. Implement exponential backoff when hitting 429 errors.

> Originally published at [apistatuscheck.com](https://apistatuscheck.com/blog/api-rate-limiting-cheat-sheet)

Rate limiting is the invisible traffic cop of the API world. Whether you're integrating with Stripe's payment APIs, GitHub's webhooks, or OpenAI's GPT models, understanding rate limits is non-negotiable for production applications.

## What is API Rate Limiting?

API rate limiting controls the number of requests a client can make within a specified time window. It serves multiple purposes:

- **Performance Protection:** Prevents resource monopolization
- **Cost Control:** Manages infrastructure expenses
- **Security Defense:** First line against DoS attacks
- **Fair Usage:** Ensures equitable access across customers
- **Data Integrity:** Prevents duplicate operations

## Common Rate Limit HTTP Headers

| Header | Description | Example |
|--------|-------------|----------|
| `X-RateLimit-Limit` | Max requests allowed | `5000` |
| `X-RateLimit-Remaining` | Requests remaining | `4273` |
| `X-RateLimit-Reset` | Unix timestamp for reset | `1738627200` |
| `Retry-After` | Seconds to wait (with 429) | `45` |

### Parsing Headers in JavaScript

```javascript
async function makeAPIRequest(url, options) {
  const response = await fetch(url, options);
  
  const limit = parseInt(response.headers.get('X-RateLimit-Limit'));
  const remaining = parseInt(response.headers.get('X-RateLimit-Remaining'));
  
  // Proactive throttling
  if (remaining < limit * 0.1) {
    console.warn('⚠️ Approaching rate limit');
    await sleep(1000);
  }
  
  if (response.status === 429) {
    const retryAfter = parseInt(response.headers.get('Retry-After') || '60');
    throw new RateLimitError(`Rate limited. Retry after ${retryAfter}s`);
  }
  
  return response.json();
}
```

## Rate Limits for Popular APIs

### Payment & Financial

| API | Free/Basic | Standard | Enterprise |
|-----|-----------|----------|------------|
| **Stripe** | 100 req/s (test)<br/>25 req/s (live) | 100 req/s | Custom (1000+) |
| **PayPal** | 50 req/s | 100 req/s | Custom |
| **Plaid** | 10 req/s | 50 req/s | Custom |

### Communication

| API | Limits | Notes |
|-----|--------|-------|
| **Twilio** | 1 msg/s (trial)<br/>100 msg/s (paid) | Per account SID |
| **SendGrid** | 100 emails/day (free)<br/>100 req/s (paid) | Plan-based |
| **Slack** | 1-100 req/min | Method-specific tiers |

### Developer Platforms

| API | Authenticated | Unauthenticated |
|-----|--------------|----------------|
| **GitHub** | 5,000 req/hour | 60 req/hour |
| **GitLab** | 2,000 req/min | 10 req/min |

### AI & ML

| API | Model | RPM | TPM |
|-----|-------|-----|-----|
| **OpenAI** | GPT-4 (Tier 1) | 500 | 30,000 |
| **OpenAI** | GPT-4 (Tier 5) | 10,000 | 300M |
| **Anthropic** | Claude (Pro) | 1,000 | Varies |

### E-commerce

| API | Limits |
|-----|--------|
| **Shopify** | 2 req/s (REST)<br/>1000 points/s (GraphQL) |
| **Amazon SP-API** | 1-200 req/s (varies) |

### Cloud Infrastructure

| Provider | Service | Limit |
|----------|---------|-------|
| **AWS** | API Gateway | 10,000 req/s |
| **AWS** | Lambda | 1,000 concurrent |
| **Google Cloud** | Cloud Functions | 1,000 req/s |

## Implementation Patterns

### Token Bucket

Most popular algorithm—allows controlled bursts while preventing abuse.

```python
import time

class TokenBucket:
    def __init__(self, capacity, refill_rate):
        self.capacity = capacity
        self.tokens = capacity
        self.refill_rate = refill_rate
        self.last_refill = time.time()
    
    def consume(self, tokens=1):
        self._refill()
        if self.tokens >= tokens:
            self.tokens -= tokens
            return True
        return False
    
    def _refill(self):
        now = time.time()
        elapsed = now - self.last_refill
        tokens_to_add = elapsed * self.refill_rate
        self.tokens = min(self.capacity, self.tokens + tokens_to_add)
        self.last_refill = now
```

### Fixed Window

Simplest implementation—fixed quota per time window.

**Pros:** Simple, predictable
**Cons:** Burst vulnerability at window boundaries

### Sliding Window

Considers past N time units from current moment.

**Pros:** Prevents burst exploitation
**Cons:** Higher memory usage, more complex

## Handling Rate Limits in Code

### Exponential Backoff with Jitter

```javascript
async function exponentialBackoff(fn, maxRetries = 5) {
  let retries = 0;
  
  while (retries < maxRetries) {
    try {
      return await fn();
    } catch (error) {
      if (error.status === 429) {
        retries++;
        if (retries >= maxRetries) throw new Error('Max retries exceeded');
        
        const baseDelay = Math.min(1000 * Math.pow(2, retries), 32000);
        const jitter = Math.random() * 1000;
        await sleep(baseDelay + jitter);
      } else {
        throw error;
      }
    }
  }
}
```

### Request Queuing

```javascript
class RateLimitedQueue {
  constructor(requestsPerSecond) {
    this.queue = [];
    this.requestsPerSecond = requestsPerSecond;
    this.delayBetweenRequests = 1000 / requestsPerSecond;
  }
  
  async enqueue(fn) {
    return new Promise((resolve, reject) => {
      this.queue.push({ fn, resolve, reject });
      this.processQueue();
    });
  }
  
  async processQueue() {
    if (this.processing || this.queue.length === 0) return;
    this.processing = true;
    
    while (this.queue.length > 0) {
      const { fn, resolve, reject } = this.queue.shift();
      try {
        const result = await fn();
        resolve(result);
      } catch (error) {
        reject(error);
      }
      if (this.queue.length > 0) {
        await sleep(this.delayBetweenRequests);
      }
    }
    this.processing = false;
  }
}
```

## Monitoring Rate Limits

Proactive monitoring prevents surprises:

- Track rate limit headers from every request
- Alert at 80% consumption
- Monitor 429 error rates
- Use external monitoring services

[API Status Check](https://apistatuscheck.com) monitors 100+ APIs with real-time alerts.

## FAQs

**Q: Rate limiting vs throttling?**
A: Rate limiting enforces hard limits (429 errors). Throttling slows requests gradually.

**Q: Request a limit increase?**
A: Document use case, show optimization efforts, contact support/sales.

**Q: Implement my own rate limiting?**
A: Yes! Even internal APIs benefit from rate limiting.

**Q: Webhooks during rate limits?**
A: Separate limits apply. Providers retry with exponential backoff.

**Q: Risk of bans?**
A: Yes—persistent violations may result in IP bans or account suspension.

**Q: Best algorithm?**
A: Token bucket for most applications—allows bursts while preventing abuse.

**Q: Concurrent vs rate limits?**
A: Rate limits = requests/time. Concurrent = simultaneous in-flight requests.

**Q: Test rate limit handling?**
A: Mock 429 responses, use test endpoints, load testing tools (k6, Artillery).

---

**Read the full article with all code examples at:** [apistatuscheck.com/blog/api-rate-limiting-cheat-sheet](https://apistatuscheck.com/blog/api-rate-limiting-cheat-sheet)

**Monitor your APIs:** [API Status Check](https://apistatuscheck.com) provides real-time monitoring and instant alerts for 100+ popular APIs.
