Skip to main content

Overview

The TerraQuake API implements rate limiting to ensure fair usage and maintain service quality for all users. Rate limits are applied per IP address using a fixed window approach.

Rate Limit Configuration

Default Rate Limit: 100 requests per minute per IP address
The API uses a fixed window rate limiting algorithm with the following configuration:
  • Window Duration: 60 seconds (1 minute)
  • Maximum Requests: 100 requests per IP per window
  • Reset Behavior: The window resets every minute

Endpoint-Specific Limits

Different endpoints may have different rate limits:
Endpoint TypeWindowMax RequestsPurpose
General API (earthquakes, stations)1 minute100Standard endpoints
Authentication15 minutes50Prevent brute force attacks
Contact Form15 minutes20Prevent spam

Rate Limit Headers

Every API response includes rate limit headers to help you track your usage:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1709481234

Header Descriptions

X-RateLimit-Limit
integer
The maximum number of requests allowed in the current window (100)
X-RateLimit-Remaining
integer
The number of requests remaining in the current window
X-RateLimit-Reset
integer
Unix timestamp (in seconds) when the rate limit window resets
Retry-After
integer
Number of seconds to wait before retrying (only included in 429 responses)

Handling Rate Limit Errors

When you exceed the rate limit, the API returns a 429 Too Many Requests status code:

Error Response

{
  "success": false,
  "status": 429,
  "message": "Too many requests, please try again later.",
  "meta": {
    "timestamp": "2026-03-03T10:30:45.123Z"
  }
}

Response Headers

HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1709481046
Retry-After: 1

Best Practices

1

Monitor Rate Limit Headers

Always check the X-RateLimit-Remaining header to track your usage before making additional requests.
const response = await fetch('https://api.terraquakeapi.com/earthquakes/recent');
const remaining = response.headers.get('X-RateLimit-Remaining');

if (remaining < 10) {
  console.warn('Approaching rate limit!');
}
2

Implement Exponential Backoff

When you receive a 429 error, wait for the duration specified in the Retry-After header before retrying.
async function fetchWithRetry(url, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    const response = await fetch(url);
    
    if (response.status === 429) {
      const retryAfter = response.headers.get('Retry-After');
      const waitTime = retryAfter ? parseInt(retryAfter) * 1000 : 1000;
      
      console.log(`Rate limited. Waiting ${waitTime}ms...`);
      await new Promise(resolve => setTimeout(resolve, waitTime));
      continue;
    }
    
    return response;
  }
  
  throw new Error('Max retries exceeded');
}
3

Cache Responses

Cache API responses to reduce the number of requests. Earthquake data doesn’t change frequently, so caching for 5-10 minutes is reasonable.
const cache = new Map();
const CACHE_DURATION = 5 * 60 * 1000; // 5 minutes

async function getCachedEarthquakes() {
  const cached = cache.get('recent');
  
  if (cached && Date.now() - cached.timestamp < CACHE_DURATION) {
    return cached.data;
  }
  
  const response = await fetch('https://api.terraquakeapi.com/earthquakes/recent');
  const data = await response.json();
  
  cache.set('recent', { data, timestamp: Date.now() });
  return data;
}
4

Use Pagination Efficiently

Request only the data you need using the limit parameter. Don’t fetch all results if you only need a subset.
// Good: Only fetch what you need
const response = await fetch(
  'https://api.terraquakeapi.com/earthquakes/recent?limit=10'
);

// Avoid: Fetching more data than necessary
const response = await fetch(
  'https://api.terraquakeapi.com/earthquakes/recent?limit=100'
);
5

Batch Requests

If you need to make multiple requests, space them out over time instead of sending them all at once.
async function batchRequests(urls) {
  const results = [];
  
  for (const url of urls) {
    const response = await fetch(url);
    results.push(await response.json());
    
    // Wait 100ms between requests
    await new Promise(resolve => setTimeout(resolve, 100));
  }
  
  return results;
}

Code Examples

class TerraQuakeClient {
  constructor() {
    this.baseURL = 'https://api.terraquakeapi.com';
    this.rateLimit = { remaining: 100, reset: Date.now() };
  }

  async request(endpoint) {
    // Wait if rate limit is exhausted
    if (this.rateLimit.remaining === 0) {
      const waitTime = this.rateLimit.reset - Date.now();
      if (waitTime > 0) {
        await new Promise(resolve => setTimeout(resolve, waitTime));
      }
    }

    const response = await fetch(`${this.baseURL}${endpoint}`);
    
    // Update rate limit info from headers
    this.rateLimit.remaining = parseInt(
      response.headers.get('X-RateLimit-Remaining') || '0'
    );
    this.rateLimit.reset = parseInt(
      response.headers.get('X-RateLimit-Reset') || '0'
    ) * 1000;

    if (response.status === 429) {
      const retryAfter = response.headers.get('Retry-After');
      throw new Error(`Rate limited. Retry after ${retryAfter}s`);
    }

    return response.json();
  }
}

const client = new TerraQuakeClient();
const data = await client.request('/earthquakes/recent?limit=10');

Common Questions

You’ll receive a 429 Too Many Requests response with a Retry-After header indicating when you can retry. The request will not be processed.
Rate limits are applied per IP address. All requests from the same IP share the same rate limit.
For production applications requiring higher limits, please contact the TerraQuake team to discuss custom rate limit options.
Yes, the API uses a fixed window approach. Each window is exactly 1 minute, and the counter resets at the start of each new window.

Implementation Details

The rate limiter uses a fixed window algorithm with an in-memory HashMap structure:
// Internal structure: { ip: { count: number, windowStart: number } }
const buckets = new Map();

// Cleanup expired buckets to prevent memory leaks
setInterval(() => {
  const now = Date.now();
  for (const [ip, bucket] of buckets) {
    if (now - bucket.windowStart > WINDOW_MS) {
      buckets.delete(ip);
    }
  }
}, WINDOW_MS);
Source: /home/daytona/workspace/source/backend/src/middleware/rateLimiter.js:12-23

Build docs developers (and LLMs) love