Skip to main content

Error Response Format

All errors follow a consistent JSON structure:
{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid request parameters",
    "details": {
      "field": "prompt",
      "reason": "Required field is missing"
    }
  }
}

HTTP Status Codes

StatusMeaningWhen
400Bad RequestInvalid parameters
401UnauthorizedMissing or invalid API key
402Payment RequiredInsufficient balance
403ForbiddenKey lacks permissions
404Not FoundResource does not exist
409ConflictCannot cancel a completed task
422Unprocessable EntityBusiness logic error
429Too Many RequestsRate limit exceeded
500Internal Server ErrorUnexpected server error
502Bad GatewayProvider returned an error
503Service UnavailableProvider temporarily down

Error Codes

Authentication Errors (401)

CodeDescription
AUTH_REQUIREDNo Authorization header provided
AUTH_INVALID_TOKENAPI key is malformed or does not exist
AUTH_TOKEN_EXPIREDAPI key has passed its expiry date

Permission Errors (403)

CodeDescription
FORBIDDENAction not allowed
KEY_PERMISSION_DENIEDAPI key lacks required provider/type permission
KEY_EXPIREDAPI key has expired
KEY_REVOKEDAPI key has been deleted

Resource Errors (404)

CodeDescription
NOT_FOUNDGeneric resource not found
MODEL_NOT_FOUNDRequested model does not exist
TASK_NOT_FOUNDTask ID does not exist
PROVIDER_NOT_FOUNDProvider does not exist

Validation Errors (400/422)

CodeDescription
VALIDATION_ERRORRequest body failed validation
INVALID_PARAMETERA parameter value is out of range or wrong type
MISSING_PARAMETERA required parameter is missing

Payment Errors (402)

CodeDescription
INSUFFICIENT_BALANCEAccount balance too low for this request
PAYMENT_REQUIREDNo payment method on file
PAYMENT_FAILEDCharge attempt failed

Business Logic Errors (422)

CodeDescription
QUOTA_EXCEEDEDMonthly quota for this API key reached
FILE_TOO_LARGEUploaded file exceeds size limit
UNSUPPORTED_FORMATFile format not supported

Rate Limit Errors (429)

CodeDescription
RATE_LIMIT_EXCEEDEDToo many requests — slow down

Conflict Errors (409)

CodeDescription
TASK_ALREADY_COMPLETEDCannot modify a completed task
TASK_CANNOT_CANCELTask is not in a cancellable state

Provider Errors (502)

CodeDescription
PROVIDER_ERRORUpstream provider returned an error
PROVIDER_UNAVAILABLEProvider is temporarily offline
PROVIDER_TIMEOUTProvider did not respond in time
PROVIDER_RATE_LIMITEDProvider’s own rate limit hit

Internal Errors (500)

CodeDescription
INTERNAL_ERRORUnexpected server error

Retry Strategy

Retryable vs Non-Retryable

RetryableNon-Retryable
429 Rate limited400 Validation error
500 Internal error401 Authentication error
502 Provider error402 Payment error
503 Unavailable403 Permission error
PROVIDER_TIMEOUT404 Not found
PROVIDER_RATE_LIMITED409 Conflict

Exponential Backoff

For retryable errors, use exponential backoff:
async function requestWithRetry(fn, maxRetries = 3) {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      return await fn();
    } catch (error) {
      if (attempt === maxRetries || !isRetryable(error.status)) {
        throw error;
      }
      const delay = Math.min(1000 * Math.pow(2, attempt), 30000);
      await new Promise((resolve) => setTimeout(resolve, delay));
    }
  }
}

function isRetryable(status) {
  return [429, 500, 502, 503].includes(status);
}

Rate Limit Handling

When you receive a 429, check the X-RateLimit-Reset header for when you can retry:
if (response.status === 429) {
  const resetTimestamp = response.headers.get("X-RateLimit-Reset");
  const waitMs = parseInt(resetTimestamp, 10) * 1000 - Date.now();
  await new Promise((resolve) => setTimeout(resolve, Math.max(waitMs, 1000)));
}