# TypeScript SDK (/docs/sdks/typescript)

The orakle npm package — typed pricing jobs over SSE, with streaming callbacks and AbortSignal support.



Published as `orakle` on npm. Ships ESM and CJS. Requires Node.js 22+ (uses
the global `fetch` and `AbortController`) and works in modern browsers.

## Install [#install]

<Tabs items="[&#x22;pnpm&#x22;, &#x22;npm&#x22;]">
  <Tab value="pnpm">
    ```bash
    pnpm add orakle
    ```
  </Tab>

  <Tab value="npm">
    ```bash
    npm install orakle
    ```
  </Tab>
</Tabs>

## Instantiate [#instantiate]

```ts
import { OrakleClient } from "orakle";

const orakle = new OrakleClient({ apiKey: process.env.ORAKLE_API_KEY! });
```

<TypeTable
  type="{
  apiKey: {
    description: &#x22;Your key from the dashboard. Sent as X-Orakle-API-Key.&#x22;,
    type: &#x22;string&#x22;,
  },
  baseUrl: {
    description: &#x22;API base URL. Must be https://.&#x22;,
    type: &#x22;string&#x22;,
    default: '&#x22;https://api.orakle.xyz&#x22;',
  },
  timeoutSec: {
    description: &#x22;Per-job timeout in seconds. Minimum 240.&#x22;,
    type: &#x22;number&#x22;,
    default: &#x22;240&#x22;,
  },
  maxLineBytes: {
    description: &#x22;Maximum bytes per SSE line accepted by the parser.&#x22;,
    type: &#x22;number&#x22;,
    default: &#x22;5 MiB&#x22;,
  },
  maxEventBytes: {
    description: &#x22;Byte cap per SSE event (summed across multi-line data fields).&#x22;,
    type: &#x22;number&#x22;,
    default: &#x22;10 MiB&#x22;,
  },
}"
/>

## priceCheck [#pricecheck]

Submits a job, opens the SSE stream, and resolves with the final result.
Rejects with an [`OrakleError`](#errors) of `kind: "job_failed"` on a
`failed` event.

<CodeBlockTabs defaultValue="Natural language">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="Natural language">
      Natural language
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="Structured">
      Structured
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="Natural language">
    ```ts
    const result = await orakle.priceCheck({
      request_type: "nl",
      query: "iPhone 15 Pro 256GB unlocked mint",
      geography: "US",
    });

    console.log(result.fused_distribution.quantiles);
    // { "0.05": 740, "0.1": 780, "0.25": 820, "0.5": 870, "0.75": 920, "0.9": 960, "0.95": 1010 }
    ```
  </CodeBlockTab>

  <CodeBlockTab value="Structured">
    ```ts
    const result = await orakle.priceCheck({
      request_type: "structured",
      product: {
        category: "smartphone",
        make: "Apple",
        model: "iPhone 15 Pro",
        condition: "mint",
        attributes: { storage: "256GB", carrier_lock: "unlocked" },
      },
      geography: "US",
    });
    ```
  </CodeBlockTab>
</CodeBlockTabs>

`geography` is one of `"US" | "GB" | "CA" | "AU" | "AE"` and is optional.
`webhook_url` is also accepted on both request shapes.

## priceCheckStream [#pricecheckstream]

Yields every [`JobEvent`](/docs/api/events) as it arrives — `stage`, `partial`,
then a terminal `completed` or `failed` — and ends after the terminal event.

```ts
for await (const event of orakle.priceCheckStream({
  request_type: "nl",
  query: "PS5 Disc Edition",
})) {
  if (event.event_type === "stage") {
    console.log(event.stage, event.message);
  }
  if (event.event_type === "completed") {
    console.log(event.result.fused_distribution.quantiles);
  }
  if (event.event_type === "failed") {
    console.error(event.code, event.message);
  }
}
```

`priceCheckStream` does not throw on a `failed` event — it yields it and
returns. It still throws on transport, timeout, and HTTP errors.

## Options [#options]

Both methods take an optional second argument:

<TypeTable
  type="{
  signal: {
    description: &#x22;AbortSignal to cancel the job. Aborting rejects with the signal's reason.&#x22;,
    type: &#x22;AbortSignal&#x22;,
  },
  timeoutSec: {
    description: &#x22;Override the client timeout for this call, in seconds. Minimum 240.&#x22;,
    type: &#x22;number&#x22;,
  },
  onProgress: {
    description: &#x22;Called for each stage event as the pipeline advances.&#x22;,
    type: &#x22;(event: StageEvent) => void&#x22;,
  },
  onPartial: {
    description: &#x22;Fires once with the analyzed product profile right after the Analyzing stage, before the full result.&#x22;,
    type: &#x22;(event: PartialEvent) => void&#x22;,
  },
}"
/>

```ts
const result = await orakle.priceCheck(
  { request_type: "nl", query: "MacBook Air M3 16GB" },
  {
    timeoutSec: 300,
    onProgress: (e) => console.log(`[${e.stage}] ${e.message}`),
    onPartial: (e) => renderPlaceholder(e.result.product_profile),
  },
);
```

## Cancellation [#cancellation]

Pass an `AbortSignal` to cancel an in-flight job. The promise rejects with the
signal's abort reason — a plain `DOMException`, distinct from the SDK's own
`OrakleError` of `kind: "timeout"`.

```ts
const controller = new AbortController();
setTimeout(() => controller.abort(), 10_000);

try {
  await orakle.priceCheck(
    { request_type: "nl", query: "..." },
    { signal: controller.signal },
  );
} catch (err) {
  if (err instanceof DOMException && err.name === "AbortError") {
    console.log("Cancelled by caller");
  }
}
```

## Errors [#errors]

Every SDK failure is a single `OrakleError` class carrying a `kind` literal
and optional per-kind fields. Branch with the exported predicates — they
narrow `status`, `code`, `timeoutSec`, and `cause` without casts.

| `kind`       | Predicate                | When                                                           | Extra fields     |
| ------------ | ------------------------ | -------------------------------------------------------------- | ---------------- |
| `http`       | `isHttpError(err)`       | Non-2xx HTTP response — the job never started.                 | `status`, `code` |
| `job_failed` | `isJobFailedError(err)`  | Job was accepted but failed in the pipeline (`failed` SSE).    | `code`           |
| `timeout`    | `isTimeoutError(err)`    | The job exceeded the effective timeout.                        | `timeoutSec`     |
| `connection` | `isConnectionError(err)` | Network, DNS, or transport failure.                            | `cause`          |
| `config`     | `isConfigError(err)`     | Caller misconfigured the client (bad `baseUrl`, bad byte cap). | —                |
| `protocol`   | `isProtocolError(err)`   | Server/transport violated the SSE contract.                    | —                |

```ts
import {
  OrakleError,
  isHttpError,
  isJobFailedError,
  isTimeoutError,
  isConnectionError,
  isConfigError,
  isProtocolError,
} from "orakle";

try {
  await orakle.priceCheck({ request_type: "nl", query: "..." });
} catch (err) {
  if (!(err instanceof OrakleError)) throw err;
  // Predicates (not `switch (err.kind)`) so `status`, `code`, `timeoutSec`, and
  // `cause` narrow to non-optional inside each branch without `!` or `as`.
  if (isHttpError(err)) console.error(err.code, err.status);
  else if (isJobFailedError(err)) console.error(err.code);
  else if (isTimeoutError(err)) console.error(err.timeoutSec);
  else if (isConnectionError(err)) console.error(err.cause);
  else if (isConfigError(err) || isProtocolError(err)) console.error(err.message);
}
```

The `http` and `job_failed` kinds share the same `ErrorCode` taxonomy:
`invalid_request`, `auth_required`, `rate_limited`, `job_not_found`,
`insufficient_data`, `internal_error`, `service_unavailable`.

The raw API and MCP surfaces use the same public error-code taxonomy.

## Result shape [#result-shape]

`priceCheck` resolves with a `PriceCheckResult`:

<TypeTable
  type="{
  product_profile: {
    description: &#x22;The normalized product Orakle priced.&#x22;,
    type: &#x22;ProductProfile&#x22;,
  },
  fused_distribution: {
    description: &#x22;The calibrated price distribution: quantiles + the market currency.&#x22;,
    type: &#x22;PriceDistribution&#x22;,
  },
  scenario_forecast: {
    description: &#x22;Scenario-weighted forward view. May be null.&#x22;,
    type: &#x22;ScenarioForecast | null&#x22;,
  },
  forecast_series: {
    description: &#x22;Historical + forecast price points for charting.&#x22;,
    type: &#x22;ForecastPoint[]&#x22;,
  },
  forecast_narrative: {
    description: &#x22;Prose summary of the forward view.&#x22;,
    type: &#x22;string&#x22;,
  },
  events: {
    description: &#x22;Dated market catalysts relevant to the price.&#x22;,
    type: &#x22;PricingEvent[]&#x22;,
  },
  token_usage: {
    description: &#x22;Usage for the request. Drives metered billing; may be absent.&#x22;,
    type: &#x22;number&#x22;,
  },
}"
/>

The SSE primitive `createSSEStream` is also exported for server-side proxies
that re-emit a sanitized stream to the browser.