API Errors

Understanding and handling API error responses.

Error Response Format

All errors follow this format:

{
  "error": {
    "code": "error_code",
    "message": "Human-readable description",
    "details": {
      // Optional additional context
    }
  }
}

HTTP Status Codes

Status Meaning When
200 OK Request succeeded
201 Created Resource created successfully
204 No Content Successful deletion
400 Bad Request Invalid request format
401 Unauthorized Missing or invalid API key
403 Forbidden API key lacks permission
404 Not Found Resource doesn't exist
422 Unprocessable Entity Validation errors
429 Too Many Requests Rate limit exceeded
500 Internal Server Error Something went wrong on our end

Common Error Codes

Authentication Errors

Code Message Solution
unauthorized API key is required Add X-API-Key header
invalid_api_key Invalid API key Check your API key is correct
revoked_api_key API key has been revoked Generate a new API key

Validation Errors

{
  "error": {
    "code": "validation_error",
    "message": "Validation failed",
    "details": {
      "email": ["is invalid", "has already been taken"],
      "name": ["can't be blank"]
    }
  }
}
Code Description
validation_error One or more fields failed validation
invalid_email Email format is invalid
duplicate_email Email already exists
missing_required_field Required field was not provided

Resource Errors

Code Description
not_found Resource doesn't exist
already_sent Issue has already been sent
cannot_delete Resource cannot be deleted

Rate Limiting

HTTP/1.1 429 Too Many Requests

{
  "error": {
    "code": "rate_limited",
    "message": "Rate limit exceeded",
    "details": {
      "limit": 1000,
      "remaining": 0,
      "reset_at": "2025-01-10T13:00:00Z"
    }
  }
}

Feature/Tier Errors

Code Description
sending_disabled Newsletter is on free tier
feature_not_available Feature requires upgrade
no_subscribers Newsletter has no active subscribers

Handling Errors

JavaScript Example

try {
  const response = await fetch('https://dailydraft.ai/api/v1/subscriptions', {
    method: 'POST',
    headers: {
      'X-API-Key': apiKey,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ email: '[email protected]' })
  });

  if (!response.ok) {
    const error = await response.json();

    switch (error.error.code) {
      case 'duplicate_email':
        console.log('Subscriber already exists');
        break;
      case 'validation_error':
        console.log('Validation errors:', error.error.details);
        break;
      case 'rate_limited':
        const resetAt = new Date(error.error.details.reset_at);
        console.log(`Rate limited. Retry after ${resetAt}`);
        break;
      default:
        console.log('Error:', error.error.message);
    }
  }
} catch (e) {
  console.log('Network error:', e);
}

Ruby Example

response = HTTParty.post(
  'https://dailydraft.ai/api/v1/subscriptions',
  headers: { 'X-API-Key' => api_key },
  body: { email: '[email protected]' }.to_json
)

unless response.success?
  error = response.parsed_response['error']

  case error['code']
  when 'duplicate_email'
    puts 'Subscriber already exists'
  when 'rate_limited'
    retry_after = Time.parse(error['details']['reset_at'])
    puts "Rate limited. Retry after #{retry_after}"
  else
    puts "Error: #{error['message']}"
  end
end

Best Practices

  1. Always check status codes: Don't assume success
  2. Handle rate limits: Implement exponential backoff
  3. Parse error details: Use details for field-level errors
  4. Log errors: Keep logs for debugging
  5. Graceful degradation: Don't crash on API errors

Need Help?

Getting unexpected errors? Email [email protected] with:

  • The endpoint you're calling
  • The request body (without sensitive data)
  • The full error response
  • Timestamp of the request

Still need help?

Can't find what you're looking for? Chat with our AI assistant or create a support ticket.

Sign in to get support