Error Codes
When an API request fails, the response includes an error code and message to help you understand and handle the issue.
Error Response Format
All error responses follow this structure (request_id is included when available — quote it when contacting support):
{
"success": false,
"error": {
"code": "ERROR_CODE",
"message": "Human-readable error description",
"request_id": "abc123..."
}
}Error Code Reference
| Code | HTTP | Description |
|---|---|---|
BAD_REQUEST | 400 | Invalid request parameters, malformed JSON, or missing required fields (details in the message) |
INVALID_WEBHOOK_SECRET | 400 | webhook_secret is empty, longer than 256 characters, or supplied without webhook_url |
WEBHOOK_URL_INVALID | 400 | Webhook URL is malformed, not HTTPS, or targets a private/reserved address |
UNAUTHORIZED | 401 | Missing or invalid API key |
INSUFFICIENT_CREDITS | 402 | Not enough credits for this operation |
PAYMENT_REQUIRED | 402 | A paid plan or payment is required for this operation |
FORBIDDEN | 403 | API key does not have permission to access this resource |
PLAN_LIMIT_EXCEEDED | 403 | Your plan’s limit for this feature has been reached (e.g. connected integrations) |
NOT_FOUND | 404 | Resource not found (e.g. unknown job ID or expired uploaded file) |
METHOD_NOT_ALLOWED | 405 | HTTP method not supported on this endpoint |
CONFLICT | 409 | Request conflicts with the current state of the resource |
BATCH_SIZE_EXCEEDED | 413 | Batch or file exceeds your plan’s per-batch email cap |
RATE_LIMITED | 429 | Too many requests — check the Retry-After header when present |
INTERNAL_ERROR | 500 | Unexpected server error |
SERVICE_UNAVAILABLE | 503 | Service temporarily unavailable |
WEBHOOK_DNS_TEMPORARY_FAILURE | 503 | Webhook URL hostname could not be resolved (transient DNS failure) — safe to retry |
INTEGRATION_NOT_CONFIGURED | 503 | The requested integration provider is not configured/available yet |
GATEWAY_TIMEOUT | 504 | Upstream verification timed out — retry or poll the job |
Handling Errors
4xx Client Errors
These errors indicate a problem with the request. Check the error message, fix the issue, and retry.
429 Rate Limited
Retry timing is communicated via the Retry-After response header (there is no retry_after field in the JSON body). Auth endpoints (login, registration, password reset) and per-key API throttling set it — wait that long, then retry. Other 429s come from a shared platform throttle that may omit the header; back off with exponential, jittered delays.
5xx Server Errors
These are server-side issues. Retry with exponential backoff. If the problem persists, contact support.
Example: Error Handling in Node.js
async function verifyEmail(email) {
try {
const response = await fetch('https://api.kawaa.com/v1/verify', {
method: 'POST',
headers: {
'X-Api-Key': process.env.KAWAA_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({ email }),
});
const data = await response.json();
if (!data.success) {
switch (data.error.code) {
case 'RATE_LIMITED': {
// Retry timing arrives in the Retry-After response HEADER (not the
// JSON body). It may be absent on shared-throttle 429s — back off.
const retryAfter = Number(response.headers.get('retry-after')) || 60;
await sleep(retryAfter * 1000);
return verifyEmail(email);
}
case 'INSUFFICIENT_CREDITS':
// Handle billing issue
throw new Error('Please add credits to continue');
case 'BAD_REQUEST':
// e.g. malformed email input — skip it
return { status: 'skipped', reason: data.error.message };
default:
throw new Error(data.error.message);
}
}
return data.data;
} catch (error) {
console.error('Verification failed:', error);
throw error;
}
}Need Help?
If you're experiencing persistent errors, check our status page for any ongoing issues, or contact support.