The Ark API uses conventional HTTP response codes and returns detailed error information to help you diagnose and fix issues. All SDKs provide typed exceptions for easy error handling.
All error responses follow this structure:
{
"success": false,
"error": {
"code": "validation_error",
"message": "Human-readable error message",
"field": "fieldName",
"suggestion": "How to fix the error",
"hint": "Detailed guidance with examples or documentation links"
},
"meta": {
"requestId": "req_abc123"
}
}
| Field | Description |
|---|
code | Machine-readable error code |
message | Human-readable description |
field | The field that caused the error (if applicable) |
suggestion | Recommended action to fix the error |
hint | Detailed guidance with examples or documentation links (for complex errors) |
requestId | Unique ID for support requests |
HTTP Status Codes
| Status | Description |
|---|
200 | Success |
201 | Resource created |
207 | Partial success (batch operations) |
400 | Bad request - invalid parameters |
401 | Unauthorized - invalid or missing API key |
403 | Forbidden - insufficient permissions |
404 | Resource not found |
409 | Conflict - resource already exists |
422 | Unprocessable - semantic error |
429 | Rate limit exceeded |
500 | Server error |
SDK Exception Types
All SDKs provide typed exceptions that map to HTTP status codes:
| Exception | HTTP Status | Description |
|---|
BadRequestError | 400 | Invalid request parameters |
AuthenticationError | 401 | Invalid or missing API key |
PermissionDeniedError | 403 | Insufficient permissions |
NotFoundError | 404 | Resource not found |
UnprocessableEntityError | 422 | Validation error |
RateLimitError | 429 | Too many requests |
InternalServerError | 5xx | Server error |
APIConnectionError | — | Network/connection issue |
Handling Errors with SDKs
import ark
from ark import Ark
client = Ark()
try:
email = client.emails.send(
from_="hello@yourdomain.com",
to=["user@example.com"],
subject="Hello",
html="<p>Hello!</p>"
)
print(f"Email sent: {email.data.id}")
except ark.BadRequestError as e:
# Invalid request parameters
print(f"Bad request: {e.message}")
if hasattr(e, 'code'):
print(f"Error code: {e.code}")
except ark.AuthenticationError:
# Invalid or missing API key
print("Authentication failed - check your API key")
except ark.RateLimitError as e:
# Too many requests - wait and retry
print("Rate limited - slow down requests")
# Access retry-after header if available
# retry_after = e.response.headers.get('retry-after')
except ark.NotFoundError:
# Resource not found
print("Resource not found")
except ark.APIConnectionError:
# Network error - safe to retry
print("Network error - retrying...")
except ark.APIError as e:
# Catch-all for other API errors
print(f"API error: {e.message}")
import Ark from 'ark';
const client = new Ark();
try {
const email = await client.emails.send({
from: 'hello@yourdomain.com',
to: ['user@example.com'],
subject: 'Hello',
html: '<p>Hello!</p>',
});
console.log(`Email sent: ${email.data.id}`);
} catch (error) {
if (error instanceof Ark.BadRequestError) {
// Invalid request parameters
console.log(`Bad request: ${error.message}`);
console.log(`Error code: ${error.code}`);
} else if (error instanceof Ark.AuthenticationError) {
// Invalid or missing API key
console.log('Authentication failed - check your API key');
} else if (error instanceof Ark.RateLimitError) {
// Too many requests - wait and retry
console.log('Rate limited - slow down requests');
const retryAfter = error.headers?.['retry-after'];
} else if (error instanceof Ark.NotFoundError) {
// Resource not found
console.log('Resource not found');
} else if (error instanceof Ark.APIConnectionError) {
// Network error - safe to retry
console.log('Network error - retrying...');
} else if (error instanceof Ark.APIError) {
// Catch-all for other API errors
console.log(`API error: ${error.message}`);
}
}
require "ark_email"
client = ArkEmail::Client.new
begin
email = client.emails.send_(
from: "hello@yourdomain.com",
to: ["user@example.com"],
subject: "Hello",
html: "<p>Hello!</p>"
)
puts "Email sent: #{email.data.id}"
rescue ArkEmail::Errors::BadRequestError => e
# Invalid request parameters
puts "Bad request: #{e.message}"
puts "Error code: #{e.code}" if e.respond_to?(:code)
rescue ArkEmail::Errors::AuthenticationError
# Invalid or missing API key
puts "Authentication failed - check your API key"
rescue ArkEmail::Errors::RateLimitError => e
# Too many requests - wait and retry
puts "Rate limited - slow down requests"
rescue ArkEmail::Errors::NotFoundError
# Resource not found
puts "Resource not found"
rescue ArkEmail::Errors::APIConnectionError
# Network error - safe to retry
puts "Network error - retrying..."
rescue ArkEmail::Errors::APIError => e
# Catch-all for other API errors
puts "API error: #{e.message}"
end
import (
"errors"
"fmt"
"github.com/ArkHQ-io/ark-go"
)
client := ark.NewClient()
email, err := client.Emails.Send(ctx, ark.EmailSendParams{
From: "hello@yourdomain.com",
To: []string{"user@example.com"},
Subject: "Hello",
HTML: ark.String("<p>Hello!</p>"),
})
if err != nil {
var arkErr *ark.Error
if errors.As(err, &arkErr) {
switch arkErr.StatusCode {
case 400:
// Invalid request parameters
fmt.Printf("Bad request: %s\n", arkErr.Message)
case 401:
// Invalid or missing API key
fmt.Println("Authentication failed - check your API key")
case 429:
// Too many requests - wait and retry
fmt.Println("Rate limited - slow down requests")
case 404:
// Resource not found
fmt.Println("Resource not found")
default:
// Other API errors
fmt.Printf("API error %d: %s\n", arkErr.StatusCode, arkErr.Message)
}
} else {
// Network or other error
fmt.Printf("Error: %v\n", err)
}
return
}
fmt.Printf("Email sent: %s\n", email.Data.ID)
Error Codes Reference
Authentication Errors
| Code | Description | Solution |
|---|
authentication_required | No API key provided | Add Authorization: Bearer <key> header |
invalid_api_key | API key is malformed | Check key format (should start with ark_) |
expired_api_key | API key has been revoked | Generate a new key |
permission_denied | Key lacks required scope | Use a key with appropriate permissions |
Validation Errors
| Code | Description | Solution |
|---|
validation_error | Request failed validation | Check the field and suggestion |
invalid_email_format | Email address is invalid | Fix the email format |
missing_required_field | Required field not provided | Add the missing field |
invalid_field_value | Field value is not allowed | Check allowed values |
invalid_raw_message | Raw MIME message is invalid | Check the hint field for detailed guidance |
Resource Errors
| Code | Description | Solution |
|---|
resource_not_found | Resource doesn’t exist | Check the ID |
already_exists | Resource already exists | Use existing or choose different identifier |
invalid_state | Operation not allowed in current state | Check resource status |
Sending Errors
| Code | Description | Solution |
|---|
domain_not_verified | Sending domain not verified | Complete domain setup or use sandbox mode |
suppressed_recipient | Email is on suppression list | Remove from suppressions or skip |
Sandbox Errors
These errors occur when using sandbox mode (sending from @arkhq.io addresses):
| Code | Description | Solution |
|---|
sandbox_recipient_not_allowed | Recipient is not an organization member | Add recipient to your team at arkhq.io/org/team, or set up a custom domain to send to anyone |
sandbox_rate_limit_exceeded | Daily sandbox limit (10 emails/day) reached | Wait 24 hours for the limit to reset, or set up a custom domain for unlimited sending |
Sandbox Mode: Sandbox is designed for testing the API without domain verification. Use sandbox@arkhq.io as the sender to send test emails to organization members. No billing charges apply to sandbox emails.
Billing Errors
Billing errors occur when your account doesn’t have an active plan or has exceeded its email limit.
| Code | Description | Solution |
|---|
billing_not_configured | Billing hasn’t been set up | Visit billing settings to choose a plan |
insufficient_balance | Email limit reached for current plan | Upgrade your plan or wait for your next billing cycle |
billing_error | Billing system temporarily unavailable | Retry your request; the system will fail-open if issues persist |
Fail-open design: If the billing system experiences temporary issues, emails are still accepted to prevent service disruption. You’re only charged for successfully processed emails.
Handling Billing Errors in SDKs
import ark
from ark import Ark
client = Ark()
try:
email = client.emails.send(
from_="hello@yourdomain.com",
to=["user@example.com"],
subject="Hello",
html="<p>Hello!</p>"
)
except ark.BadRequestError as e:
if e.code == "insufficient_balance":
print("Upgrade your plan at https://arkhq.io/org/billing")
elif e.code == "billing_not_configured":
print("Set up billing at https://arkhq.io/org/billing")
import Ark from 'ark-email';
const client = new Ark();
try {
const email = await client.emails.send({
from: 'hello@yourdomain.com',
to: ['user@example.com'],
subject: 'Hello',
html: '<p>Hello!</p>',
});
} catch (error) {
if (error instanceof Ark.BadRequestError) {
if (error.code === 'insufficient_balance') {
console.log('Upgrade your plan at https://arkhq.io/org/billing');
} else if (error.code === 'billing_not_configured') {
console.log('Set up billing at https://arkhq.io/org/billing');
}
}
}
Automatic Retries
All SDKs automatically retry failed requests with exponential backoff for:
- Connection errors
- 408 Request Timeout
- 409 Conflict
- 429 Rate Limit
- 5xx Server Errors
Default: 2 retries. Configure per-client:
client = Ark(max_retries=5) # 5 retries
client = Ark(max_retries=0) # Disable retries
const client = new Ark({ maxRetries: 5 }); // 5 retries
const client = new Ark({ maxRetries: 0 }); // Disable retries
client = ArkEmail::Client.new(max_retries: 5) # 5 retries
client = ArkEmail::Client.new(max_retries: 0) # Disable retries
client := ark.NewClient(option.WithMaxRetries(5)) // 5 retries
client := ark.NewClient(option.WithMaxRetries(0)) // Disable retries
Getting Help
If you encounter persistent errors:
- Check the
requestId in the error response
- Review your request parameters
- Check the status page for outages
- Contact support with the
requestId