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.
All error responses follow this standardized structure:
{
"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
Always false for error responses
HTTP status code (400, 404, 429, 500, 503, etc.)
Human-readable error description explaining what went wrong and how to fix it
Additional metadata about the error 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
Request succeeded. The response body contains the requested data.
Client Error Codes (4xx)
Invalid request parameters or malformed request. Common causes:
Invalid pagination parameters (page < 1, limit < 1)
Invalid date formats
Missing required parameters
Invalid parameter types
The requested resource does not exist. Common causes:
Invalid endpoint URL
Non-existent earthquake ID
Unavailable region
Rate limit exceeded. Solution: Wait for the duration specified in the Retry-After header before retrying.See Rate Limiting for more details.
Server Error Codes (5xx)
500 Internal Server Error
An unexpected error occurred on the server. What to do:
Retry the request after a brief delay
If the error persists, contact support
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
Request:
curl "https://api.terraquakeapi.com/earthquakes/recent?page=0&limit=-5"
Response:
{
"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:
# 101st request in 1 second
curl "https://api.terraquakeapi.com/earthquakes/recent"
Response:
{
"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 / 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:
curl "https://api.terraquakeapi.com/earthquakes/recent"
Response:
{
"success" : false ,
"status" : 500 ,
"message" : "HTTP error from the INGV source: 503 Service Unavailable" ,
"meta" : {
"timestamp" : "2026-03-03T10:30:45.123Z"
}
}
Request:
curl "https://api.terraquakeapi.com/earthquakes/date-range?startdate=invalid&enddate=2026-03-03"
Response:
{
"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:
curl "https://api.terraquakeapi.com/earthquakes/nonexistent-endpoint"
Response:
{
"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:
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
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 ;
}
}
Python
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 } " )
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 %d s... \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 %d s... \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
Always Check Response Status
Don’t assume requests succeed. Always check the HTTP status code or the success field in the response. const response = await fetch ( url );
if ( ! response . ok ) {
// Handle error
}
Implement Retry Logic
For server errors (5xx) and rate limits (429), implement retry logic with exponential backoff. 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 ;
}
}
}
Log Errors for Debugging
Always log error details including the timestamp, status code, and message for debugging. catch ( error ) {
console . error ( 'API Error:' , {
status: error . status ,
message: error . message ,
timestamp: error . meta ?. timestamp ,
url: url
});
}
Handle Rate Limits Gracefully
Use the Retry-After header to determine when to retry after hitting rate limits. See Rate Limiting for detailed examples.
Validate Input Before Sending
Validate parameters client-side before making requests to catch errors early. 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' );
}
Set Appropriate Timeouts
Set reasonable request timeouts to avoid hanging requests. 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:
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:
The HTTP status code
The timestamp in meta.timestamp
Your request parameters
If the issue persists, contact support with the error details.