Error Handling

Guide

Handle API errors gracefully with typed exceptions and rate-limit retries. Examples shown for both Node.js and Python.

Error Codes Reference

CodeHTTPDescriptionNode.jsPython
invalid_request400Request parameters are invalidBearLumenApiErrorInvalidRequestError
authentication_error401API key is invalid or missingBearLumenApiErrorAuthenticationError
not_found404Requested resource does not existBearLumenApiErrorNotFoundError
rate_limit_exceeded429Too many requests, retry after delayBearLumenApiErrorRateLimitError
server_error500Internal server errorBearLumenApiErrorBearLumenError
network_error-Network connectivity issueBearLumenApiErrorNetworkError

Basic Error Handling

Use instanceof checks with BearLumenApiError and switch on the error.code property:

TypeScript
import { BearLumen, BearLumenApiError } from '@bearlumen/node-sdk';

try {
  const margins = await bear.margins.summary({
    startDate: '2026-01-01',
    endDate: '2026-01-31',
  });
} catch (error) {
  if (error instanceof BearLumenApiError) {
    switch (error.code) {
      case 'authentication_error':
        console.error('Invalid API key');
        break;
      case 'rate_limit_exceeded':
        console.error('Rate limited, retry after', error.retryAfter, 'seconds');
        break;
      case 'not_found':
        console.error('Resource not found');
        break;
      default:
        console.error('Error', error.code, error.message);
    }
    console.error('Request ID:', error.correlationId); // include in support tickets
  }
}

Rate Limit Handling

When you hit a rate limit, the error.retryAfter property tells you how long to wait. Here is a simple retry pattern with exponential backoff:

TypeScript
import { BearLumenApiError } from '@bearlumen/node-sdk';

async function withRetry<T>(
  fn: () => Promise<T>,
  maxRetries = 3,
): Promise<T> {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      return await fn();
    } catch (error) {
      if (
        error instanceof BearLumenApiError &&
        error.code === 'rate_limit_exceeded' &&
        attempt < maxRetries
      ) {
        const delay = error.retryAfter
          ? error.retryAfter * 1000
          : Math.pow(2, attempt) * 1000;
        console.log('Rate limited. Retrying in', delay, 'ms...');
        await new Promise((resolve) => setTimeout(resolve, delay));
        continue;
      }
      throw error;
    }
  }
  throw new Error('Max retries exceeded');
}

// Wrap any read query that might be rate limited.
const result = await withRetry(() =>
  bear.usage.events({ startDate: '2026-01-01', endDate: '2026-01-31' })
);

Best Practices

  • 1

    Handle rate limits with exponential backoff. Respect the retryAfter / retry_after value when available, otherwise use increasing delays.

  • 2

    Flush before your process exits. track() batches events in the background, so call bear.shutdown() (or flush()) before exit, or queued events are lost.

  • 3

    Log error codes and request IDs for debugging. Error codes are stable identifiers for alerting and log analysis; the correlationId (Node.js) / request_id (Python) identifies the exact request in support tickets.

  • 4

    Use specific error types over generic catch-all. In Python, catch specific exceptions like RateLimitError before BearLumenError. In Node.js, check error.code for specific handling.