Python SDK Reference
Type reference for the PredictorSDK Python client.
The Python client is auto-generated from the OpenAPI spec and published as predictorsdk on PyPI.
Client surfaces
| Surface | Endpoint |
|---|---|
client.get_plans() | GET /v1/plans (no auth required) |
client.get_sports_matching_markets(...) | GET /v1/matching-markets/sports |
client.lookup_sports_matching_markets(...) | GET /v1/matching-markets/sports/lookup |
client.get_markets(...) | GET /v1/markets |
client.get_categories() | GET /v1/categories |
client.get_market(market_id, ...) | GET /v1/markets/{market_id} |
client.get_event(event_id, ...) | GET /v1/events/{event_id} |
client.get_binance_crypto_prices(...) | GET /v1/crypto-prices/binance |
client.get_polymarket_wallet(...) | GET /v1/polymarket/wallet |
client.list_polymarket_wallet_positions(...) | GET /v1/polymarket/wallet/positions |
The SDK provides both synchronous (PredictorSDK) and asynchronous (AsyncPredictorSDK) clients.
Four surfaces are paginated with a cursor: get_sports_matching_markets, get_markets, get_binance_crypto_prices and list_polymarket_wallet_positions. Each returns a pager that walks the cursor for you; see Pagination. The other surfaces return the parsed response.
Sync client
from predictorsdk import PredictorSDK
client = PredictorSDK(token="YOUR_API_KEY")
# A paginated call returns a pager: pager.items holds this page's events.
pager = client.get_sports_matching_markets(league="nfl")
for event in pager.items:
print(event.event_id, event.scheduled_date, event.scheduled_start, event.title)
# A lookup finds events by identifier and answers one unpaginated response.
found = client.lookup_sports_matching_markets(source_id="kalshi:KXNBAGAME-26OCT20OKCSAS")
categories = client.get_categories()
plans = client.get_plans() # Public: works without an API key.Async client
import asyncio
from predictorsdk import AsyncPredictorSDK
client = AsyncPredictorSDK(token="YOUR_API_KEY")
async def main():
pager = await client.get_sports_matching_markets(league="nfl")
async for event in pager: # every event on every page
print(event.event_id, event.title)
asyncio.run(main())Pagination
A call to get_sports_matching_markets, get_markets, get_binance_crypto_prices or list_polymarket_wallet_positions returns a pager. Iterate it to visit every item on every page; the client sends each next cursor and repeats the filters that issued it. On the async client, await the call, then use async for.
# Every event on every page.
for event in client.get_sports_matching_markets(league="nfl", limit=50):
print(event.event_id, 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.
pager = client.get_markets(provider="prophetx", limit=100)
for page in pager.iter_pages():
print(len(page.items), page.response.pagination.total)| Member | Description |
|---|---|
pager.items | The items on this page, as a list |
pager.response | This page's whole parsed response, such as snapshot and pagination |
pager.has_next | True while pagination.next_cursor is set |
pager.next_page() | The next page's pager, or None after the last page |
pager.iter_pages() | The pager of every page in turn, starting with this one |
for item in pager | Every item on every page (async for on the async client) |
A pager is immutable: next_page() and iter_pages() return new pagers. Iterating the pager itself walks every page, so use pager.items when you want only the first. pagination.total is an integer on get_markets and get_sports_matching_markets, and None on get_binance_crypto_prices and list_polymarket_wallet_positions, whose upstreams cannot count; follow has_next rather than total. See the Pagination guide for the cursor rules.
Configuration
| Parameter | Type | Description |
|---|---|---|
token | str | Your API key (Bearer token) |
base_url | str | None | Override the base URL |
environment | PredictorSDKEnvironment | API environment (defaults to PRODUCTION) |
timeout | float | None | Request timeout in seconds (default: 60) |
max_retries | int | None | Retries after a 408, 429 or 5xx response, or a connection that failed before any response (default: 2) |
httpx_client | httpx.Client | None | Custom httpx client instance |
Key types
These tables describe what the API promises. A response that leaves a field out still parses, and the field reads None; see Responses that leave a field out.
SportsMatchingListResponse
The pager.response of get_sports_matching_markets. Contains the matched events in data, the pagination block, and a snapshot freshness block.
| Field | Type | Description |
|---|---|---|
data | List[CanonicalSportsEvent] | Matched events, soonest scheduled start first, each once. Each carries its full-game moneyline, or every matched submarket with include_submarkets=True |
pagination | PaginationBlock | Pagination metadata |
snapshot | SportsMatchingSnapshot | Matching freshness. observed_at is when the oldest venue read behind the snapshot began, or None when unknown — check it before treating a missing event as unmatched |
SportsMatchingLookupResponse
The response from lookup_sports_matching_markets: one unpaginated response, with no pagination block.
| Field | Type | Description |
|---|---|---|
data | List[CanonicalSportsEvent] | The events the identifiers found, soonest scheduled start first, each once, with every matched submarket unless include_submarkets=False |
lookups | Dict[str, SportsMatchingLookupResult] | Every event_id and source_id you sent, as sent, with the events it found |
snapshot | SportsMatchingSnapshot | Matching freshness, as above |
SportsMatchingLookupResult
| Field | Type | Description |
|---|---|---|
event_ids | List[str] | The event_ids 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.
| Field | Type | Description |
|---|---|---|
event_id | str | The canonical event key, such as nba-okc-sas-2026-10-20 |
sport | str | The sport, such as basketball |
league | str | The league: nba, wnba, nfl, mlb or nhl |
title | str | The event title, such as Oklahoma City Thunder vs. San Antonio Spurs |
scheduled_start | datetime | None | When the game starts, when a venue published an exact start time; None when venues publish only a date. Millisecond precision, in UTC |
scheduled_date | date | The game's calendar day in America/New_York. Always present, even when scheduled_start is None |
participants | List[CanonicalSportsParticipant] | The two sides, each with a key, a name and a role (away or home) |
submarkets | List[CanonicalSportsSubmarket] | 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
| Field | Type | Description |
|---|---|---|
limit | int | Echoes the limit request param (default 25, max 100) |
total | int | None | Total matching items across all pages; None on the routes that cannot count (wallet positions, crypto prices) |
has_more | bool | True when more pages exist |
next_cursor | str | None | The cursor for the next page. A pager 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. event_id is the venue's parent event, for get_event; market_id is the market itself, for get_market. Pass provider to either call as its provider, or use the composite f"{provider}:{id}". Any of event_id, market_id or market_slug, prefixed with provider, finds the event again as source_id. See Which identifier goes where.
| Field | Type | Description |
|---|---|---|
provider | CanonicalSportsSourceMarketProvider | kalshi, polymarket, predict, sxbet, alpha-arcade, prophetx, pred, or limitless |
event_id | str | The venue's parent event, for get_event. Not a market ID |
market_id | str | The market, for get_market. Kalshi lists a game winner as one market per team, so it appears once per team |
market_name | str | The market's name: the venue's own title, or on SX Bet its two outcome labels, as get_market titles it |
market_slug | str | None | The venue's slug, where it has one (Polymarket, AlphaArcade). source_id accepts it; for get_market send market_id |
outcomes | List[CanonicalSportsSourceOutcome] | Each side's outcome_id and label, mapped to a canonical outcome; get_market returns the same IDs. Kalshi and Pred sides are yes/no, labelled Yes/No |
get_sports_matching_markets parameters
All keyword arguments, all optional. The filters narrow the list, and total, has_more and the cursors describe the filtered set.
| Parameter | Type | Description |
|---|---|---|
limit | int | None | Events per page (1–100, default 25) |
cursor | str | None | Resumes a walk by hand; a pager sends it for you. Valid only with the filters that issued it |
league | GetSportsMatchingMarketsRequestLeague | None | Only this league: "nba", "wnba", "nfl", "mlb" or "nhl" |
scheduled_date | datetime.date | None | Only the games on this day in America/New_York: the event's scheduled_date |
participant | str | None | Only 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 |
include_settled | bool | None | Also read the settled archive: games whose date has passed, and cancelled games (default False) |
include_submarkets | bool | None | Every matched submarket, or only the full-game moneyline (default False) |
player_prop_match | str | None | Player-prop policy (strict or same_prop); requires include_submarkets=True |
from datetime import date
# Tonight's NFL games: filters compose, and every page keeps them.
for event in client.get_sports_matching_markets(league="nfl", scheduled_date=date(2026, 10, 15)):
print(event.event_id, event.scheduled_start)lookup_sports_matching_markets parameters
Send at least one identifier; with both, at most 100 unique values in all.
| Parameter | Type | Description |
|---|---|---|
event_id | str | Sequence[str] | None | Canonical event key(s) |
source_id | str | Sequence[str] | None | Venue ID(s) as {provider}:{id}: any market_id, market_slug or event_id a source market publishes, a Polymarket, Predict or AlphaArcade outcome token, or a Polymarket condition ID. Case-insensitive |
include_settled | bool | None | Also search the settled archive: games whose date has passed, and cancelled games (default False) |
include_submarkets | bool | None | Every matched submarket (default True for a lookup), or only the full-game moneyline |
player_prop_match | str | None | Player-prop policy (strict or same_prop); requires include_submarkets=True, the default for a lookup |
# `lookups` reports what each identifier found; there is no pagination block.
response = client.lookup_sports_matching_markets(
source_id=["kalshi:KXNBAGAME-26OCT20OKCSAS", "sxbet:L19766755"]
)
for identifier, found in response.lookups.items():
print(identifier, found.event_ids)MarketsListResponse
The pager.response of get_markets. Contains data, catalog snapshot freshness, and pagination.
| Field | Type | Description |
|---|---|---|
data | List[UnifiedMarket] | Array of markets for the current page |
snapshot | MarketsSnapshot | Catalog freshness; observed_at is when the crawl behind the oldest provider snapshot in the bound page scope began |
pagination | PaginationBlock | Pagination metadata |
UnifiedMarket
A market from any supported prediction market provider.
| Field | Type | Description |
|---|---|---|
id | str | Composite ID in the format {provider}:{provider_id} |
provider | UnifiedMarketProvider | kalshi, polymarket, predict, sxbet, hyperliquid, alpha-arcade, prophetx, limitless, or pred |
title | str | Human-readable market title/question |
category | MarketCategory | Canonical top-level category, such as sports, politics, or crypto |
get_markets parameters
| Parameter | Type | Description |
|---|---|---|
limit | int | None | Results per page (1–100, default 25) |
cursor | str | None | Resumes a walk by hand; a pager sends it for you |
category | MarketCategory | None | Optional canonical category filter |
provider | GetMarketsRequestProvider | None | Optional canonical provider filter; replay cursors with the same value |
get_market and get_event parameters
get_market(market_id, ...) and get_event(event_id, ...) take the ID as their first argument, either platform-native or the composite {provider}:{id}. Both take an optional provider keyword (GetMarketRequestProvider and GetEventRequestProvider) to say which venue an ID belongs to. A bare ID that more than one venue holds raises ConflictError with the candidates; retry with provider set to one of them, or with the composite form. The retired platform is not accepted.
market = client.get_market("KXNBAGAME-26OCT20OKCSAS-OKC", provider="kalshi")
event = client.get_event("KXNBAGAME-26OCT20OKCSAS", provider="kalshi")
print(event.provider) # "kalshi": pass it back as `provider` for the nested marketsEventResponse
The response from get_event.
| Field | Type | Description |
|---|---|---|
event_id | str | Echo of the event ID you sent |
provider | EventResponseProvider | The venue the ID resolved against, the value to pass as provider to get_market for its nested markets |
title | str | The event title |
markets | List[EventMarket] | The markets nested under the event, each with a platform-native market_id and a title |
fanout | EventFanout | None | Present when Kalshi sibling events were merged in; lists which siblings were attempted and which returned nothing |
get_binance_crypto_prices parameters
| Parameter | Type | Description |
|---|---|---|
currency | str | Binance trading pair, such as btcusdt |
start_time | int | None | Start of the range, a Unix timestamp in milliseconds (inclusive) |
end_time | int | None | End of the range, a Unix timestamp in milliseconds (inclusive) |
limit | int | None | Prices per page, 1–100 (default 100 with a time range); more than 100 is a 400 |
cursor | str | None | Resumes a walk by hand; a pager sends it for you. The retired pagination_key is not accepted |
CryptoPricesResponse
The pager.response of get_binance_crypto_prices: newest prices first, one per second.
| Field | Type | Description |
|---|---|---|
data | List[CryptoPriceItem] | The prices on this page |
pagination | PaginationBlock | Pagination metadata; total is None |
Raw responses
Use with_raw_response on a surface that is not paginated to access HTTP metadata alongside the parsed body:
raw = client.with_raw_response.lookup_sports_matching_markets(
source_id="kalshi:KXNBAGAME-26OCT20OKCSAS"
)
print(raw.status_code)
print(raw.headers)
print(len(raw.data.data))A paginated surface returns its pager from with_raw_response too, and a pager carries no status or headers; for those, send the request through your own httpx_client and read its responses there.
Responses that leave a field out
The client reads a response without validating it against the spec, as the TypeScript and Go clients do. A field the spec marks required that a response does not carry reads None instead of raising an error, so a call against an API that is a release ahead of or behind your SDK still returns everything the response did carry. Older SDK releases raised a ParsingError in this case.
- A value of an unexpected type is passed through as the API sent it.
- A field the SDK does not know is kept as an attribute on its model.
- Dates and datetimes are still parsed to
dateanddatetime, and nested objects are still the generated models, soisinstancechecks and attribute access work as before.
Because the types above describe what the API promises, check the fields you depend on. pager.response.snapshot can be None, and so can pager.items when a page carries no data:
pager = client.get_sports_matching_markets()
snapshot = pager.response.snapshot
if snapshot is not None and snapshot.observed_at is not None:
print(snapshot.observed_at)
for event in pager.items or []:
print(event.event_id)An error response whose body does not match ErrorResponse still raises the typed error for its status, with e.body.code and e.body.message reading None for whatever the body did not carry.
Error handling
The SDK raises typed exceptions for known HTTP error codes:
| Error class | Status code | Description |
|---|---|---|
BadRequestError | 400 | Invalid query parameters |
UnauthorizedError | 401 | Missing or invalid API key |
PaymentRequiredError | 402 | Monthly allowance exhausted or subscription payment recovery required |
ForbiddenError | 403 | Insufficient permissions |
NotFoundError | 404 | Market or event not found |
ConflictError | 409 | Bare identifier resolves on multiple providers; retry with provider or a composite ID |
TooManyRequestsError | 429 | Rate limit exceeded |
BadGatewayError | 502 | Upstream provider or API-key authorization dependency failure |
ServiceUnavailableError | 503 | Service temporarily unavailable |
The client retries a 408, 429 or 5xx response twice by default
(max_retries, on the client or in request_options). 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. 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 is retried the same way, with the same backoff and the same limit. A timeout is never retried, and cancelling an async call ends a wait at once. Every call is a read, so repeating one is safe. The Go and TypeScript SDKs follow the same policy, and every response and error carries the same request id.
from predictorsdk import PredictorSDK, BadRequestError
client = PredictorSDK(token="YOUR_API_KEY")
try:
# A venue ID needs its provider prefix: "kalshi:KXNBAGAME-26OCT20OKCSAS".
client.lookup_sports_matching_markets(source_id="KXNBAGAME-26OCT20OKCSAS")
except BadRequestError as e:
print(f"Bad request: {e.body.code}: {e.body.message}")