When a request fails, the API returns a JSON error response with an HTTP status code:
{
"error": {
"message": "Validation failed: amount must be greater than 0",
"code": "validation_error",
"details": {
"field": "amount",
"reason": "must be greater than 0"
}
}
}
| Field | Type | Description |
|---|
message | string | A human-readable description of what went wrong |
code | string | A machine-readable error code (e.g., validation_error, insufficient_balance, rate_limit_exceeded) |
details | object | Additional context about the error. Present for validation errors and other cases where field-level detail is available. |
HTTP status codes
Success codes
| Code | Meaning |
|---|
200 OK | Request succeeded |
201 Created | Resource created successfully |
204 No Content | Request succeeded, no body returned (e.g., delete) |
Client error codes
| Code | Meaning | What to do |
|---|
400 Bad Request | Invalid request body or parameters | Check the message and details fields for specifics. Fix the request and retry. |
401 Unauthorized | Missing or invalid API key | Check your Authorization header. Ensure the key isn’t revoked. |
403 Forbidden | Valid key but insufficient permissions | Your key doesn’t have access to this resource or action. |
404 Not Found | Resource doesn’t exist | Check the ID in your URL. The resource may have been deleted. |
409 Conflict | State conflict (e.g., cancelling a completed payout) | The resource can’t transition to the requested state. Check current status. |
422 Unprocessable Entity | Validation error | The request body is syntactically valid JSON but semantically wrong. |
429 Too Many Requests | Rate limit exceeded | Back off and retry after the Retry-After header value. See Rate limiting below. |
Server error codes
| Code | Meaning | What to do |
|---|
500 Internal Server Error | Something went wrong on our end | Retry with exponential backoff. If persistent, contact support. |
502 Bad Gateway | Upstream service unavailable | Retry in a few seconds. |
503 Service Unavailable | Service temporarily unavailable | Retry with backoff. Check status page. |
Rate limiting
The API enforces rate limits per merchant at 1,000 requests per minute. Every response includes rate limit headers so you can monitor your usage:
| Header | Description |
|---|
X-RateLimit-Limit | Maximum number of requests allowed per window |
X-RateLimit-Remaining | Number of requests remaining in the current window |
X-RateLimit-Reset | Unix timestamp (seconds) when the rate limit window resets |
Retry-After | Seconds to wait before retrying (only present on 429 responses) |
When you exceed the limit, the API returns a 429 response:
{
"error": {
"message": "Rate limit exceeded. Retry after 30 seconds.",
"code": "rate_limit_exceeded"
}
}
Use the Retry-After header to determine how long to wait, or use the X-RateLimit-Reset timestamp to calculate the delay.
Handling errors in code
const response = await fetch("https://api.antonpayments.dev/v1/payouts", {
method: "POST",
headers: {
"Authorization": `Bearer ${apiKey}`,
"Content-Type": "application/json",
"Idempotency-Key": idempotencyKey,
},
body: JSON.stringify(payoutData),
});
if (!response.ok) {
const { error } = await response.json();
switch (response.status) {
case 400:
case 422:
// Fix the request - don't retry
console.error("Validation error:", error.message);
if (error.details) {
console.error("Details:", error.details);
}
break;
case 401:
// Check API key
console.error("Authentication failed");
break;
case 429: {
// Back off and retry
const retryAfter = parseInt(response.headers.get("Retry-After") || "30", 10);
await new Promise(r => setTimeout(r, retryAfter * 1000));
// ... retry the request
break;
}
case 500:
case 502:
case 503:
// Retry with exponential backoff
break;
}
}
Never retry 4xx errors (except 429). They indicate a problem with your request that won’t be fixed by retrying. Only retry 5xx errors and rate limit (429) responses.