Error Handling

The HeadshotPro API uses conventional HTTP status codes and returns consistent error objects.

Error Response Format

All errors return a JSON object with the following structure:

{
  "success": false,
  "error": "Human-readable error message",
  "code": "MACHINE_READABLE_CODE"
}

HTTP Status Codes

StatusDescription
200Success
400Bad Request - Invalid parameters or request body
401Unauthorized - Invalid or missing API key
402Payment Required - Insufficient credits
403Forbidden - Feature not enabled or permission denied
404Not Found - Resource doesn't exist
429Too Many Requests - Rate limit exceeded
500Internal Server Error - Something went wrong on our end

Error Codes

Authentication Errors

CodeHTTP StatusDescription
UNAUTHORIZED401Missing or invalid API key
FORBIDDEN403API key valid but lacks permission

Validation Errors

CodeHTTP StatusDescription
INVALID_REQUEST400Missing required field or invalid format
INVALID_EMAIL400Email address format is invalid

Resource Errors

CodeHTTP StatusDescription
NOT_FOUND404Requested resource doesn't exist
ALREADY_EXISTS400Resource already exists (duplicate)

Business Logic Errors

CodeHTTP StatusDescription
INSUFFICIENT_CREDITS402Organization has insufficient credits
ALREADY_DELETED400Resource was already deleted

Rate Limiting

CodeHTTP StatusDescription
RATE_LIMIT_EXCEEDED429Too many requests in time window
INVITE_LIMIT_EXCEEDED429Too many pending invites (see Invite Limits)

Common Error Scenarios

Invalid API Key

{
  "success": false,
  "error": "Invalid API key",
  "code": "UNAUTHORIZED"
}

Solution: Check that your API key is correct and included in the Authorization header.

Insufficient Credits

{
  "success": false,
  "error": "Insufficient credits",
  "code": "INSUFFICIENT_CREDITS"
}

Solution: Purchase more credits or check your current balance with GET /organization/credits.

User Already Exists

{
  "success": false,
  "error": "A user with this email already exists. You can solve this by appending a + to the email address, i.e. user+1@example.com",
  "code": "INVALID_REQUEST"
}

Solution: Use email aliasing (e.g., user+timestamp@example.com) or check if the user already has a model.

Team Not Found

{
  "success": false,
  "error": "Team not found",
  "code": "NOT_FOUND"
}

Solution: Verify the team ID by calling GET /organization/teams.

Whitelabel Not Enabled

{
  "success": false,
  "error": "Whitelabel feature not enabled",
  "code": "FORBIDDEN"
}

Solution: Contact sales@headshotpro.com to enable whitelabel for your organization.

Error Handling Best Practices

1. Always Check success Field

const response = await fetch(url, options);
const data = await response.json();

if (!data.success) {
  console.error(`API Error: ${data.code} - ${data.error}`);
  // Handle error appropriately
  return;
}

// Process successful response

2. Handle Specific Error Codes

if (!data.success) {
  switch (data.code) {
    case 'INSUFFICIENT_CREDITS':
      notifyAdmin('Credits running low');
      break;
    case 'RATE_LIMIT_EXCEEDED':
      await delay(60000); // Wait 1 minute
      return retry(request);
    case 'NOT_FOUND':
      return null; // Resource doesn't exist
    default:
      throw new Error(data.error);
  }
}

3. Implement Retry Logic

async function apiRequest(url, options, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    const response = await fetch(url, options);

    if (response.status === 429) {
      const retryAfter = response.headers.get('X-RateLimit-Reset');
      const waitTime = new Date(retryAfter) - new Date();
      await delay(waitTime);
      continue;
    }

    if (response.status >= 500) {
      await delay(1000 * attempt); // Exponential backoff
      continue;
    }

    return response.json();
  }
  throw new Error('Max retries exceeded');
}

4. Log Errors for Debugging

if (!data.success) {
  console.error({
    timestamp: new Date().toISOString(),
    endpoint: url,
    method: options.method,
    status: response.status,
    code: data.code,
    error: data.error
  });
}