SDKsClients
TypeScript SDK
@predictefy/sdk is the official TypeScript client — one typed client across all
16 served 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.
Install
Section titled “Install”npm install @predictefy/sdkQuickstart
Section titled “Quickstart”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:
// Books, the tape, and candles are outcome-keyed: resolve an `outcomeId` from that// venue's own catalog first. A venue-native ticker or symbol is not resolved for you.const [market] = await client.kalshi.fetchMarkets({ limit: 1, status: 'active' });const outcomeId = market.outcomes[0].outcomeId;
await client.kalshi.fetchOrderBook({ outcomeId });await client.kalshi.fetchOHLCV({ outcomeId, resolution: '1h', limit: 500 });
// `client.exchange(id)` is the dynamic form of the same verbs on any venue.const hyperliquid = client.exchange('hyperliquid');const [hlMarket] = await hyperliquid.fetchMarkets({ limit: 1, status: 'active' });await hyperliquid.fetchTrades({ outcomeId: hlMarket.outcomes[0].outcomeId });
await client.router.fetchMarkets({ query: 'election', status: 'active' }); // all venuesawait client.fetchDiscrepancies({ live: true }); // indicative price discrepanciesawait client.router.fetchArbitrage({ contracts: 100, executableOnly: true });USD stake calculator (dark by default)
Section titled “USD stake calculator (dark by default)”The optional stake parameter on client.router.fetchArbitrage projects a USD budget.
It is dark by default, behind the server flag READS_ENABLE_ARBITRAGE_STAKE.
const assessments = await client.router.fetchArbitrage({ stake: 100, limit: 20, executableOnly: true,});The SDK validates stake locally: it must be finite, greater than zero, at most
1,000,000 USD, and have at most two decimal places. When stake is supplied, an explicit
limit must be at most 100. Invalid values throw ValidationError before an HTTP request.
The SDK sends stake.toFixed(2) on the wire, so 100 becomes 100.00.
Pass apiKey (or set the PREDICTEFY_API_KEY environment variable). The key is sent as
Authorization: Bearer <key>, is never logged, is redacted from every error field (message,
code, requestId, exchange), and is never printed by console.log(client).
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 // A 429 whose envelope says `retryable: false` is never retried — the server's flag is authoritative.});Execution
Section titled “Execution”Trading uses the separate, isolated execution origin and is always an explicit opt-in:
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);New API keys default to read; opt into the required trade scope per key in the console.
Existing keys keep their scopes. execBaseUrl has no implicit default, so the reads API never becomes an execution
proxy. See Trading & execution for the non-custodial signing flow,
venue status, idempotency rules, spend caps, and limitations.
Trader Intelligence
Section titled “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:
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.
Fixture price history (dark by default)
Section titled “Fixture price history (dark by default)”client.fetchFixturePriceHistory(params) reads recorded prices for one fixture market block.
It is dark by default: public access requires both server flags READS_ENABLE_SPORTS
and READS_ENABLE_SPORTS_HISTORY. An unavailable route returns NotFoundError with code
ROUTE_NOT_FOUND.
const history = await client.fetchFixturePriceHistory({ fixtureId: 'fixture-id', // Use an id returned by fetchSportsFixtures. marketType: 'match_result', interval: '1h', from: new Date('2026-09-29T00:00:00Z'), to: new Date('2026-09-30T00:00:00Z'),});console.log(history.series, history.meta.degradedReason);The result is the response’s data object with meta attached directly. It includes fixture
and market identity, series, movement, closing, and closingUnavailable. The metadata
describes the resolved window, interval, priceBasis: 'best_ask', retention, truncation, and
recorder health. The SDK normalizes an omitted meta.degradedReason to null.
Errors
Section titled “Errors”Credit metering is operation-specific. Hosted reads use their route weights. Isolated execution
meters submit, acknowledgement, cancel, and modify; build/precheck and execution reads are not
debited, and billing-session routes cost zero. 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 or the envelope says retryable: false. |
PlatformUnavailableError | 503 | Temporary outage — retry with backoff. |
NetworkError | — | Transport failure / non-envelope body. |
Every account starts on the Free plan with 25,000 credits, topped up to 25,000 at the start of each UTC month; unused allowance does not carry over. The top-up is applied within a minute of 00:00 UTC. Handle insufficient credits without retrying the request, then upgrade your plan or contact support if the balance looks wrong:
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
Section titled “Pagination”List verbs return the data array with page / meta / nextCursor attached. Follow
cursors manually, or let the async iterator do it:
// 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
Section titled “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, the sports price comparison family
(fetchSportsFacets, fetchSportsScreen, fetchSportsCompetitions, fetchSportsTeams,
fetchSportsFixtures, fetchSportsFixture, compareFixturePrices), the dark-by-default
fetchFixturePriceHistory, execution/account clients, and billing.checkout.
Public billing checkout opens with the public plans.
Sports catalogue, screen, and comparison reads have been live and metered since 2026-09-29.
On a deployment that has them switched off, calls reject with NotFoundError code
ROUTE_NOT_FOUND and are not charged, and the screen
has a second switch; fair and edge are Predictefy’s own definitions for price comparison, not an
arbitrage claim.
Rotate a secret with client.webhooks.rotate(id) to issue a new signing secret for an existing
webhook endpoint; see Rotating a secret.
UnifiedMarket records carry asOf, provenance, and capabilities. Other response families
use type-specific contracts: series omit those fields, order books make freshness/provenance
optional and omit capabilities, and candles expose source, sourceType, quality, and
isTrueCandle. Cross-venue price gaps are labeled indicative price discrepancy.