Rate Limits

Understanding and handling API rate limits

Rate limits protect the API from abuse and ensure fair usage for all users.

Current Limits

Limit TypeValueScope
Requests per minute (RPM)60Per API key
Requests per day (RPD)1,000Per API key

Rate Limit Headers

All API responses include rate limit information:

X-RateLimit-Limit: 60
X-RateLimit-Remaining: 45
X-RateLimit-Reset: 1699999999
HeaderDescription
X-RateLimit-LimitMaximum requests allowed in the window
X-RateLimit-RemainingRequests remaining in current window
X-RateLimit-ResetUnix timestamp when the limit resets

Rate Limit Errors

When you exceed the rate limit:

HTTP Status: 429 Too Many Requests

{
  "error": "Rate Limit Exceeded",
  "message": "Too many requests per minute"
}

Additional header when rate limited:

Retry-After: 45

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

Handling Rate Limits

Basic Retry Logic

async function fetchWithRateLimit(url: string, options: RequestInit) {
  const response = await fetch(url, options);

  if (response.status === 429) {
    const retryAfter = parseInt(
      response.headers.get('Retry-After') || '60'
    );

    console.log(`Rate limited. Waiting ${retryAfter} seconds...`);
    await sleep(retryAfter * 1000);

    // Retry the request
    return fetch(url, options);
  }

  return response;
}

function sleep(ms: number) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

Exponential Backoff

For production applications, use exponential backoff:

async function fetchWithBackoff(
  url: string,
  options: RequestInit,
  maxRetries = 5
) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const response = await fetch(url, options);

    if (response.status !== 429) {
      return response;
    }

    // Get retry delay
    const retryAfter = response.headers.get('Retry-After');
    const baseDelay = retryAfter
      ? parseInt(retryAfter) * 1000
      : Math.pow(2, attempt) * 1000;

    // Add jitter to prevent thundering herd
    const jitter = Math.random() * 1000;
    const delay = baseDelay + jitter;

    console.log(`Rate limited. Retry ${attempt + 1}/${maxRetries} in ${delay}ms`);
    await sleep(delay);
  }

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

Request Queue

For high-throughput applications, implement a queue:

class RequestQueue {
  private queue: Array<() => Promise<void>> = [];
  private processing = false;
  private requestsThisMinute = 0;
  private minuteStart = Date.now();

  async add<T>(fn: () => Promise<T>): Promise<T> {
    return new Promise((resolve, reject) => {
      this.queue.push(async () => {
        try {
          const result = await fn();
          resolve(result);
        } catch (error) {
          reject(error);
        }
      });
      this.process();
    });
  }

  private async process() {
    if (this.processing) return;
    this.processing = true;

    while (this.queue.length > 0) {
      // Reset counter every minute
      if (Date.now() - this.minuteStart > 60000) {
        this.requestsThisMinute = 0;
        this.minuteStart = Date.now();
      }

      // Wait if at limit
      if (this.requestsThisMinute >= 60) {
        const waitTime = 60000 - (Date.now() - this.minuteStart);
        await sleep(waitTime);
        continue;
      }

      const task = this.queue.shift();
      if (task) {
        this.requestsThisMinute++;
        await task();
      }
    }

    this.processing = false;
  }
}

// Usage
const queue = new RequestQueue();

// These requests will be automatically queued and rate-limited
await queue.add(() => verifyFact('Fact 1'));
await queue.add(() => verifyFact('Fact 2'));

Best Practices

1. Check Headers Proactively

Monitor X-RateLimit-Remaining and slow down before hitting limits:

function checkRateLimit(response: Response) {
  const remaining = parseInt(
    response.headers.get('X-RateLimit-Remaining') || '60'
  );

  if (remaining < 10) {
    console.warn(`Low rate limit: ${remaining} requests remaining`);
  }

  if (remaining < 5) {
    // Slow down requests
    return { shouldDelay: true, delay: 5000 };
  }

  return { shouldDelay: false };
}

2. Cache Results

Don't verify the same fact twice:

const cache = new Map<string, VerifyResult>();

async function verifyFactCached(fact: string) {
  const cacheKey = fact.toLowerCase().trim();

  if (cache.has(cacheKey)) {
    return cache.get(cacheKey);
  }

  const result = await verifyFact(fact);
  cache.set(cacheKey, result);

  return result;
}

3. Batch When Possible

If verifying multiple facts, consider combining them:

// Instead of:
await verifyFact('The sky is blue');
await verifyFact('Water is wet');

// Combine into one statement:
await verifyFact('The sky is blue and water is wet');

4. Use Multiple API Keys

For different use cases, create separate API keys:

  • Development key (lower usage)
  • Production key (higher limits)
  • Testing key (isolated limits)

Each key has its own rate limit quota.

Need Higher Limits?

If you consistently need higher rate limits:

  1. Contact us at support@mira.network
  2. Describe your use case
  3. We can discuss custom rate limit tiers

Enterprise plans include higher limits and dedicated support.