Skip to main content

Overview

The Mighty Networks API uses standard HTTP status codes to indicate the success or failure of requests. Proper error handling is essential for building robust integrations.

HTTP Status Codes

Status CodeMeaningDescription
200OKRequest succeeded
201CreatedResource created successfully
204No ContentRequest succeeded with no response body
400Bad RequestInvalid parameters or malformed request
401UnauthorizedMissing or invalid API token
403ForbiddenValid token but insufficient permissions
404Not FoundResource doesn’t exist
409ConflictResource conflict (e.g., duplicate)
422Unprocessable EntityValidation error
429Too Many RequestsRate limit exceeded
500Internal Server ErrorServer-side error
502Bad GatewayTemporary server issue
503Service UnavailableService temporarily down

Error Response Format

All errors return a JSON response with consistent structure:
{
  "error": "error_code",
  "message": "Human-readable error description",
  "status": 401
}

Example Error Responses

Authentication Error (401)

{
  "error": "unauthorized",
  "message": "Invalid API token provided",
  "status": 401
}

Not Found Error (404)

{
  "error": "not_found",
  "message": "Member not found",
  "status": 404
}

Validation Error (422)

{
  "error": "validation_error",
  "message": "Invalid role value. Must be one of: admin, host, member",
  "status": 422
}

Common Error Types

1. Authentication Errors (401)

Causes:
  • Missing Authorization header
  • Invalid or expired API token
  • Malformed Bearer token
Example:
const response = await fetch(
  `https://api.mn.co/admin/v1/networks/${NETWORK_ID}/members`,
  {
    headers: { 'Authorization': 'Bearer INVALID_TOKEN' }
  }
);

// Response: 401 Unauthorized
How to Handle:
if (response.status === 401) {
  console.error('Authentication failed. Check your API token.');
  // Refresh token or prompt user to re-authenticate
}

2. Permission Errors (403)

Causes:
  • Token lacks required scopes/permissions
  • Attempting to access resources outside your network
  • Admin-only operation with non-admin token
Example:
// Token doesn't have 'members:write' permission
const response = await fetch(
  `https://api.mn.co/admin/v1/networks/${NETWORK_ID}/members/${MEMBER_ID}/`,
  {
    method: 'PATCH',
    headers: {
      'Authorization': `Bearer ${API_TOKEN}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ role: 'admin' })
  }
);

// Response: 403 Forbidden
How to Handle:
if (response.status === 403) {
  console.error('Insufficient permissions for this operation');
  // Check token scopes or request elevated permissions
}

3. Resource Not Found (404)

Causes:
  • Invalid resource ID
  • Resource was deleted
  • Incorrect network ID
  • Typo in endpoint URL
How to Handle:
const response = await fetch(
  `https://api.mn.co/admin/v1/networks/${NETWORK_ID}/members/${MEMBER_ID}/`,
  { headers: { 'Authorization': `Bearer ${API_TOKEN}` } }
);

if (response.status === 404) {
  console.error('Member not found. They may have been deleted.');
  // Handle missing resource gracefully
  return null;
}

4. Validation Errors (422)

Causes:
  • Invalid field values
  • Missing required fields
  • Data format errors
How to Handle:
const response = await fetch(
  `https://api.mn.co/admin/v1/networks/${NETWORK_ID}/members/${MEMBER_ID}/`,
  {
    method: 'PATCH',
    headers: {
      'Authorization': `Bearer ${API_TOKEN}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ role: 'invalid_role' })
  }
);

if (response.status === 422) {
  const error = await response.json();
  console.error('Validation error:', error.message);
  // Display validation errors to user
}

5. Rate Limit Errors (429)

Causes:
  • Exceeded requests per minute limit
  • Too many requests in short time
How to Handle: See the Rate Limits guide for detailed handling strategies.
if (response.status === 429) {
  const retryAfter = response.headers.get('Retry-After') || 60;
  console.log(`Rate limited. Retrying in ${retryAfter}s`);
  await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
  // Retry request
}

Error Handling Patterns

Basic Error Handling

async function fetchMember(networkId, memberId, apiToken) {
  try {
    const response = await fetch(
      `https://api.mn.co/admin/v1/networks/${networkId}/members/${memberId}/`,
      { headers: { 'Authorization': `Bearer ${apiToken}` } }
    );

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

    return await response.json();
  } catch (error) {
    console.error('Failed to fetch member:', error);
    throw error;
  }
}

Comprehensive Error Handler

async function makeAPIRequest(url, options = {}) {
  try {
    const response = await fetch(url, options);

    // Handle different status codes
    switch (response.status) {
      case 200:
      case 201:
      case 204:
        return await response.json().catch(() => ({}));

      case 400:
        const badRequest = await response.json();
        throw new Error(`Bad Request: ${badRequest.message}`);

      case 401:
        throw new Error('Authentication failed. Please check your API token.');

      case 403:
        throw new Error('Permission denied. Check your token permissions.');

      case 404:
        throw new Error('Resource not found.');

      case 409:
        const conflict = await response.json();
        throw new Error(`Conflict: ${conflict.message}`);

      case 422:
        const validation = await response.json();
        throw new Error(`Validation Error: ${validation.message}`);

      case 429:
        const retryAfter = response.headers.get('Retry-After') || 60;
        throw new Error(`Rate limited. Retry after ${retryAfter}s`);

      case 500:
      case 502:
      case 503:
        throw new Error('Server error. Please try again later.');

      default:
        throw new Error(`Unexpected error: ${response.status}`);
    }
  } catch (error) {
    console.error('API request failed:', error);
    throw error;
  }
}

Custom Error Classes

class APIError extends Error {
  constructor(status, message, code) {
    super(message);
    this.name = 'APIError';
    this.status = status;
    this.code = code;
  }
}

class AuthenticationError extends APIError {
  constructor(message) {
    super(401, message, 'authentication_error');
    this.name = 'AuthenticationError';
  }
}

class NotFoundError extends APIError {
  constructor(resource) {
    super(404, `${resource} not found`, 'not_found');
    this.name = 'NotFoundError';
  }
}

class RateLimitError extends APIError {
  constructor(retryAfter) {
    super(429, 'Rate limit exceeded', 'rate_limit_exceeded');
    this.name = 'RateLimitError';
    this.retryAfter = retryAfter;
  }
}

async function fetchWithCustomErrors(url, options) {
  const response = await fetch(url, options);

  if (response.ok) {
    return await response.json();
  }

  const error = await response.json();

  switch (response.status) {
    case 401:
      throw new AuthenticationError(error.message);
    case 404:
      throw new NotFoundError('Resource');
    case 429:
      const retryAfter = response.headers.get('Retry-After');
      throw new RateLimitError(retryAfter);
    default:
      throw new APIError(response.status, error.message, error.error);
  }
}

// Usage
try {
  const member = await fetchWithCustomErrors(url, options);
} catch (error) {
  if (error instanceof AuthenticationError) {
    console.log('Please re-authenticate');
  } else if (error instanceof NotFoundError) {
    console.log('Resource was deleted');
  } else if (error instanceof RateLimitError) {
    console.log(`Wait ${error.retryAfter}s before retrying`);
  }
}

Retry Logic

async function fetchWithRetry(url, options, maxRetries = 3) {
  let lastError;

  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      const response = await fetch(url, options);

      // Don't retry on client errors (except 429)
      if (response.status >= 400 && response.status < 500 && response.status !== 429) {
        const error = await response.json();
        throw new Error(error.message);
      }

      // Retry on server errors and rate limits
      if (!response.ok) {
        const waitTime = response.status === 429
          ? parseInt(response.headers.get('Retry-After') || '60') * 1000
          : Math.pow(2, attempt) * 1000;

        console.log(`Request failed (attempt ${attempt + 1}/${maxRetries}). Retrying in ${waitTime}ms`);
        await new Promise(resolve => setTimeout(resolve, waitTime));
        continue;
      }

      return await response.json();
    } catch (error) {
      lastError = error;
      if (attempt === maxRetries - 1) {
        throw error;
      }
    }
  }

  throw lastError;
}

Python Error Handling

Basic Pattern

import requests

def fetch_member(network_id, member_id, api_token):
    try:
        response = requests.get(
            f"https://api.mn.co/admin/v1/networks/{network_id}/members/{member_id}/",
            headers={"Authorization": f"Bearer {api_token}"}
        )

        response.raise_for_status()
        return response.json()

    except requests.exceptions.HTTPError as e:
        if e.response.status_code == 401:
            print("Authentication failed")
        elif e.response.status_code == 404:
            print("Member not found")
        elif e.response.status_code == 429:
            print("Rate limit exceeded")
        else:
            print(f"HTTP error: {e}")
        raise

    except requests.exceptions.RequestException as e:
        print(f"Request failed: {e}")
        raise

Custom Exception Classes

class APIException(Exception):
    def __init__(self, status_code, message, error_code):
        self.status_code = status_code
        self.message = message
        self.error_code = error_code
        super().__init__(self.message)

class AuthenticationException(APIException):
    def __init__(self, message):
        super().__init__(401, message, 'authentication_error')

class NotFoundException(APIException):
    def __init__(self, resource):
        super().__init__(404, f"{resource} not found", 'not_found')

def make_request(url, headers):
    response = requests.get(url, headers=headers)

    if response.ok:
        return response.json()

    error_data = response.json()

    if response.status_code == 401:
        raise AuthenticationException(error_data.get('message'))
    elif response.status_code == 404:
        raise NotFoundException('Resource')
    else:
        raise APIException(
            response.status_code,
            error_data.get('message'),
            error_data.get('error')
        )

Retry with Backoff

import time
from functools import wraps

def retry_with_backoff(max_retries=3):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            for attempt in range(max_retries):
                try:
                    response = func(*args, **kwargs)

                    if response.status_code >= 500 or response.status_code == 429:
                        if attempt < max_retries - 1:
                            wait_time = 2 ** attempt
                            print(f"Retry {attempt + 1}/{max_retries} in {wait_time}s")
                            time.sleep(wait_time)
                            continue

                    return response

                except Exception as e:
                    if attempt == max_retries - 1:
                        raise
                    time.sleep(2 ** attempt)

            return response

        return wrapper
    return decorator

@retry_with_backoff(max_retries=5)
def make_api_call(url, headers):
    return requests.get(url, headers=headers)

Best Practices

  1. Always Check Status Codes - Don’t assume requests succeed
  2. Parse Error Messages - Extract meaningful information from error responses
  3. Implement Retries - Retry transient errors (5xx, 429)
  4. Don’t Retry 4xx Errors - Client errors won’t succeed on retry (except 429)
  5. Log Errors - Keep detailed error logs for debugging
  6. Handle Gracefully - Provide user-friendly error messages
  7. Monitor Errors - Track error rates to identify issues
  8. Validate Input - Catch errors before making API calls

Common Pitfalls

❌ Ignoring Status Codes

// Bad: Assumes success
const data = await response.json();

✅ Check Response Status

// Good: Verify success
if (!response.ok) {
  throw new Error(`HTTP ${response.status}`);
}
const data = await response.json();

❌ Generic Error Messages

// Bad: Unhelpful error
catch (error) {
  console.log('Error occurred');
}

✅ Specific Error Handling

// Good: Actionable errors
catch (error) {
  if (error.status === 404) {
    console.log('Member was deleted');
  } else {
    console.log(`Failed: ${error.message}`);
  }
}

Next Steps