Skip to main content
Understanding API error responses and implementing proper error handling is crucial for building reliable integrations with the Streamforge External API.

HTTP Status Codes

The API uses standard HTTP status codes to indicate request outcomes:
CodeMeaningWhen It Occurs
200SuccessRequest completed successfully
400Bad RequestInvalid parameters, missing required fields, or validation errors
401UnauthorizedMissing x-api-key header
403ForbiddenInvalid or revoked API key
404Not FoundResource doesn’t exist (profile, content, game)
429Too Many RequestsRate limit exceeded
5xxServer ErrorInternal server issues (retry with backoff)

Error Response Format

All error responses follow a consistent structure:
{
  "error": {
    "status": 404,
    "message": "Profile not found"
  },
  "meta": {
    "request_id": "5e7c0137-1ed3-4b4b-8e5f-3d9cf00f7ac4"
  }
}
The request_id can be used when contacting support about specific errors.

Handling Specific Errors

400 Bad Request

Invalid parameters or malformed requests:
if (response.status === 400) {
  const error = await response.json();
  console.error('Validation error:', error.error.message);
  // Check your request parameters
}
Common causes:
  • Invalid platform value (must be twitch, youtube, or tiktok)
  • limit out of range (must be 1-50)
  • Missing required fields in bulk requests
  • Invalid date format in query parameters

401 Unauthorized

Missing API key:
if (response.status === 401) {
  console.error('API key is missing');
  // Add x-api-key header to your request
}
Solution: Ensure all requests include the x-api-key header.

403 Forbidden

Invalid or revoked API key:
if (response.status === 403) {
  console.error('API key is invalid or revoked');
  // Contact [email protected] for a new key
}
Common causes:
  • Typo in API key
  • Key was rotated or revoked
  • Key is inactive

404 Not Found

Resource doesn’t exist:
if (response.status === 404) {
  const error = await response.json();
  // Resource doesn't exist - this is normal for some use cases
  // Handle gracefully (e.g., skip, log, or retry later)
}
Note: 404s are normal in some scenarios:
  • Profile IDs that don’t exist
  • Content that was deleted
  • Games not in the database
Handle 404s gracefully rather than treating them as errors.

429 Too Many Requests

Rate limit exceeded:
if (response.status === 429) {
  const retryAfter = response.headers.get('Retry-After');
  const seconds = parseInt(retryAfter, 10);

  // Wait before retrying
  await sleep(seconds * 1000);
  // Retry the request
}
Response headers:
  • Retry-After: Seconds to wait before retrying
  • X-RateLimit-*-remaining: Remaining quota (often 0)

Retry Strategies

Exponential Backoff

Implement exponential backoff for retryable errors (429, 5xx):
async function fetchWithRetry(url, options, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const response = await fetch(url, options);

    if (response.ok) {
      return response;
    }

    // Don't retry 4xx errors (except 429)
    if (response.status >= 400 && response.status < 500 && response.status !== 429) {
      throw new Error(`Client error: ${response.status}`);
    }

    // Calculate backoff delay
    const delay = Math.min(1000 * Math.pow(2, attempt), 30000);

    // For 429, use Retry-After header
    if (response.status === 429) {
      const retryAfter = response.headers.get('Retry-After');
      if (retryAfter) {
        await sleep(parseInt(retryAfter, 10) * 1000);
        continue;
      }
    }

    await sleep(delay);
  }

  throw new Error('Max retries exceeded');
}

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

Handling Bulk Operation Errors

Bulk operations use partial success - some items may be missing:
async function handleBulkResponse(response) {
  const data = await response.json();

  if (response.ok) {
    // Check for missing items
    if (data.meta.missing_ids && data.meta.missing_ids.length > 0) {
      console.warn(`Missing items: ${data.meta.missing_ids.join(', ')}`);
      // Handle missing items (e.g., log, retry individually, or skip)
    }

    return data.payload;
  } else {
    // Handle full request failure
    throw new Error(`Bulk request failed: ${data.error.message}`);
  }
}

Best Practices

Always check response status before parsing JSON. Some errors may not return JSON bodies.
  • Check status first: Verify response.ok or response.status before parsing JSON
  • Handle 404s gracefully: Missing resources are normal; don’t treat as errors
  • Respect Retry-After: For 429 errors, wait the specified time before retrying
  • Log request IDs: Include request_id from error responses in your logs
  • Monitor error rates: Track error rates to detect issues early
  • Implement circuit breakers: Stop retrying if error rate is too high

Example: Complete Error Handler

async function apiRequest(url, options) {
  try {
    const response = await fetch(url, {
      ...options,
      headers: {
        'x-api-key': process.env.API_KEY,
        'Content-Type': 'application/json',
        ...options.headers
      }
    });

    // Handle rate limiting
    if (response.status === 429) {
      const retryAfter = response.headers.get('Retry-After');
      throw new RateLimitError(parseInt(retryAfter, 10));
    }

    // Parse response
    const data = await response.json();

    if (!response.ok) {
      throw new ApiError(data.error.message, response.status, data.meta.request_id);
    }

    return data;
  } catch (error) {
    if (error instanceof RateLimitError) {
      // Handle rate limit
      await sleep(error.retryAfter * 1000);
      return apiRequest(url, options); // Retry
    }
    throw error;
  }
}

class ApiError extends Error {
  constructor(message, status, requestId) {
    super(message);
    this.status = status;
    this.requestId = requestId;
  }
}

class RateLimitError extends Error {
  constructor(retryAfter) {
    super('Rate limit exceeded');
    this.retryAfter = retryAfter;
  }
}
Don’t retry 4xx errors (except 429) - they indicate client errors that won’t be fixed by retrying.