PredictorSDK
SDK

TypeScript SDK Reference

Type reference for the PredictorSDK TypeScript client.

The TypeScript client is auto-generated from the OpenAPI spec and published as @predictorsdk/client on npm. It needs Node 18 or later and works with both import and require: the package ships an ES module build and a CommonJS build, each with its own types.

// CommonJS
const { PredictorSDKClient } = require("@predictorsdk/client");

An app that loads the package both ways gets two copies of it, so an error thrown by one is not an instanceof the other's PredictorSDKError; use one style.

Client surfaces

SurfaceEndpoint
client.getPlans()GET /v1/plans (no auth required)
client.getSportsMatchingMarkets(request)GET /v1/matching-markets/sports
client.lookupSportsMatchingMarkets(request)GET /v1/matching-markets/sports/lookup
client.getMarkets(request)GET /v1/markets
client.getCategories()GET /v1/categories
client.getMarket(request)GET /v1/markets/{market_id}
client.getEvent(request)GET /v1/events/{event_id}
client.getBinanceCryptoPrices(request)GET /v1/crypto-prices/binance
client.getPolymarketWallet(request)GET /v1/polymarket/wallet
client.listPolymarketWalletPositions(request)GET /v1/polymarket/wallet/positions

When you await a call to one of the other surfaces, it resolves to the parsed response body. Use .withRawResponse() if you also need headers or status metadata.

Four surfaces are paginated with a cursor: getSportsMatchingMarkets, getMarkets, getBinanceCryptoPrices and listPolymarketWalletPositions. Each resolves to a Page that walks the cursor for you; see Pagination.

Usage

import { PredictorSDKClient } from "@predictorsdk/client";

const client = new PredictorSDKClient({
  token: "YOUR_API_KEY",
});

// A paginated call answers a page: page.data holds this page's events.
const page = await client.getSportsMatchingMarkets({ league: "nfl" });
for (const event of page.data) {
  console.log(event.eventId, event.scheduledDate, event.scheduledStart, event.title);
}

// A lookup finds events by identifier and answers one unpaginated response.
const found = await client.lookupSportsMatchingMarkets({
  sourceId: "kalshi:KXNBAGAME-26OCT20OKCSAS",
});

const categories = await client.getCategories();
const plans = await client.getPlans(); // Public; no API key is sent.

Pagination

A call to getSportsMatchingMarkets, getMarkets, getBinanceCryptoPrices or listPolymarketWalletPositions resolves to a Page. Iterate it with for await to visit every item on every page; the client sends each next cursor and repeats the filters that issued it.

// Every event on every page.
const page = await client.getSportsMatchingMarkets({ league: "nfl", limit: 50 });
for await (const event of page) {
  console.log(event.eventId, event.title);
}

// One provider's markets, a page at a time. The whole catalog is about
// 470,000 markets, thousands of pages, so filter by provider or category first.
const markets = await client.getMarkets({ provider: "prophetx", limit: 100 });
while (true) {
  console.log(markets.data.length, markets.response.pagination.total);
  if (!markets.hasNextPage()) break;
  await markets.getNextPage(); // advances `markets` in place
}
MemberDescription
page.dataThe items on the current page
page.responseThe whole response body of the current page, such as snapshot and pagination
page.rawResponseThe current page's HTTP metadata: status and headers
page.hasNextPage()true while pagination.nextCursor is set
page.getNextPage()Fetches the next page, moves page onto it and returns it
for await (const item of page)Visits page.data, then every later page

Iterating or calling getNextPage() moves the same Page forward, so read page.response first if you need the first page's pagination or snapshot. pagination.total is a number on getMarkets and getSportsMatchingMarkets, and null on getBinanceCryptoPrices and listPolymarketWalletPositions, whose upstreams cannot count; follow hasNextPage() rather than total. See the Pagination guide for the cursor rules.

Configuration

OptionTypeDescription
tokenstringYour API key (Bearer token)
baseUrlstringOverride the base URL
environmentPredictorSDKEnvironmentAPI environment (defaults to Production)
timeoutInSecondsnumberRequest timeout in seconds (default: 60)
maxRetriesnumberRetries after a 408, 429 or 5xx response, or a connection that failed before any response (default: 2)
fetchtypeof fetchCustom fetch implementation

Key types

SportsMatchingListResponse

The page.response of getSportsMatchingMarkets. Contains the matched events in data, the pagination block, and a snapshot freshness block.

FieldTypeDescription
dataCanonicalSportsEvent[]Matched events, soonest scheduled start first, each once. Each carries its full-game moneyline, or every matched submarket with includeSubmarkets: true
paginationPaginationBlockPagination metadata
snapshotSportsMatchingSnapshotMatching freshness. observedAt is when the oldest venue read behind the snapshot began, or null when unknown — check it before treating a missing event as unmatched

SportsMatchingLookupResponse

The response from lookupSportsMatchingMarkets: one unpaginated response, with no pagination block.

FieldTypeDescription
dataCanonicalSportsEvent[]The events the identifiers found, soonest scheduled start first, each once, with every matched submarket unless includeSubmarkets: false
lookupsRecord<string, SportsMatchingLookupResult>Every eventId and sourceId you sent, as sent, with the events it found
snapshotSportsMatchingSnapshotMatching freshness, as above

SportsMatchingLookupResult

FieldTypeDescription
eventIdsstring[]The eventIds of the events in data that hold the identifier; empty when it found none

CanonicalSportsEvent

One matched game, as the list and the lookup both return it.

FieldTypeDescription
eventIdstringThe canonical event key, such as nba-okc-sas-2026-10-20
sportstringThe sport, such as basketball
leaguestringThe league: nba, wnba, nfl, mlb or nhl
titlestringThe event title, such as Oklahoma City Thunder vs. San Antonio Spurs
scheduledStartDate | nullWhen the game starts, when a venue published an exact start time; null when venues publish only a date. Millisecond precision, in UTC
scheduledDatestringThe game's calendar day in America/New_York, as YYYY-MM-DD. Always present, even when scheduledStart is null
participantsCanonicalSportsParticipant[]The two sides, each with a key, a name and a role (away or home)
submarketsCanonicalSportsSubmarket[]The matched submarkets in reading order: the full game, then each half and quarter, and within each the moneyline, spreads, totals, team totals and props

PaginationBlock

FieldTypeDescription
limitnumberEchoes the limit request param (default 25, max 100)
totalnumber | nullTotal matching items across all pages; null on the routes that cannot count (wallet positions, crypto prices)
hasMorebooleantrue when more pages exist
nextCursorstring | undefinedThe cursor for the next page. A Page sends it for you; pass it back as cursor only to resume a walk by hand

CanonicalSportsSourceMarket

One venue market matched to a canonical submarket, with the same fields on every venue. eventId is the venue's parent event, for getEvent; marketId is the market itself, for getMarket. Pass provider to either call as its provider, or use the composite provider + ":" + id. Any of eventId, marketId or marketSlug, prefixed with provider, finds the event again as sourceId. See Which identifier goes where.

FieldTypeDescription
providerCanonicalSportsSourceMarketProviderkalshi, polymarket, predict, sxbet, alpha-arcade, prophetx, pred, or limitless
eventIdstringThe venue's parent event, for getEvent. Not a market ID
marketIdstringThe market, for getMarket. Kalshi lists a game winner as one market per team, so it appears once per team
marketNamestringThe market's name: the venue's own title, or on SX Bet its two outcome labels, as getMarket titles it
marketSlugstring | undefinedThe venue's slug, where it has one (Polymarket, AlphaArcade). sourceId accepts it; for getMarket send marketId
outcomesCanonicalSportsSourceOutcome[]Each side's outcomeId and label, mapped to a canonical outcome; getMarket returns the same IDs. Kalshi and Pred sides are yes/no, labelled Yes/No

GetSportsMatchingMarketsRequest

Pass an empty object {} (or nothing) to list every matched event. The filters narrow the list, and total, hasMore and the cursors describe the filtered set.

FieldTypeDescription
limitnumberEvents per page (1–100, default 25)
cursorstringResumes a walk by hand; a Page sends it for you. Valid only with the filters that issued it
leagueGetSportsMatchingMarketsRequestLeagueOnly this league: nba, wnba, nfl, mlb or nhl
scheduledDatestringOnly the games on this day, as YYYY-MM-DD in America/New_York: the event's scheduledDate
participantstringOnly events with a participant whose key is this (such as okc), case-insensitive. Keys are unique only within a league (la is the Rams in the NFL and the Sparks in the WNBA), so pair it with league
includeSettledbooleanAlso read the settled archive: games whose date has passed, and cancelled games (default false)
includeSubmarketsbooleanEvery matched submarket, or only the full-game moneyline (default false)
playerPropMatchstrict | same_propPlayer-prop policy; requires includeSubmarkets: true (default strict)
// Tonight's NFL games: filters compose, and every page keeps them.
const page = await client.getSportsMatchingMarkets({
  league: "nfl",
  scheduledDate: "2026-10-15",
});
for await (const event of page) {
  console.log(event.eventId, event.scheduledStart);
}

LookupSportsMatchingMarketsRequest

Send at least one identifier; with both parameters, at most 100 unique values in all.

FieldTypeDescription
eventIdstring | string[]Canonical event key(s)
sourceIdstring | string[]Venue ID(s) as {provider}:{id}: any marketId, marketSlug or eventId a source market publishes, a Polymarket, Predict or AlphaArcade outcome token, or a Polymarket condition ID. Case-insensitive
includeSettledbooleanAlso search the settled archive: games whose date has passed, and cancelled games (default false)
includeSubmarketsbooleanEvery matched submarket (default true for a lookup), or only the full-game moneyline
playerPropMatchstrict | same_propPlayer-prop policy; requires includeSubmarkets: true, the default for a lookup (default strict)
// `lookups` reports what each identifier found; there is no pagination block.
const response = await client.lookupSportsMatchingMarkets({
  sourceId: ["kalshi:KXNBAGAME-26OCT20OKCSAS", "sxbet:L19766755"],
});
for (const [id, found] of Object.entries(response.lookups)) {
  console.log(id, found.eventIds);
}

MarketsListResponse

The page.response of getMarkets. Contains data, catalog snapshot freshness, and pagination.

FieldTypeDescription
dataUnifiedMarket[]Array of markets for the current page
snapshotMarketsSnapshotCatalog freshness; observedAt is when the crawl behind the oldest provider snapshot in the bound page scope began
paginationPaginationBlockPagination metadata

UnifiedMarket

A market from any supported prediction market provider.

FieldTypeDescription
idstringComposite ID in the format {provider}:{provider_id}
providerUnifiedMarketProviderkalshi, polymarket, predict, sxbet, hyperliquid, alpha-arcade, prophetx, limitless, or pred
titlestringHuman-readable market title/question
categoryMarketCategoryCanonical top-level category, such as sports, politics, or crypto

GetMarketsRequest

FieldTypeDescription
limitnumberResults per page (1–100, default 25)
cursorstringResumes a walk by hand; a Page sends it for you
categoryMarketCategoryOptional canonical category filter
providerGetMarketsRequestProviderOptional canonical provider filter; replay cursors with the same value

GetMarketRequest and GetEventRequest

getMarket takes a marketId and getEvent an eventId, either as a platform-native ID or the composite {provider}:{id}. Both take an optional provider (GetMarketRequestProvider and GetEventRequestProvider) to say which venue an ID belongs to. A bare ID that more than one venue holds answers 409 with the candidates; retry with provider set to one of them, or with the composite form. The retired platform is not accepted.

const market = await client.getMarket({
  marketId: "KXNBAGAME-26OCT20OKCSAS-OKC",
  provider: "kalshi",
});
const event = await client.getEvent({ eventId: "KXNBAGAME-26OCT20OKCSAS", provider: "kalshi" });
console.log(event.provider); // "kalshi": pass it back as `provider` for the nested markets

EventResponse

The response from getEvent.

FieldTypeDescription
eventIdstringEcho of the event ID you sent
providerEventResponseProviderThe venue the ID resolved against, the value to pass as provider to getMarket for its nested markets
titlestringThe event title
marketsEventMarket[]The markets nested under the event, each with a platform-native marketId and a title
fanoutEventFanout | undefinedPresent when Kalshi sibling events were merged in; lists which siblings were attempted and which returned nothing

GetBinanceCryptoPricesRequest

FieldTypeDescription
currencystringBinance trading pair, such as btcusdt
startTimenumberStart of the range, a Unix timestamp in milliseconds (inclusive)
endTimenumberEnd of the range, a Unix timestamp in milliseconds (inclusive)
limitnumberPrices per page, 1–100 (default 100 with a time range); more than 100 is a 400
cursorstringResumes a walk by hand; a Page sends it for you. The retired paginationKey is not accepted

CryptoPricesResponse

The page.response of getBinanceCryptoPrices: newest prices first, one per second.

FieldTypeDescription
dataCryptoPriceItem[]The prices on this page
paginationPaginationBlockPagination metadata; total is null

Raw responses

Use .withRawResponse() on a surface that is not paginated to access HTTP metadata alongside the parsed body:

const raw = await client
  .lookupSportsMatchingMarkets({ sourceId: "kalshi:KXNBAGAME-26OCT20OKCSAS" })
  .withRawResponse();
console.log(raw.rawResponse.status);
console.log(raw.rawResponse.headers.get("X-RateLimit-Reset")); // Unix seconds
console.log(raw.data.data.length);

A paginated surface keeps the metadata on its page:

const page = await client.getSportsMatchingMarkets();
console.log(page.rawResponse.status);
console.log(page.rawResponse.headers.get("X-RateLimit-Reset")); // Unix seconds

Error handling

The SDK throws typed errors for known HTTP error codes:

Error classStatus codeDescription
BadRequestError400Invalid query parameters
UnauthorizedError401Missing or invalid API key
PaymentRequiredError402Monthly allowance exhausted or subscription payment recovery required
ForbiddenError403Insufficient permissions
NotFoundError404Market or event not found
ConflictError409Bare identifier resolves on multiple providers; retry with provider or a composite ID
TooManyRequestsError429Rate limit exceeded
BadGatewayError502Upstream provider or API-key authorization dependency failure
ServiceUnavailableError503Service temporarily unavailable

The client retries a 408, 429 or 5xx response twice by default (maxRetries, on the client or per request). A 429 waits for Retry-After, or else until the second-based X-RateLimit-Reset. Any other retried status honours Retry-After and otherwise backs off exponentially from one second. No wait exceeds 60 seconds, and an abortSignal ends a wait as soon as it aborts; timeoutInSeconds limits each attempt. A 409 is never retried: send the request again with a provider from its candidates.

A request that gets no response because the connection was refused, reset or closed by the server (Node reports ECONNREFUSED, ECONNRESET or UND_ERR_SOCKET on the error's cause) is retried the same way, with the same backoff and the same limit. A timeout and an abort are never retried. Every call is a read, so repeating one is safe. The Go and Python SDKs follow the same policy, and error.requestId is the response's request id.

import { PredictorSDK, PredictorSDKClient } from "@predictorsdk/client";

const client = new PredictorSDKClient({
  token: "YOUR_API_KEY",
});

try {
  // A venue ID needs its provider prefix: "kalshi:KXNBAGAME-26OCT20OKCSAS".
  await client.lookupSportsMatchingMarkets({ sourceId: "KXNBAGAME-26OCT20OKCSAS" });
} catch (error) {
  if (error instanceof PredictorSDK.BadRequestError) {
    console.log("Bad request:", error.body.code, error.body.message);
  }
}

On this page