> ## Documentation index
> Fetch the complete documentation index at: https://docs.predictefy.com/llms.txt
> Use it to discover every available page before exploring further.

# TypeScript SDK

> The official TypeScript client for the Predictefy API.

`@predictefy/sdk` is the official TypeScript client — one typed client across all
[17 venues](/reference/venues/), including PredictStreet, plus the cross-venue `router`, matched clusters, and
indicative price discrepancies.

The package is ESM with bundled types and supports Node `>=20.19 <21 || >=22.12` (Node
22.12+ recommended). Hosted reads and normalization stay server-side; signing and funding
remain client-side.

:::note[Beta release]
Published on npm as **`1.0.0-beta.2`**. The API surface is stable for the documented verbs;
pin an exact version while the beta line moves.
:::

## Install

```sh
npm install @predictefy/sdk
```

## Quickstart

```ts
import Predictefy from '@predictefy/sdk';

const client = new Predictefy({ apiKey: process.env.PREDICTEFY_API_KEY });
const markets = await client.polymarket.fetchMarkets({ limit: 5, query: 'fed' });
console.log(
  markets.map((m) => m.title),
  markets.page?.total,
);
```

One normalized method family with familiar exchange-style names; each venue serves only the
capabilities it supports:

```ts
await client.kalshi.fetchOrderBook({ outcomeId: 'KXFED-26MAR-T4.00' });
await client.exchange('hyperliquid').fetchTrades('BTC-100K');
await client.router.fetchMarkets({ query: 'election', status: 'active' }); // all venues
await client.polymarket.fetchOHLCV({ outcomeId: '123', resolution: '1h', limit: 500 });
await client.fetchDiscrepancies({ live: true }); // indicative price discrepancies
await client.router.fetchArbitrage({ contracts: 100, executableOnly: true });
```

:::note[Cross-venue routes are live]
`fetchClusters` / `fetchCluster` / `fetchDiscrepancies` call the live
[cross-venue endpoints](/guides/cross-venue/). `client.router.fetchArbitrage` is the
separate executable assessment surface. If live-book assessment is unavailable, the API returns
the standard `PlatformUnavailableError` with code `ARBITRAGE_UNAVAILABLE`.
:::

## Auth

Pass `apiKey` (or set the `PREDICTEFY_API_KEY` environment variable). The key is sent as
`Authorization: Bearer <key>`, is never logged, and is redacted from every error message.

```ts
const client = new Predictefy({
  apiKey: 'pk_live_…', // falls back to PREDICTEFY_API_KEY
  baseUrl: process.env.PREDICTEFY_API_URL, // shown in your developer dashboard
  retryOn429: true, // default: GETs retried once on 429; POSTs are NEVER auto-retried
});
```

## Execution

Trading uses the separate, isolated execution origin and is always an explicit opt-in:

```ts
import { Predictefy, PREDICTEFY_EXEC_BASE_URL } from '@predictefy/sdk';

const client = new Predictefy({
  apiKey: process.env.PREDICTEFY_API_KEY,
  execBaseUrl: PREDICTEFY_EXEC_BASE_URL,
});

const openOrders = await client.exec.fetchOpenOrders({ venue: 'hyperliquid' });
console.log(openOrders);
```

Every API key includes the required `trade` scope by default; no separate approval is
required. `execBaseUrl` has no implicit default, so the reads API never becomes an execution
proxy. See [Trading & execution](/guides/trading/) for the non-custodial signing flow,
venue status, idempotency rules, spend caps, and limitations.

## Trader Intelligence

Trader identity is capability-qualified by venue. Supported venues expose
wallet-attributed tapes, holders, leaderboards, wallet profiles, and cross-venue
smart-money signals:

```ts
const tape = await client.polymarket.fetchTraderTrades('market-id', { limit: 50 });
const leaders = await client.hyperliquid.fetchLeaderboard({ by: 'score', limit: 50 });
const profile = await client.hyperliquid.fetchWalletProfile('0x...');
const signals = await client.fetchSmartMoney({ venue: 'hyperliquid', limit: 20 });
```

Unsupported venue/verb combinations fail honestly; the SDK does not fabricate
trader identity. Scored feed rows exist only where the venue source is producing activity.

## Errors

Every request is metered against your credit balance. Errors are typed — catch
`PredictefyError` (the base class) and switch on the class or `.code`. The server's
`code` and `message` are always preserved on the thrown error.

| Error class                | HTTP    | Notes                                               |
| -------------------------- | ------- | --------------------------------------------------- |
| `UnauthorizedError`        | 401     | Missing/unknown/revoked key.                        |
| `InsufficientCreditsError` | 402     | `.topUpHint` provides the next balance step.        |
| `ValidationError`          | 400     | Bad params.                                         |
| `NotFoundError`            | 404     | Unknown venue/record.                               |
| `NotSupportedError`        | 400/501 | Honest capability gap (e.g. no public trades tape). |
| `RateLimitedError`         | 429     | GETs auto-retried once unless `retryOn429: false`.  |
| `PlatformUnavailableError` | 503     | Temporary outage — retry with backoff.              |
| `NetworkError`             | —       | Transport failure / non-envelope body.              |

Every account starts with 25,000 free credits, refilled monthly. Handle insufficient
credits without retrying the request, then upgrade your plan or
[contact support](mailto:support@predictefy.com) if the balance looks wrong:

```ts
import { InsufficientCreditsError } from '@predictefy/sdk';

try {
  await client.polymarket.fetchMarkets();
} catch (err) {
  if (err instanceof InsufficientCreditsError) {
    console.error('Insufficient credits. Check the balance shown in your dashboard.');
  }
}
```

## Pagination

List verbs return the data array with `page` / `meta` / `nextCursor` attached. Follow
cursors manually, or let the async iterator do it:

```ts
// Manual: cursor pagination freezes the catalog snapshot from page one.
let page = await client.kalshi.fetchMarkets({ limit: 100 });
while (page.nextCursor)
  page = await client.kalshi.fetchMarkets({ limit: 100, cursor: page.nextCursor });

// Iterator: walks every page for you (defaults snapshotTTL: 0 so the cursor
// chain never expires mid-walk).
for await (const market of client.kalshi.iterateMarkets({ status: 'active' })) {
  console.log(market.title);
}
```

## Surface

`fetchMarkets` · `fetchMarket` · `fetchEvents` · `fetchEvent` · `fetchSeries` ·
`fetchOHLCV` · `fetchOrderBook` · `fetchOrderBooks` · `fetchTrades` ·
`getExecutionPrice` · `getExecutionPriceDetailed` (per exchange; `router` serves the
list verbs, `fetchEvent`, and the stateless execution-price calculators) — plus
`fetchClusters` / `fetchCluster` / `fetchDiscrepancies` (`{ live: true }` for the live
overlay), router-only `fetchArbitrage`, venue-scoped Trader Intelligence, cross-venue
`fetchSmartMoney` / `fetchTopTraders`, execution/account clients, and `billing.checkout`.
Public billing checkout opens with the public plans.

All responses carry honest-data fields (`asOf`, `provenance`, `capabilities`);
cross-venue price gaps are labeled _indicative price discrepancy_.

:::note
`getExecutionPrice` is a **stateless VWAP calculator** over an order book you pass in —
it estimates what a fill of a given size would average. It does not place, route, or
prepare orders. Order lifecycle calls use the separate `client.exec` surface documented
in [Trading & execution](/guides/trading/).
:::
