Skip to main content
Every non-2xx API response is re-thrown as a typed error. Import from the package root:
import { SpekoApiError, SpekoAuthError, SpekoRateLimitError } from '@spekoai/sdk';

SpekoApiError

Base class for all API errors. Carries HTTP status plus a server-provided code.
class SpekoApiError extends Error {
  status: number;
  code: string;
}
Thrown on any non-2xx response that isn’t a 401 or 429. code is parsed from the JSON body ({ "error": "...", "code": "..." }); falls back to "UNKNOWN" when the body isn’t JSON.

SpekoAuthError

Thrown on HTTP 401. Extends SpekoApiError with status: 401, code: 'AUTH_ERROR'.
try {
  await speko.complete({ ... });
} catch (err) {
  if (err instanceof SpekoAuthError) {
    // Prompt the user to rotate their API key.
  }
}

SpekoRateLimitError

Thrown on HTTP 429. Extends SpekoApiError with code: 'RATE_LIMITED' and a parsed retryAfter (seconds) from the Retry-After response header.
class SpekoRateLimitError extends SpekoApiError {
  retryAfter: number | null;
}
Example backoff:
async function withRetry<T>(fn: () => Promise<T>): Promise<T> {
  try {
    return await fn();
  } catch (err) {
    if (err instanceof SpekoRateLimitError) {
      const wait = (err.retryAfter ?? 1) * 1000;
      await new Promise((r) => setTimeout(r, wait));
      return fn();
    }
    throw err;
  }
}

Timeouts and cancellation

When the internal timeout fires or an external AbortSignal is aborted, fetch rejects with a DOMException named AbortError. The SDK does not re-wrap these — callers can distinguish abort from API failure with a standard instanceof / .name === 'AbortError' check.