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

# Error Handling

> Understand error responses and how to handle them in the TerraQuake API

## Overview

The TerraQuake API uses standard HTTP status codes and returns consistent error response formats to help you identify and handle issues effectively. All errors include descriptive messages and metadata to assist with debugging.

## Error Response Format

All error responses follow this standardized structure:

```json theme={null}
{
  "success": false,
  "status": 400,
  "message": "The limit parameter must be a positive integer greater than 0. Example: ?limit=50",
  "meta": {
    "timestamp": "2026-03-03T10:30:45.123Z"
  }
}
```

### Error Response Fields

**success** `boolean`

Always `false` for error responses

**status** `integer`

HTTP status code (400, 404, 429, 500, 503, etc.)

**message** `string`

Human-readable error description explaining what went wrong and how to fix it

**meta** `object`

Additional metadata about the error

**properties**

**timestamp** `string`

ISO 8601 timestamp of when the error occurred

## HTTP Status Codes

The API uses standard HTTP status codes to indicate the success or failure of requests:

### Success Codes

**200 OK** `success`

Request succeeded. The response body contains the requested data.

### Client Error Codes (4xx)

**400 Bad Request** `error`

Invalid request parameters or malformed request.

**Common causes:**

* Invalid pagination parameters (page \< 1, limit \< 1)
* Invalid date formats
* Missing required parameters
* Invalid parameter types

**404 Not Found** `error`

The requested resource does not exist.

**Common causes:**

* Invalid endpoint URL
* Non-existent earthquake ID
* Unavailable region

**429 Too Many Requests** `error`

Rate limit exceeded.

**Solution:** Wait for the duration specified in the `Retry-After` header before retrying.

See [Rate Limiting](https://docs.terraquakeapi.com/concepts/rate-limiting.md) for more details.

### Server Error Codes (5xx)

**500 Internal Server Error** `error`

An unexpected error occurred on the server.

**What to do:**

* Retry the request after a brief delay
* If the error persists, contact support

**503 Service Unavailable** `error`

The upstream data source (INGV API) is temporarily unavailable.

**What to do:**

* Retry the request after a few seconds
* Implement exponential backoff for retries

## Common Error Scenarios

### Invalid Pagination Parameters

**Request:**

```bash theme={null}
curl "https://api.terraquakeapi.com/earthquakes/recent?page=0&limit=-5"
```

**Response:**

```json theme={null}
{
  "success": false,
  "status": 400,
  "message": "The page parameter must be a positive integer greater than 0. Example: ?page=2",
  "meta": {
    "timestamp": "2026-03-03T10:30:45.123Z"
  }
}
```

### Rate Limit Exceeded

**Request:**

```bash theme={null}
# 101st request in 1 second
curl "https://api.terraquakeapi.com/earthquakes/recent"
```

**Response:**

```json theme={null}
{
  "success": false,
  "status": 429,
  "message": "Rate limit exceeded: max 100 requests per second. Please slow down boi, cache responses where possible, and retry after the reset window.",
  "meta": {
    "timestamp": "2026-03-03T10:30:45.123Z"
  }
}
```

**Response Headers:**

```http theme={null}
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1709481046
Retry-After: 1
```

### Upstream Service Error

**Request:**

```bash theme={null}
curl "https://api.terraquakeapi.com/earthquakes/recent"
```

**Response:**

```json theme={null}
{
  "success": false,
  "status": 500,
  "message": "HTTP error from the INGV source: 503 Service Unavailable",
  "meta": {
    "timestamp": "2026-03-03T10:30:45.123Z"
  }
}
```

### Invalid Date Format

**Request:**

```bash theme={null}
curl "https://api.terraquakeapi.com/earthquakes/date-range?startdate=invalid&enddate=2026-03-03"
```

**Response:**

```json theme={null}
{
  "success": false,
  "status": 400,
  "message": "Invalid date format. Use YYYY-MM-DD format. Example: ?startdate=2026-01-01",
  "meta": {
    "timestamp": "2026-03-03T10:30:45.123Z"
  }
}
```

### Not Found Error

**Request:**

```bash theme={null}
curl "https://api.terraquakeapi.com/earthquakes/nonexistent-endpoint"
```

**Response:**

```json theme={null}
{
  "success": false,
  "status": 404,
  "message": "Endpoint not found. Check the API documentation for available endpoints.",
  "meta": {
    "timestamp": "2026-03-03T10:30:45.123Z"
  }
}
```

## Error Handling Implementation

The API uses a centralized error handler utility for consistent error responses:

```javascript theme={null}
const handleHttpError = (
  res,
  message = 'Internal server error. Your request cannot be processed at this time',
  code = 500
) => {
  res.status(code).json({
    success: false,
    status: code,
    message,
    meta: {
      timestamp: new Date().toISOString()
    }
  })
}
```

Source: `/home/daytona/workspace/source/backend/src/utils/handleHttpError.js:5-19`

## Handling Errors in Your Code

### JavaScript/TypeScript

```javascript Fetch API theme={null}
async function getEarthquakes() {
  try {
    const response = await fetch(
      'https://api.terraquakeapi.com/earthquakes/recent?limit=50'
    );

    // Check if response is ok (status 200-299)
    if (!response.ok) {
      const error = await response.json();
      
      // Handle specific error codes
      switch (error.status) {
        case 400:
          console.error('Invalid request:', error.message);
          break;
        case 429:
          const retryAfter = response.headers.get('Retry-After');
          console.error(`Rate limited. Retry after ${retryAfter}s`);
          break;
        case 500:
        case 503:
          console.error('Server error. Retrying...');
          // Implement retry logic
          break;
        default:
          console.error('Unexpected error:', error.message);
      }
      
      throw new Error(error.message);
    }

    const data = await response.json();
    return data;
    
  } catch (error) {
    console.error('Request failed:', error);
    throw error;
  }
}
```

```javascript Axios theme={null}
import axios from 'axios';

const api = axios.create({
  baseURL: 'https://api.terraquakeapi.com',
  timeout: 10000,
});

// Add response interceptor for global error handling
api.interceptors.response.use(
  response => response,
  error => {
    if (error.response) {
      // Server responded with error status
      const { status, data } = error.response;
      
      switch (status) {
        case 400:
          console.error('Bad request:', data.message);
          break;
        case 429:
          const retryAfter = error.response.headers['retry-after'];
          console.error(`Rate limited. Retry after ${retryAfter}s`);
          break;
        case 500:
        case 503:
          console.error('Server error:', data.message);
          break;
        default:
          console.error('Error:', data.message);
      }
    } else if (error.request) {
      // Request made but no response received
      console.error('No response from server');
    } else {
      // Error setting up the request
      console.error('Request error:', error.message);
    }
    
    return Promise.reject(error);
  }
);

async function getEarthquakes() {
  try {
    const { data } = await api.get('/earthquakes/recent', {
      params: { limit: 50 }
    });
    return data;
  } catch (error) {
    // Error already logged by interceptor
    throw error;
  }
}
```

### Python

```python Requests theme={null}
import requests
import time

def get_earthquakes(max_retries=3):
    url = 'https://api.terraquakeapi.com/earthquakes/recent'
    params = {'limit': 50}
    
    for attempt in range(max_retries):
        try:
            response = requests.get(url, params=params, timeout=10)
            
            # Check for errors
            if response.status_code == 400:
                error = response.json()
                raise ValueError(f"Bad request: {error['message']}")
            
            elif response.status_code == 429:
                retry_after = int(response.headers.get('Retry-After', 1))
                print(f"Rate limited. Waiting {retry_after}s...")
                time.sleep(retry_after)
                continue
            
            elif response.status_code in [500, 503]:
                if attempt < max_retries - 1:
                    wait_time = 2 ** attempt  # Exponential backoff
                    print(f"Server error. Retrying in {wait_time}s...")
                    time.sleep(wait_time)
                    continue
                else:
                    error = response.json()
                    raise Exception(f"Server error: {error['message']}")
            
            elif response.status_code == 404:
                error = response.json()
                raise Exception(f"Not found: {error['message']}")
            
            # Raise for any other error status
            response.raise_for_status()
            
            return response.json()
            
        except requests.exceptions.Timeout:
            if attempt < max_retries - 1:
                print(f"Timeout. Retrying...")
                continue
            raise
        
        except requests.exceptions.ConnectionError:
            if attempt < max_retries - 1:
                print(f"Connection error. Retrying...")
                time.sleep(2 ** attempt)
                continue
            raise
    
    raise Exception("Max retries exceeded")

# Usage
try:
    data = get_earthquakes()
    print(f"Fetched {len(data['payload'])} earthquakes")
except Exception as e:
    print(f"Failed to fetch earthquakes: {e}")
```

### Go

```go theme={null}
package main

import (
    "encoding/json"
    "fmt"
    "io"
    "net/http"
    "strconv"
    "time"
)

type ErrorResponse struct {
    Success bool   `json:"success"`
    Status  int    `json:"status"`
    Message string `json:"message"`
    Meta    struct {
        Timestamp string `json:"timestamp"`
    } `json:"meta"`
}

func getEarthquakes(maxRetries int) (map[string]interface{}, error) {
    url := "https://api.terraquakeapi.com/earthquakes/recent?limit=50"
    
    for attempt := 0; attempt < maxRetries; attempt++ {
        resp, err := http.Get(url)
        if err != nil {
            if attempt < maxRetries-1 {
                time.Sleep(time.Duration(1<<attempt) * time.Second)
                continue
            }
            return nil, err
        }
        defer resp.Body.Close()
        
        // Handle different status codes
        switch resp.StatusCode {
        case 200:
            var data map[string]interface{}
            json.NewDecoder(resp.Body).Decode(&data)
            return data, nil
            
        case 400:
            var errResp ErrorResponse
            json.NewDecoder(resp.Body).Decode(&errResp)
            return nil, fmt.Errorf("bad request: %s", errResp.Message)
            
        case 429:
            retryAfter, _ := strconv.Atoi(resp.Header.Get("Retry-After"))
            if retryAfter == 0 {
                retryAfter = 1
            }
            fmt.Printf("Rate limited. Waiting %ds...\n", retryAfter)
            time.Sleep(time.Duration(retryAfter) * time.Second)
            continue
            
        case 500, 503:
            if attempt < maxRetries-1 {
                waitTime := 1 << attempt
                fmt.Printf("Server error. Retrying in %ds...\n", waitTime)
                time.Sleep(time.Duration(waitTime) * time.Second)
                continue
            }
            body, _ := io.ReadAll(resp.Body)
            var errResp ErrorResponse
            json.Unmarshal(body, &errResp)
            return nil, fmt.Errorf("server error: %s", errResp.Message)
            
        default:
            var errResp ErrorResponse
            json.NewDecoder(resp.Body).Decode(&errResp)
            return nil, fmt.Errorf("error %d: %s", errResp.Status, errResp.Message)
        }
    }
    
    return nil, fmt.Errorf("max retries exceeded")
}
```

## Best Practices

**1. Always Check Response Status**

Don't assume requests succeed. Always check the HTTP status code or the `success` field in the response.

```javascript theme={null}
const response = await fetch(url);
if (!response.ok) {
  // Handle error
}
```

**2. Implement Retry Logic**

For server errors (5xx) and rate limits (429), implement retry logic with exponential backoff.

```javascript theme={null}
async function fetchWithRetry(url, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      const response = await fetch(url);
      if (response.ok) return response.json();
      
      if (response.status >= 500) {
        // Exponential backoff: 1s, 2s, 4s
        await new Promise(r => setTimeout(r, 1000 * Math.pow(2, i)));
        continue;
      }
      
      throw new Error('Request failed');
    } catch (error) {
      if (i === maxRetries - 1) throw error;
    }
  }
}
```

**3. Log Errors for Debugging**

Always log error details including the timestamp, status code, and message for debugging.

```javascript theme={null}
catch (error) {
  console.error('API Error:', {
    status: error.status,
    message: error.message,
    timestamp: error.meta?.timestamp,
    url: url
  });
}
```

**4. Handle Rate Limits Gracefully**

Use the `Retry-After` header to determine when to retry after hitting rate limits.

See [Rate Limiting](https://docs.terraquakeapi.com/concepts/rate-limiting.md) for detailed examples.

**5. Validate Input Before Sending**

Validate parameters client-side before making requests to catch errors early.

```javascript theme={null}
function validateParams(page, limit) {
  if (page < 1) throw new Error('Page must be >= 1');
  if (limit < 1 || limit > 100) throw new Error('Limit must be 1-100');
}
```

**6. Set Appropriate Timeouts**

Set reasonable request timeouts to avoid hanging requests.

```javascript theme={null}
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 10000); // 10s

const response = await fetch(url, { signal: controller.signal });
clearTimeout(timeout);
```

## Error Monitoring

For production applications, implement error monitoring:

```javascript theme={null}
class APIErrorTracker {
  constructor() {
    this.errors = [];
  }

  track(error) {
    this.errors.push({
      status: error.status,
      message: error.message,
      timestamp: error.meta?.timestamp || new Date().toISOString(),
      url: error.url
    });

    // Send to monitoring service
    if (this.errors.length >= 10) {
      this.flush();
    }
  }

  flush() {
    // Send errors to your monitoring service (e.g., Sentry, DataDog)
    console.log('Flushing errors to monitoring service:', this.errors);
    this.errors = [];
  }
}

const errorTracker = new APIErrorTracker();

try {
  await fetch(url);
} catch (error) {
  errorTracker.track(error);
}
```

## Common Questions

> **What's the difference between 500 and 503 errors?**
>
> * **500 (Internal Server Error):** An unexpected error in the TerraQuake API itself
> * **503 (Service Unavailable):** The upstream INGV data source is temporarily unavailable
>
> Both should be retried with exponential backoff.

> **Should I retry on 4xx errors?**
>
> No. 4xx errors indicate client-side problems (invalid parameters, rate limits, etc.). Fix the request instead of retrying, except for 429 (rate limit) where you should wait and retry.

> **How long should I wait before retrying?**
>
> * **429 errors:** Use the `Retry-After` header value
> * **5xx errors:** Use exponential backoff (1s, 2s, 4s, etc.)
> * **Network errors:** Start with 1s and increase exponentially

> **What if the error message is unclear?**
>
> Error messages are designed to be descriptive. If you encounter an unclear error, check:
>
> 1. The HTTP status code
> 2. The timestamp in `meta.timestamp`
> 3. Your request parameters
>
> If the issue persists, contact support with the error details.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.