Error Handling

The emissions.dev API uses conventional HTTP response codes to indicate the success or failure of an API request.

HTTP Status Codes

Code Meaning
200 Success
400 Bad Request - Missing required parameters
401 Unauthorized - Invalid or missing API key
403 Forbidden - API key doesn't have access
404 Not Found - Endpoint doesn't exist
422 Validation Error - Parameters failed validation
429 Rate Limited - Too many requests
500 Server Error - Something went wrong on our end

Error Response Format

All errors return a consistent JSON structure:

{
  "error": {
    "code": "validation_error",
    "message": "Country code 'XX' not recognised.",
    "field": "origin_country",
    "status": 422
  }
}
Field Description
code Machine-readable error code
message Human-readable description
field The parameter that caused the error (if applicable)
status HTTP status code

Common Error Codes

unauthorized

{
  "error": {
    "code": "unauthorized",
    "message": "Invalid API key. Get your key at https://emissions.dev/dashboard",
    "status": 401
  }
}

Cause: Missing or invalid API key.

validation_error

{
  "error": {
    "code": "validation_error",
    "message": "The weight field is required.",
    "field": "weight",
    "status": 422
  }
}

Cause: Required parameter missing or invalid value.

rate_limited

{
  "error": {
    "message": "Rate limit exceeded. Try again in 1 seconds.",
    "status": 429
  }
}

Cause: Too many requests per second, or monthly quota exceeded. See Rate Limits for details on limits by plan and usage warning headers.

Handling Errors in Code

JavaScript

try {
  const result = await fetch('/api/v1/freight/emissions?...');
  const data = await result.json();
  
  if (!result.ok) {
    throw new Error(data.error.message);
  }
  
  console.log('Emissions:', data.data.attributes.emissions.co2e);
} catch (error) {
  console.error('API Error:', error.message);
}

Python

import requests

try:
    response = requests.get('https://api.emissions.dev/v1/freight/emissions', params={...})
    response.raise_for_status()
    data = response.json()
except requests.exceptions.HTTPError as e:
    error = e.response.json()
    print(f"Error: {error['error']['message']}")

PHP

try {
    $response = Http::withToken($apiKey)
        ->get('https://api.emissions.dev/v1/freight/emissions', [...]);
    
    if ($response->failed()) {
        $error = $response->json('error');
        throw new Exception($error['message']);
    }
    
    $data = $response->json();
} catch (Exception $e) {
    Log::error('API Error: ' . $e->getMessage());
}

Retry Strategy

For transient errors (5xx, rate limits), implement exponential backoff:

async function fetchWithRetry(url, options, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      const response = await fetch(url, options);
      if (response.ok) return response;
      
      if (response.status === 429 || response.status >= 500) {
        const delay = Math.pow(2, attempt) * 1000; // 1s, 2s, 4s
        await new Promise(r => setTimeout(r, delay));
        continue;
      }
      
      throw new Error(`HTTP ${response.status}`);
    } catch (error) {
      if (attempt === maxRetries - 1) throw error;
    }
  }
}