Skip to main content

Error Handling

Posthoot API uses standard HTTP status codes and provides detailed error messages to help you understand and resolve issues.

📋 HTTP Status Codes

2xx Success

  • 200 OK: Request successful
  • 201 Created: Resource created successfully

4xx Client Errors

  • 400 Bad Request: Invalid request data
  • 401 Unauthorized: Authentication required
  • 403 Forbidden: Insufficient permissions
  • 404 Not Found: Resource not found
  • 409 Conflict: Resource conflict
  • 422 Unprocessable Entity: Validation errors
  • 429 Too Many Requests: Rate limit exceeded

5xx Server Errors

  • 500 Internal Server Error: Server error
  • 502 Bad Gateway: Gateway error
  • 503 Service Unavailable: Service temporarily unavailable

🚨 Error Response Format

All error responses follow this format:
{
  "error": "error_type",
  "message": "Human-readable error message",
  "details": {
    "field": "Additional error details"
  },
  "code": "ERROR_CODE",
  "timestamp": "2024-01-01T00:00:00Z"
}

🔍 Common Error Types

Validation Errors (400)

{
  "error": "validation_error",
  "message": "Invalid request data",
  "details": {
    "email": "Invalid email format",
    "password": "Password must be at least 8 characters"
  },
  "code": "VALIDATION_ERROR"
}

Authentication Errors (401)

{
  "error": "unauthorized",
  "message": "Invalid or expired token",
  "code": "INVALID_TOKEN"
}

Permission Errors (403)

{
  "error": "forbidden",
  "message": "Insufficient permissions for this resource",
  "code": "INSUFFICIENT_PERMISSIONS"
}

Not Found Errors (404)

{
  "error": "not_found",
  "message": "Campaign not found",
  "code": "RESOURCE_NOT_FOUND"
}

Rate Limit Errors (429)

{
  "error": "rate_limit_exceeded",
  "message": "Rate limit exceeded. Try again in 60 seconds.",
  "retry_after": 60,
  "code": "RATE_LIMIT_EXCEEDED"
}

🛠️ Error Handling Examples

JavaScript/Node.js

async function makeApiCall() {
  try {
    const response = await fetch('/api/v1/campaigns', {
      headers: {
        'Authorization': `Bearer ${token}`
      }
    });

    if (!response.ok) {
      const error = await response.json();
      
      switch (response.status) {
        case 401:
          // Handle authentication error
          await refreshToken();
          break;
        case 403:
          // Handle permission error
          console.error('Insufficient permissions');
          break;
        case 429:
          // Handle rate limit
          const retryAfter = error.retry_after || 60;
          await delay(retryAfter * 1000);
          return makeApiCall();
        default:
          throw new Error(error.message);
      }
    }

    return await response.json();
  } catch (error) {
    console.error('API Error:', error);
    throw error;
  }
}

Python

import requests
import time

def make_api_call():
    try:
        response = requests.get(
            'https://api.posthoot.com/api/v1/campaigns',
            headers={'Authorization': f'Bearer {token}'}
        )
        
        if response.status_code == 401:
            # Handle authentication error
            refresh_token()
            return make_api_call()
        elif response.status_code == 429:
            # Handle rate limit
            error = response.json()
            retry_after = error.get('retry_after', 60)
            time.sleep(retry_after)
            return make_api_call()
        elif response.status_code >= 400:
            # Handle other errors
            error = response.json()
            raise Exception(error['message'])
            
        return response.json()
        
    except requests.exceptions.RequestException as e:
        print(f"Request error: {e}")
        raise

cURL

# Check for errors in response
response=$(curl -s -w "%{http_code}" \
  -H "Authorization: Bearer $TOKEN" \
  https://api.posthoot.com/api/v1/campaigns)

http_code="${response: -3}"
body="${response%???}"

if [ "$http_code" -eq 401 ]; then
    echo "Authentication error: $body"
elif [ "$http_code" -eq 429 ]; then
    echo "Rate limit exceeded: $body"
elif [ "$http_code" -ge 400 ]; then
    echo "Error $http_code: $body"
else
    echo "Success: $body"
fi

🔄 Retry Strategies

Exponential Backoff

async function retryWithBackoff(fn, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fn();
    } catch (error) {
      if (error.status === 429) {
        const retryAfter = error.retry_after || Math.pow(2, i) * 1000;
        await delay(retryAfter);
        continue;
      }
      throw error;
    }
  }
  throw new Error('Max retries exceeded');
}

Circuit Breaker

class CircuitBreaker {
  constructor(failureThreshold = 5, timeout = 60000) {
    this.failureThreshold = failureThreshold;
    this.timeout = timeout;
    this.failures = 0;
    this.lastFailure = null;
    this.state = 'CLOSED';
  }

  async call(fn) {
    if (this.state === 'OPEN') {
      if (Date.now() - this.lastFailure > this.timeout) {
        this.state = 'HALF_OPEN';
      } else {
        throw new Error('Circuit breaker is OPEN');
      }
    }

    try {
      const result = await fn();
      this.onSuccess();
      return result;
    } catch (error) {
      this.onFailure();
      throw error;
    }
  }

  onSuccess() {
    this.failures = 0;
    this.state = 'CLOSED';
  }

  onFailure() {
    this.failures++;
    this.lastFailure = Date.now();
    
    if (this.failures >= this.failureThreshold) {
      this.state = 'OPEN';
    }
  }
}

📊 Error Monitoring

Log Errors

function logError(error, context = {}) {
  console.error({
    timestamp: new Date().toISOString(),
    error: error.message,
    status: error.status,
    code: error.code,
    context,
    stack: error.stack
  });
}

Error Tracking

// Send errors to monitoring service
function trackError(error) {
  fetch('/api/errors', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      error: error.message,
      status: error.status,
      code: error.code,
      userAgent: navigator.userAgent,
      url: window.location.href
    })
  });
}

🛡️ Best Practices

1. Always Check Status Codes

if (!response.ok) {
  const error = await response.json();
  throw new Error(error.message);
}

2. Handle Specific Error Types

switch (error.code) {
  case 'INVALID_TOKEN':
    await refreshToken();
    break;
  case 'RATE_LIMIT_EXCEEDED':
    await delay(error.retry_after * 1000);
    break;
  default:
    console.error('Unknown error:', error);
}

3. Provide User-Friendly Messages

const errorMessages = {
  'VALIDATION_ERROR': 'Please check your input and try again',
  'INVALID_TOKEN': 'Please log in again',
  'RATE_LIMIT_EXCEEDED': 'Too many requests. Please wait a moment.',
  'INSUFFICIENT_PERMISSIONS': 'You don\'t have permission to perform this action'
};

const userMessage = errorMessages[error.code] || error.message;

4. Implement Proper Logging

function handleError(error, context) {
  // Log for debugging
  console.error('API Error:', { error, context });
  
  // Show user-friendly message
  showUserMessage(getUserMessage(error));
  
  // Track for monitoring
  trackError(error);
}