# TypeScript SDK

`@fathom-charts/sdk` handles authentication, the initial snapshot, resuming after a disconnect, duplicates, history pagination, exports and rate-limit retries for you. This page describes every option and method; the behaviour the four SDKs share is in [SDKs](https://fathomcharts.com/docs/sdk.md).

## Install

```bash
npm install @fathom-charts/sdk ws
```

- **ESM only**: the package ships no CommonJS build. Your project must declare `"type": "module"` in its `package.json` (or use `.mjs` / `.mts` files).
- **Node 22 or later**, and browsers. `exportNdjson()` requires Node 22.15 or later (zstd decompression from `node:zlib`).
- **`ws`** is only needed in Node: it opens the connection with the `Authorization` header. In a browser, the SDK uses the native `WebSocket` with a [stream token](https://fathomcharts.com/docs/authentication.md#browser-access), and never imports `ws`.

The SDK imports `ws` dynamically, with the module name in a variable, so that browser bundlers leave it out. Webpack then reports `Critical dependency: the request of a dependency is an expression`: this warning is harmless.

### Indicator types

The SDK is generic: an indicator’s data is typed by the types you generate from the catalog (`GET /v1/indicators`, no authentication).

```bash
curl "https://api.fathomcharts.com/v1/indicators" -o catalog.json
npx fathom-charts-types catalog.json src/fathom-charts-types.ts
```

The generated file exports, for each indicator, `<Name>Params` and `<Name>Data`, named after its id (`big-trades` → `BigTradesParams`, `BigTradesData`), plus `IndicatorParams`, `IndicatorData` and `IndicatorId`. The catalog descriptions become JSDoc comments. The generator also takes the catalog URL directly: `npx fathom-charts-types https://api.fathomcharts.com/v1/indicators src/fathom-charts-types.ts`; a footprint level is typed `[number, number, number, number?]` (the 4th element is optional). The output folder is created if it does not exist. Generate the file again when the catalog changes (its `engineFingerprint` changes with the computation).

## Real time: FathomChartsStream

```ts
import { FathomChartsStream } from '@fathom-charts/sdk';
import type { BigTradesData } from './fathom-charts-types.js';

const stream = new FathomChartsStream({
  url: 'wss://stream.fathomcharts.com/v1',
  apiKey: process.env.FATHOM_CHARTS_API_KEY!,
  onStateChange: (state) => console.log('connection', state),
});

const sub = stream.subscribe<BigTradesData>({
  instrument: 'NQ.front',
  indicator: 'big-trades',
  params: { minimum: 30 },
  mode: 'confirmed',
});

for await (const event of sub) {
  if (event.type === 'upsert') console.log(event.cursor, event.data.side, event.data.volume);
}
```

### Options

| Option               | Purpose                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `url`                | Required. The stream address: `wss://stream.fathomcharts.com/v1`.                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `apiKey`             | API key, sent in the `Authorization: Bearer` header. Server-side only; requires the `ws` package unless you provide `webSocket`.                                                                                                                                                                                                                                                                                                                                                                                      |
| `streamToken`        | In a browser, instead of `apiKey`: an `async () => string` function that asks your server for a stream token. It is called on every connection, reconnections included. Give exactly one of `apiKey` and `streamToken`, otherwise the constructor throws `CONFIGURATION`.                                                                                                                                                                                                                                             |
| `webSocket`          | Optional: a `(url, headers) => WebSocket` function that opens the connection, to use an implementation other than `ws` or the browser’s `WebSocket`. `headers` holds the `Authorization` header with `apiKey`, `undefined` with `streamToken`.                                                                                                                                                                                                                                                                        |
| `reconnect`          | Optional: `initialDelayMs` (first reconnection delay, 250 ms by default), `maxDelayMs` (backoff ceiling, 30 s), `quotaRetryMs` (how long a `4029` close code is retried, 120 s), `quotaDelayMs` (minimum delay between two attempts after a `4029`, 5 s).                                                                                                                                                                                                                                                             |
| `heartbeatTimeoutMs` | Silence after which the connection is considered lost, then reopened. 15,000 ms by default: the server sends an `hb` every 5 seconds.                                                                                                                                                                                                                                                                                                                                                                                 |
| `highWaterMark`      | Number of events waiting to be read, all subscriptions together, above which the SDK pauses reading the socket (with `ws`) and keeps only the latest version of each object in progress. While paused, the SDK sends `ping` messages (every third of `heartbeatTimeoutMs`, 5 s by default) so that the server keeps the connection. 10,000 by default.                                                                                                                                                                |
| `maxBuffered`        | When the socket cannot be paused (browsers, or Node’s built-in `WebSocket`): number of waiting events above which the SDK closes the connection (close code `4000`, reason `client buffer full`), then reopens it from the last cursor received once half of `highWaterMark` has been read. Nothing waiting is lost, and the resume follows the rules below: no loss when it is served by the stream’s memory or by the history your rights cover, otherwise `reset` then a snapshot. 4 × `highWaterMark` by default. |
| `random`             | Optional, for tests: the source of the reconnection jitter, a function returning a number in \[0, 1). `Math.random` by default.                                                                                                                                                                                                                                                                                                                                                                                       |
| `onStateChange`      | Called on every change of `connectionState`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `onNotice`           | Called with each `notice` message from the server (`{ kind, id?, effectiveAt?, endsAt?, message? }`): a restart (`reconnect`, the SDK reconnects on its own), a maintenance window announced (`maintenance`) or cancelled (`maintenance-cancelled`). See [Updates and maintenance](https://fathomcharts.com/docs/maintenance.md).                                                                                                                                                                                     |

### subscribe()

`stream.subscribe<D>(options)` opens a subscription and returns a `Subscription` object, to read with `for await`. Its options are those of the [`subscribe`](https://fathomcharts.com/docs/websocket.md#subscribing) message: `instrument`, `indicator`, `params` (`{}` by default), `mode` (`live` by default) and `from` (`"live"` by default, `{ cursor, engine }` or `{ time }`). The SDK picks the `sub` name itself. The type `D` is the indicator’s data type (`BigTradesData`…).

The loop receives events whose `type` field is the name of the server message (`subscribed`, `snapshot`, `upsert`, `remove`, `status`, `reset`, `notice`): see [Events](https://fathomcharts.com/docs/sdk.md#events). An `error` on the subscription ends the loop: `for await` throws a `FathomChartsError` carrying the received `code`.

### Subscription

| Member    | Purpose                                                                                                                                                                              |
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `cursor`  | The cursor of the last event handed to you: your resume point. `undefined` until a cursor has arrived. It also moves forward with `hb` messages when no event is waiting to be read. |
| `topic`   | The `topic` of the latest `subscribed`.                                                                                                                                              |
| `engine`  | The `engine` of the latest `subscribed`: the computation your state comes from. Save it with `cursor` to resume later.                                                               |
| `id`      | The `sub` name the SDK chose for this subscription (`s1`, `s2`…).                                                                                                                    |
| `request` | The options of `subscribe()`, completed with the default values (`params`, `mode`, `from`).                                                                                          |
| `close()` | Stops the subscription: the SDK sends `unsubscribe`, and a pending loop ends.                                                                                                        |

Leaving the loop (`break`, `return` or an exception in its body) calls `close()`.

### Connection lifecycle

The connection opens on the first `subscribe()` and closes (`1000`, `idle`) when the last subscription closes; `stream.close()` closes everything, and a `subscribe()` after it throws `CLOSED`. `connectionState` is `idle`, `connecting`, `open`, `reconnecting` or `closed`. Reconnection, resuming and close codes: see [Connection lifecycle](https://fathomcharts.com/docs/sdk.md#connection-lifecycle). To resume after your process restarts, save `sub.cursor` and `sub.engine` and subscribe again with `from: { cursor, engine }`: see [Resume after a restart](https://fathomcharts.com/docs/sdk.md#resume-after-a-restart).

### Maintenance windows

`stream.maintenance` lists the maintenance windows announced on the connection and not over, by start: `{ id, startsAt, endsAt, message }` (times in nanoseconds, as strings). Every new connection receives the windows already announced. See [Updates and maintenance](https://fathomcharts.com/docs/maintenance.md).

## History: FathomChartsRest

```ts
import { FathomChartsRest } from '@fathom-charts/sdk';

const rest = new FathomChartsRest({ baseUrl: 'https://api.fathomcharts.com', apiKey: process.env.FATHOM_CHARTS_API_KEY! });
```

| Option             | Purpose                                                                                                        |
| ------------------ | -------------------------------------------------------------------------------------------------------------- |
| `baseUrl`          | Required. The API address: `https://api.fathomcharts.com`.                                                     |
| `apiKey`           | Required. API key, sent in the `Authorization: Bearer` header. Server-side only.                               |
| `fetch`            | Optional: a `fetch` implementation to use instead of the platform’s.                                           |
| `rateLimitRetries` | Number of retries of a request the server did not process, or of a read that failed (see below). 3 by default. |

### Methods

Every method that reads history takes the query described on the [History](https://fathomcharts.com/docs/history.md#make-a-query) page (`instrument`, `indicator`, `params`, `from`, `to`, `mode`, and depending on the method `limit`, `cursor`, `untilCursor`, `snapshot`), and an optional `AbortSignal`.

| Method                          | Returns                                                                                                                                                                                                                    |
| ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `page(query, signal?)`          | One page: `{ instrument, tickSizeNanos, snapshot?, items, next, computeUnits }`.                                                                                                                                           |
| `pages(query, signal?)`         | The pages one at a time, following `next`, as you read them.                                                                                                                                                               |
| `history(query, signal?)`       | The results one at a time, across pages. With `snapshot: true`, the first element is the state at `from`, `{ t: 'snapshot', cursor, items }`, then come the updates (`t` is `upsert` or `remove`, each with its `cursor`). |
| `estimate(query, signal?)`      | The cost [estimate](https://fathomcharts.com/docs/history.md#estimate-the-cost), charging nothing. Takes the same query as `page()`.                                                                                       |
| `export(query, signal?)`        | One [export](https://fathomcharts.com/docs/exports.md) response: `{ body, contentType, filename, token }`. `body` is the zstd-compressed file as is; `token` resumes the export with `resume`.                             |
| `exportNdjson(query, options?)` | The decompressed export, in chunks (`Uint8Array`) of whole lines, with automatic resume. See [Exports](https://fathomcharts.com/docs/exports.md#with-the-sdk).                                                             |
| `streamToken()`                 | A browser stream token: `{ token, expiresAt }`.                                                                                                                                                                            |
| `usage()`                       | Your [usage](https://fathomcharts.com/docs/limits.md#track-your-usage) this month and your limits.                                                                                                                         |
| `instruments()`                 | `{ instruments }`: every [instrument](https://fathomcharts.com/docs.md#instruments) served, with `symbol`, `alias`, `tickSizeNanos`, `status` and `updatedAt`.                                                             |
| `status()`                      | `{ engine: { state }, instruments, maintenance }`: the state of the computation (`up` or `down`), of each instrument, and the maintenance windows announced.                                                               |
| `offers()`                      | The plans and the limits of each combination (`GET /v1/offers`).                                                                                                                                                           |
| `indicators()`                  | The indicator catalog: `{ version, engineFingerprint, indicators }`.                                                                                                                                                       |

The options of `exportNdjson()`: `signal`, `maxResumes` (consecutive resumes without a new line before giving up, 5 by default) and `resumeDelayMs` (delay before the first resume, 1 s by default, doubled on each consecutive resume up to 30 s). If the very first request fails (network error or `5xx` before the `X-Export-Token` header), `exportNdjson()` never sends it again: a new export would be charged again. It throws the error, and you decide. A resume refused with `CURSOR_EXPIRED` means the computation changed since the export started: the lines already received cannot be continued, start the export again.

### Rate limit and retries

`rateLimitRetries` bounds the retries described in [Rate limit and retries](https://fathomcharts.com/docs/sdk.md#rate-limit-and-retries). An `AbortSignal` ends a call, and the wait for a retry, right away (`ABORTED`).

## Errors

Every error the SDK throws, or ends a loop with, is a `FathomChartsError`; the error it stems from, if any, is its `cause`. Its fields:

| Field                | Content                                                                                                                                                                                                                                    |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `code`               | The stable error code: an API code (see [Errors](https://fathomcharts.com/docs/errors.md#code-reference)), an SDK code (below), or `HTTP_<status>` (`HTTP_502`, for example) for an HTTP error response without a `problem+json` document. |
| `message`            | A human-readable explanation. Over REST: the `detail` of the response, followed by each invalid field (`<detail>: <path> <message>; …`).                                                                                                   |
| `details.status`     | The HTTP status, over REST.                                                                                                                                                                                                                |
| `details.closeCode`  | The WebSocket close code, when the connection was closed.                                                                                                                                                                                  |
| `details.retryAfter` | The value of the `Retry-After` header, in seconds, when the response has one.                                                                                                                                                              |
| `details.problem`    | The full `problem+json` document of the response, with `errors` when the API lists invalid fields; `undefined` when the response is not a `problem+json` document.                                                                         |
| `cause`              | The original error, when there is one: the `signal`’s reason, the error of `fetch`…                                                                                                                                                        |

The codes, the API’s and the SDK’s own, are listed in [Errors](https://fathomcharts.com/docs/sdk.md#errors) (exported as `ERROR_CODES`; the WebSocket close codes, as `CloseCode`). `CONFIGURATION` also covers, in TypeScript, the `ws` package missing for an API key, no `WebSocket` implementation, and zstd decompression missing for `exportNdjson()`.

## Cursors

`compareCursors(a, b)` compares two cursors `<utcDay>.<rank>.<k>` number by number and returns a negative number, zero or a positive number. A malformed cursor (numbers with leading zeros, a missing field…) throws `INVALID_PARAMETERS`.

```ts
import { compareCursors } from '@fathom-charts/sdk';

compareCursors('20720.111416.0', '20720.111417.0'); // < 0
```
