Error Handling
3 min read
This guide explains the error responses you may receive from the MailingAPI and how to handle them properly.
Error Response Format
All errors follow a consistent JSON format:
{
"error": {
"code": "validation_error",
"message": "Invalid email address format",
"field": "to",
"details": {
"value": "not-an-email",
"expected": "valid email address"
}
}
}
Fields:
-
code— Machine-readable error identifier -
message— Human-readable description -
field— (optional) Which field caused the error -
details— (optional) Additional context
HTTP Status Codes
| Code | Meaning | When it happens |
|---|---|---|
200 |
OK | Request succeeded |
201 |
Created | Resource created |
202 |
Accepted | Async operation queued |
400 |
Bad Request | Invalid parameters |
401 |
Unauthorized | Missing or invalid API key |
403 |
Forbidden | Valid key, insufficient permissions |
404 |
Not Found | Resource doesn’t exist |
409 |
Conflict | Resource already exists |
422 |
Unprocessable | Valid syntax but semantic error |
429 |
Too Many Requests | Rate limit exceeded |
500 |
Server Error | Something went wrong on our end |
Common Error Codes
Authentication Errors (401)
{"error": {"code": "unauthorized", "message": "Invalid or missing API key"}}
Causes:
-
No
Authorizationheader - Malformed bearer token
- Invalid or revoked API key
Fix: Check your API key is correct and active.
Permission Errors (403)
{"error": {"code": "forbidden", "message": "API key lacks 'manage' permission"}}
Causes:
- Key doesn’t have required permission level
- Trying to access another organization’s resources
-
fromaddress doesn’t match key’s domain
Fix: Use a key with appropriate permissions or correct the from address.
Validation Errors (400)
{
"error": {
"code": "validation_error",
"message": "Validation failed",
"errors": [
{"field": "to", "message": "is required"},
{"field": "html", "message": "must be valid HTML"}
]
}
}
Fix: Check the errors array and correct each field.
Rate Limit Errors (429)
{
"error": {
"code": "rate_limited",
"message": "Too many requests",
"retry_after": 60
}
}
Fix: Wait retry_after seconds, then retry with exponential backoff.
Resource Errors (404, 409)
{"error": {"code": "not_found", "message": "Message not found"}}
{"error": {"code": "conflict", "message": "Domain already exists"}}
Fix: Verify the resource ID or check for duplicates.
Handling Errors in Code
Python
import requests
from requests.exceptions import HTTPError
try:
response = requests.post(
"https://api.mailingapi.com/v1/messages/send",
headers={"Authorization": f"Bearer {api_key}"},
json=payload
)
response.raise_for_status()
result = response.json()
except HTTPError as e:
error = e.response.json().get("error", {})
if e.response.status_code == 401:
# Re-authenticate or check API key
handle_auth_error()
elif e.response.status_code == 429:
# Respect rate limit
retry_after = error.get("retry_after", 60)
time.sleep(retry_after)
# Retry request
elif e.response.status_code == 400:
# Log validation errors
logger.error(f"Validation failed: {error}")
else:
# Unexpected error
logger.error(f"API error: {error}")
raise
JavaScript
async function sendEmail(payload) {
try {
const response = await fetch('https://api.mailingapi.com/v1/messages/send', {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
});
if (!response.ok) {
const { error } = await response.json();
switch (response.status) {
case 401:
throw new AuthenticationError(error.message);
case 429:
const retryAfter = error.retry_after || 60;
await sleep(retryAfter * 1000);
return sendEmail(payload); // Retry
case 400:
throw new ValidationError(error.message, error.errors);
default:
throw new APIError(error.message, error.code);
}
}
return response.json();
} catch (error) {
console.error('Failed to send email:', error);
throw error;
}
}
Retry Strategy
For transient errors (429, 500, 502, 503, 504), implement exponential backoff:
import time
import random
def send_with_retry(payload, max_retries=3):
for attempt in range(max_retries):
try:
response = send_email(payload)
return response
except TransientError as e:
if attempt == max_retries - 1:
raise
# Exponential backoff with jitter
delay = (2 ** attempt) + random.uniform(0, 1)
time.sleep(delay)
Do:
- Retry on 429, 500, 502, 503, 504
- Use exponential backoff
- Add random jitter to prevent thundering herd
- Set a maximum retry count
Don’t:
- Retry on 400, 401, 403, 404 — these won’t change
- Retry immediately without delay
- Retry indefinitely
Idempotency
For safe retries, use idempotency keys:
curl -X POST https://api.mailingapi.com/v1/messages/send \
-H "Authorization: Bearer $API_KEY" \
-H "Idempotency-Key: unique-request-id-123" \
-H "Content-Type: application/json" \
-d '{...}'
If you retry with the same Idempotency-Key, you’ll get the original response instead of creating a duplicate.
Debugging Tips
-
Check the error code — The
codefield tells you exactly what went wrong -
Look at the field — For validation errors, the
fieldpoints to the problem - Read the message — Human-readable explanation
- Check request logs — In your dashboard under Logs → API Requests
-
Validate locally — Use
/v1/messages/analyzeto check content before sending
Getting Help
If you’re stuck:
- Check our troubleshooting guide
- Search API Reference for endpoint details
-
Contact support@mailingapi.com with:
- Request ID (from response headers)
- Error code and message
- Minimal reproduction example
Next steps
Now you understand how to handle errors. Learn about sending emails in detail.