Error Handling
Understanding and handling API errors
Error Response Format
All errors follow a consistent JSON format:
{
"success": false,
"error": "Error name",
"message": "Human-readable description"
}HTTP Status Codes
| Code | Meaning | Cause |
|---|---|---|
| 200 | OK | Request succeeded |
| 201 | Created | Resource created successfully |
| 400 | Bad Request | Invalid request parameters |
| 401 | Unauthorized | Invalid or missing token |
| 403 | Forbidden | Access denied (plan/subscription issue) |
| 404 | Not Found | Resource doesn't exist |
| 422 | Unprocessable Entity | Validation errors in request |
| 429 | Too Many Requests | Rate limit exceeded |
| 500 | Server Error | Internal server error |
Common Errors
401 Unauthorized - Invalid Token
{
"success": false,
"error": "Unauthorized"
}Causes:
- Missing Authorization header
- Malformed token (not "Bearer TOKEN")
- Expired or invalid token
- Wrong token format
Solution: Verify your token and authorization header format.
403 Forbidden - API Access Denied
{
"success": false,
"error": "API access is not available on your plan",
"message": "Upgrade your subscription to use the API",
"current_plan": "free"
}Causes:
- Free plan (no API access)
- Subscription expired
- Subscription inactive
Solution: Upgrade to Starter plan or higher.
403 Forbidden - Subscription Expired
{
"success": false,
"error": "Your subscription has expired",
"expired_at": "2025-01-20T00:00:00Z"
}Solution: Renew your subscription from billing settings.
404 Not Found
{
"message": "Resource not found"
}Causes:
- Wrong endpoint URL
- Resource ID doesn't exist
- Resource deleted
Solution: Verify the endpoint and resource ID.
422 Unprocessable Entity - Validation Error
{
"message": "The given data was invalid.",
"errors": {
"email": ["The email has already been taken."],
"url": ["The url field is required."]
}
}Causes:
- Missing required fields
- Invalid field format
- Field value already exists (unique constraint)
Solution: Check the errors object and provide valid data.
429 Too Many Requests - Rate Limited
{
"success": false,
"error": "Rate limit exceeded",
"message": "You have exceeded the API rate limit of 5 requests per minute",
"limit": 5,
"reset_at": "2025-01-27T15:31:00Z"
}Solution: Wait for the reset time or upgrade your plan.
500 Server Error
{
"message": "Server error"
}Causes:
- Temporary server issue
- Bug in the API
- Service down for maintenance
Solution: Retry the request after a delay. Contact support if persistent.
Error Handling Examples
JavaScript
async function callAPI(endpoint, options = {}) {
try {
const response = await fetch(endpoint, {
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
...options
});
const data = await response.json();
if (!response.ok) {
switch(response.status) {
case 401:
throw new Error('Authentication failed. Check your token.');
case 403:
if (data.error.includes('plan')) {
throw new Error('API not available on your plan. Upgrade to continue.');
}
throw new Error('Access denied.');
case 422:
const errors = Object.entries(data.errors || {})
.map(([key, msgs]) => `${key}: ${msgs.join(', ')}`)
.join('\n');
throw new Error(`Validation error:\n${errors}`);
case 429:
throw new Error(`Rate limited. Retry after ${data.reset_at}`);
default:
throw new Error(data.message || 'API error');
}
}
return data;
} catch (error) {
console.error('API Error:', error.message);
throw error;
}
}Python
import requests
from datetime import datetime
def call_api(endpoint, method='GET', data=None):
headers = {
'Authorization': f'Bearer {token}',
'Content-Type': 'application/json'
}
try:
response = requests.request(
method,
f'{base_url}{endpoint}',
headers=headers,
json=data
)
if response.status_code == 401:
raise Exception('Authentication failed. Check your token.')
elif response.status_code == 403:
error = response.json()
if 'plan' in error.get('error', ''):
raise Exception('API not available on your plan. Upgrade to continue.')
raise Exception('Access denied.')
elif response.status_code == 422:
errors = response.json().get('errors', {})
error_str = '\n'.join([
f'{k}: {", ".join(v)}'
for k, v in errors.items()
])
raise Exception(f'Validation error:\n{error_str}')
elif response.status_code == 429:
reset_at = response.json().get('reset_at')
raise Exception(f'Rate limited. Retry after {reset_at}')
elif response.status_code >= 400:
raise Exception(response.json().get('message', 'API error'))
return response.json()
except requests.RequestException as e:
raise Exception(f'Network error: {e}')Retry Strategy
Exponential Backoff
async function apiCallWithRetry(endpoint, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const response = await fetch(endpoint, {
headers: { 'Authorization': `Bearer ${token}` }
});
if (response.ok) {
return response.json();
}
if (response.status === 429) {
// Rate limited - exponential backoff
const delay = Math.pow(2, attempt) * 1000;
console.log(`Rate limited. Waiting ${delay}ms...`);
await new Promise(r => setTimeout(r, delay));
continue;
}
if (response.status >= 500) {
// Server error - retry with backoff
if (attempt < maxRetries - 1) {
const delay = Math.pow(2, attempt) * 1000;
console.log(`Server error. Retrying in ${delay}ms...`);
await new Promise(r => setTimeout(r, delay));
continue;
}
}
// Don't retry for client errors (4xx except 429)
const data = await response.json();
throw new Error(data.message || data.error);
} catch (error) {
if (attempt === maxRetries - 1) throw error;
}
}
}Logging Errors
// Log errors but never log tokens
function logError(error, context = {}) {
console.error({
timestamp: new Date().toISOString(),
error: error.message,
status: error.status,
endpoint: context.endpoint,
// Never log tokens!
// ❌ token: context.token,
userAgent: navigator.userAgent
});
}Support
If you encounter errors not listed here or need help:
- Check the FAQ
- Contact Support
- Check API Status
SnapTrack API