Skip to content
Dashboard

Error Handling

Understand API error responses and implement robust error handling in your applications.

Error Response Format

All API errors follow a consistent JSON structure. Every error response includes a success field set to false, an error object with a machine-readable code and human-readable message, and a meta object with request metadata.

{
  "success": false,
  "error": {
    "code": "ERROR_CODE",
    "message": "Human-readable error description"
  },
  "meta": {
    "request_id": "req_abc123def456",
    "timestamp": "2026-02-01T12:00:00Z"
  }
}

The request_id is useful for debugging — include it when contacting support.

Error Codes Reference

The following table lists all possible error codes, their HTTP status codes, and common causes:

StatusCodeDescriptionCommon Cause
400VALIDATION_ERRORInvalid request body or parametersMissing required fields, wrong data types, values out of range
401UNAUTHORIZEDMissing or invalid authenticationNo Bearer token, expired key, malformed key
402INSUFFICIENT_CREDITSNot enough credits for the operationAccount balance too low for the requested operation
403FORBIDDENAPI key lacks the required permission or scopeKey missing write permission for POST endpoints, wrong scope
404NOT_FOUNDResource does not exist or is not owned by the userInvalid ID, deleted resource, accessing another user's data
409CONFLICTOperation conflicts with current resource statePublishing already-published content, duplicate operations
412PRECONDITION_FAILEDRequired precondition not metPlatform not connected for social publishing
429RATE_LIMIT_EXCEEDEDToo many requestsExceeded per-minute or daily rate limit
500INTERNAL_ERRORUnexpected server errorServer-side issue — retry with backoff

Error Response Examples

400 Validation Error

400VALIDATION_ERROR
{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "topic is required and must be 3-500 characters"
  },
  "meta": {
    "request_id": "req_abc123",
    "timestamp": "2026-02-01T12:00:00Z"
  }
}

401 Unauthorized

401UNAUTHORIZED
{
  "success": false,
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Invalid or expired API key"
  },
  "meta": {
    "request_id": "req_def456",
    "timestamp": "2026-02-01T12:00:00Z"
  }
}

402 Insufficient Credits

402INSUFFICIENT_CREDITS
{
  "success": false,
  "error": {
    "code": "INSUFFICIENT_CREDITS",
    "message": "This operation requires 40 credits but your balance is 12"
  },
  "meta": {
    "request_id": "req_ghi789",
    "timestamp": "2026-02-01T12:00:00Z"
  }
}

403 Forbidden

403FORBIDDEN
{
  "success": false,
  "error": {
    "code": "FORBIDDEN",
    "message": "API key lacks 'write' permission required for this endpoint"
  },
  "meta": {
    "request_id": "req_jkl012",
    "timestamp": "2026-02-01T12:00:00Z"
  }
}

429 Rate Limit Exceeded

429RATE_LIMIT_EXCEEDED
{
  "success": false,
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "Rate limit exceeded. Retry after 45 seconds."
  },
  "meta": {
    "request_id": "req_mno345",
    "timestamp": "2026-02-01T12:00:00Z",
    "retry_after": 45
  }
}

Retry Strategies

Not all errors should be retried. Use the following guidelines to determine when and how to retry failed requests:

  • 429 Rate Limit: Always retry with exponential backoff using the Retry-After header
  • 500 Internal Error: Retry up to 3 times with exponential backoff
  • 400 / 401 / 403 / 404: Do NOT retry — fix the request first
async function apiRequest(url, options, maxRetries = 3) {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    const res = await fetch(url, options);

    if (res.ok) return res.json();

    const body = await res.json();

    // Don't retry client errors (except rate limits)
    if (res.status < 500 && res.status !== 429) {
      throw new Error(`API Error: ${body.error.code} — ${body.error.message}`);
    }

    if (attempt === maxRetries) {
      throw new Error(`Failed after ${maxRetries + 1} attempts: ${body.error.code}`);
    }

    // Exponential backoff: 1s, 2s, 4s
    const retryAfter = res.headers.get('Retry-After');
    const delay = retryAfter ? parseInt(retryAfter) * 1000 : Math.pow(2, attempt) * 1000;
    await new Promise(resolve => setTimeout(resolve, delay));
  }
}

Best Practices

  • Always check the success field before accessing data
  • Log request_id from error responses for debugging
  • Implement exponential backoff for 429 and 500 errors
  • Check credit balance before expensive operations to avoid 402 errors
  • Validate input client-side before sending to reduce 400 errors
  • Use appropriate permissions and scopes to prevent 403 errors