Documentation / Getting Started

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 Authorization header
  • 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
  • from address 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

  1. Check the error code — The code field tells you exactly what went wrong
  2. Look at the field — For validation errors, the field points to the problem
  3. Read the message — Human-readable explanation
  4. Check request logs — In your dashboard under Logs → API Requests
  5. Validate locally — Use /v1/messages/analyze to check content before sending

Getting Help

If you’re stuck:

Next steps

Now you understand how to handle errors. Learn about sending emails in detail.

Sending emails →