Error Handling
Understanding and handling API errors effectively
Overview
The SafePays API uses standard HTTP status codes to indicate the success or failure of API requests. Error responses include a descriptive message to help you identify and fix issues.
Error Response Format
All error responses follow this consistent format:
{
"error": "Descriptive error message"
}HTTP Status Codes
Success Codes (2xx)
| Code | Name | Description |
|---|---|---|
| 200 | OK | Request successful |
| 201 | Created | Resource created successfully |
Client Error Codes (4xx)
| Code | Name | Description |
|---|---|---|
| 400 | Bad Request | Invalid request parameters or missing required fields |
| 401 | Unauthorized | Invalid or missing API key |
| 404 | Not Found | Resource not found |
| 409 | Conflict | Resource already exists (e.g., duplicate email) |
| 429 | Too Many Requests | Rate limit exceeded |
Server Error Codes (5xx)
| Code | Name | Description |
|---|---|---|
| 500 | Internal Server Error | Server error - retry or contact support |
| 502 | Bad Gateway | Temporary server issue - retry |
| 503 | Service Unavailable | Service temporarily unavailable |
| 504 | Gateway Timeout | Request timeout - retry |
Common Error Messages
Authentication Errors
Invalid API Key
{
"error": "Invalid API Key"
}- Cause: API key is incorrect or has been revoked
- Solution: Verify your API key in the dashboard
Validation Errors
Missing Required Fields
{
"error": "name and email are required"
}- Cause: Required fields not provided in request
- Solution: Include all required fields in your request
Invalid Email Format
{
"error": "Invalid email format"
}- Cause: Email doesn't match valid email pattern
- Solution: Provide a valid email address
Resource Errors
Customer Not Found
{
"error": "Customer not found"
}- Cause: Customer ID doesn't exist
- Solution: Verify customer ID or create customer first
Duplicate Customer
{
"error": "Customer with this email already exists"
}- Cause: Email address already registered
- Solution: Use existing customer or different email
Error Handling Best Practices
1. Implement Retry Logic
For transient errors (5xx, 429), implement exponential backoff:
async function apiCallWithRetry(url, options, maxRetries = 3) {
let lastError;
for (let i = 0; i < maxRetries; i++) {
try {
const response = await fetch(url, options);
if (response.ok) {
return await response.json();
}
// Don't retry client errors (4xx) except rate limits
if (response.status >= 400 && response.status < 500 && response.status !== 429) {
const error = await response.json();
throw new Error(`API Error: ${error.error}`);
}
// Retry server errors and rate limits
lastError = new Error(`HTTP ${response.status}`);
// Exponential backoff
const delay = Math.min(1000 * Math.pow(2, i), 10000);
await new Promise(resolve => setTimeout(resolve, delay));
} catch (error) {
lastError = error;
// Network errors - retry
if (error.name === 'TypeError') {
const delay = Math.min(1000 * Math.pow(2, i), 10000);
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
throw error;
}
}
throw lastError;
}
// Usage
try {
const result = await apiCallWithRetry(
'https://app.safepays.com/api/customer',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
}
);
console.log('Success:', result);
} catch (error) {
console.error('Failed after retries:', error);
}import time
import requests
from typing import Optional, Dict, Any
def api_call_with_retry(
url: str,
method: str = 'GET',
data: Optional[Dict[str, Any]] = None,
max_retries: int = 3
) -> Dict[str, Any]:
last_error = None
for attempt in range(max_retries):
try:
if method == 'POST':
response = requests.post(url, json=data)
else:
response = requests.get(url, params=data)
# Success
if response.status_code in range(200, 300):
return response.json()
# Don't retry client errors (except rate limits)
if 400 <= response.status_code < 500 and response.status_code != 429:
error_data = response.json()
raise Exception(f"API Error: {error_data.get('error', 'Unknown error')}")
# Retry server errors and rate limits
last_error = f"HTTP {response.status_code}"
# Exponential backoff
delay = min(2 ** attempt, 10)
time.sleep(delay)
except requests.exceptions.RequestException as e:
last_error = str(e)
# Network error - retry with backoff
delay = min(2 ** attempt, 10)
time.sleep(delay)
raise Exception(f"Failed after {max_retries} retries: {last_error}")
# Usage
try:
result = api_call_with_retry(
'https://app.safepays.com/api/customer',
method='POST',
data={
'api_key': 'your_api_key',
'name': 'John Doe',
'email': 'john@example.com'
}
)
print('Success:', result)
except Exception as e:
print('Failed:', e)<?php
function apiCallWithRetry($url, $method = 'GET', $data = null, $maxRetries = 3) {
$lastError = null;
for ($attempt = 0; $attempt < $maxRetries; $attempt++) {
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
if ($method === 'POST') {
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
}
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curlError = curl_error($ch);
curl_close($ch);
// Network error
if ($response === false) {
$lastError = "Network error: $curlError";
sleep(min(pow(2, $attempt), 10));
continue;
}
// Success
if ($httpCode >= 200 && $httpCode < 300) {
return json_decode($response, true);
}
// Don't retry client errors (except rate limits)
if ($httpCode >= 400 && $httpCode < 500 && $httpCode !== 429) {
$error = json_decode($response, true);
throw new Exception("API Error: " . ($error['error'] ?? 'Unknown error'));
}
// Retry server errors and rate limits
$lastError = "HTTP $httpCode";
sleep(min(pow(2, $attempt), 10));
}
throw new Exception("Failed after $maxRetries retries: $lastError");
}
// Usage
try {
$result = apiCallWithRetry(
'https://app.safepays.com/api/customer',
'POST',
[
'api_key' => 'your_api_key',
'name' => 'John Doe',
'email' => 'john@example.com'
]
);
echo "Success: " . json_encode($result);
} catch (Exception $e) {
echo "Failed: " . $e->getMessage();
}
?>2. Log Errors for Debugging
Always log error responses for troubleshooting:
function logApiError(endpoint, error, context) {
console.error('API Error', {
timestamp: new Date().toISOString(),
endpoint,
error,
context,
// Include request ID if available
requestId: error.headers?.['x-request-id']
});
// Send to error tracking service
if (typeof Sentry !== 'undefined') {
Sentry.captureException(new Error(`API Error: ${error.message}`), {
extra: { endpoint, context }
});
}
}3. User-Friendly Error Messages
Map API errors to user-friendly messages:
const ERROR_MESSAGES = {
'Invalid API Key': 'Authentication failed. Please check your configuration.',
'Customer not found': 'Customer record not found. Please verify the customer ID.',
'Customer with this email already exists': 'A customer with this email already exists.',
'webhook parameter is required': 'Webhook URL is required for invoice creation.',
'Rate limit exceeded': 'Too many requests. Please try again later.',
// Default
'default': 'An error occurred. Please try again or contact support.'
};
function getUserMessage(apiError) {
return ERROR_MESSAGES[apiError] || ERROR_MESSAGES.default;
}4. Handle Network Timeouts
Set appropriate timeouts for API calls:
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 10000); // 10 second timeout
try {
const response = await fetch(url, {
...options,
signal: controller.signal
});
clearTimeout(timeout);
// Process response
} catch (error) {
if (error.name === 'AbortError') {
console.error('Request timeout');
// Handle timeout
}
}Rate Limiting
Rate limits help ensure API stability and fair usage for all users.
Current Limits
- 1000 requests per minute
- 10,000 requests per hour
Rate Limit Headers
Response headers indicate your current rate limit status:
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 950
X-RateLimit-Reset: 1642089600Handling Rate Limits
When rate limited, you'll receive a 429 response:
{
"error": "Rate limit exceeded. Please retry after 60 seconds."
}Best practices:
- Implement exponential backoff
- Cache responses when possible
- Batch operations where applicable
- Monitor rate limit headers
Debugging Tips
1. Check Request Format
Common issues:
- Missing Content-Type header
- Invalid JSON syntax
- Wrong endpoint URL
- Missing required fields
2. Verify API Key
- Ensure no extra spaces or characters
- Check if key is for correct environment (test/production)
- Verify key hasn't been revoked
3. Use Request IDs
Include a unique request ID for tracking:
const requestId = generateUUID();
const response = await fetch(url, {
headers: {
'Content-Type': 'application/json',
'X-Request-ID': requestId
},
// ...
});4. Test with cURL
Test basic connectivity:
curl -X POST https://app.safepays.com/api/customer \
-H "Content-Type: application/json" \
-d '{"api_key":"test","name":"Test","email":"test@example.com"}' \
-vError Recovery Strategies
Idempotency
Make operations idempotent to safely retry:
// Generate idempotency key
const idempotencyKey = generateIdempotencyKey(customerId, invoiceData);
// Include in request
const response = await fetch(url, {
headers: {
'Content-Type': 'application/json',
'Idempotency-Key': idempotencyKey
},
// ...
});Graceful Degradation
Handle API failures without breaking your application:
async function createInvoiceWithFallback(invoiceData) {
try {
// Try API call
return await createInvoice(invoiceData);
} catch (error) {
// Log error
console.error('Invoice creation failed:', error);
// Queue for retry
await queueInvoiceForRetry(invoiceData);
// Return fallback response
return {
status: 'pending',
message: 'Invoice queued for processing',
retryAfter: 300 // 5 minutes
};
}
}Getting Help
If you continue experiencing issues:
- Check the Dashboard: View API logs and error details
- Review Documentation: Ensure you're using endpoints correctly
- Contact Support: Email support@safepays.com with:
- Request/Response examples
- Error messages
- Timestamps
- API key (first 8 characters only)
Related Resources
- Authentication - API key setup and security
- Rate Limits - Detailed rate limiting information
- API Reference - Complete endpoint documentation