Prediction Markets
Trade prediction markets (Polymarket, Kalshi, Limitless, Myriad, Hyperliquid) with the CCXT unified API.
Prediction Markets
CCXT supports prediction-market exchanges (Polymarket, Kalshi, Limitless, Myriad, and Hyperliquid prediction markets) through a dedicated prediction namespace. Prediction exchanges implement the same unified API as regular crypto exchanges, with prices quoted between 0 and 1 USDC per outcome share.
Per-exchange API references live in the Prediction Markets section, and runnable code is under Examples.
// JavaScript / TypeScript
const exchange = new ccxt.prediction.polymarket ()# Python (async-only — ccxt.prediction.<id> IS the async class)
import ccxt.prediction
exchange = ccxt.prediction.polymarket()// PHP (async-only, ReactPHP — \ccxt\prediction\<id> IS the async class)
$exchange = new \ccxt\prediction\polymarket();// C#
var exchange = new ccxt.prediction.polymarket();// Go
import ccxtprediction "github.com/ccxt/ccxt/go/v4/prediction"
exchange := ccxtprediction.NewPolymarket()// Java
import io.github.ccxt.exchanges.prediction.Polymarket;
Polymarket exchange = new Polymarket();Prediction exchanges are flagged with exchange.has['prediction']. Their data model has three levels:
- events — a question or grouping, like "Will X happen by July?"
- markets — each event contains one or more markets, returned by
fetchMarkets()/loadMarkets()withmarket['type'] === 'prediction' - outcomes — each market carries an
outcomeslist (for example YES and NO tokens); each outcome has its ownoutcomehandle likeTRUMP_OUT_PRESIDENT_2027:YES, an exchange-specificoutcomeId, the parentmarket, and alabel(e.g.YES/NO)
How the structures relate — PredictionEvent → market → PredictionOutcome, where the outcome is the tradeable unit that every price/trade method takes. Each market is a standard ccxt Market row that additionally carries prediction fields (marketType, executionModel, collateral, …) and its own outcomes list:
Event
groups markets — e.g. "2026 World Cup"
idslugtitleactiveresolvedendvolumeMarket
one question — e.g. "Brazil to win?" · binary · categorical · scalar
idmarketmarketTypeexecutionModelcollateraltickSizefeesOutcome
tradeable unitthe tradeable unit — e.g. "…:YES"
outcomeoutcomeIdlabelprice 0..1bidaskcreateOrder()fetchMyTrades()fetchPositions()fetchTicker()fetchOrderBook()prices are probabilities 0..1 per share · amount = shares · cost = collateral
Prices are probabilities between 0 and 1 per share, amount is the number of shares, and cost is the collateral spent.
Unified methods
Prediction exchanges expose the same unified API as crypto exchanges. Discovery starts with events; every price and trade method then takes an outcome handle (or outcome id) through the outcome / outcomes parameter instead of a market symbol. Support varies per venue — always check exchange.has[methodName] and the per-exchange reference.
Discovery & markets
fetchEvents (params?)— search events (scope byquery/queries/tags/eventId/slug), caching their markets and outcomesfetchEvent (id, params?)— a single event by id, slug or tickerfetchMarkets (params?)/loadMarkets (reload?)— every market (each carriestype: 'prediction'and anoutcomeslist)fetchTime (params?)— exchange server time
Market data — each takes an outcome handle
fetchTicker (outcome, params?)/fetchTickers (outcomes?, params?)— last price, bid/ask and volumefetchOrderBook (outcome, limit?, params?)— bids and asks for an outcomefetchOHLCV (outcome, timeframe?, since?, limit?, params?)— price-history candlesfetchTrades (outcome, since?, limit?, params?)— public tradesfetchOpenInterest (outcome, params?)— open interestfetchTradingFee (outcome, params?)— taker / maker feesfetchTradeQuote (outcome, ...)— an executable quote on AMM venues
Trading — amount is the number of shares, price is a probability between 0 and 1
createOrder (outcome, type, side, amount, price?, params?)— place an ordercreateOrders (orders, params?)— batch createeditOrder (id, outcome, ...)— amend a resting ordercancelOrder (id, outcome?, params?),cancelOrders (ids, ...),cancelAllOrders (outcome?, params?)
Account & orders
fetchBalance (params?)— collateral balancesfetchOrder (id, outcome?, params?),fetchOrders,fetchOpenOrders,fetchClosedOrders,fetchCanceledOrders,fetchOrderTradesfetchMyTrades (outcome?, since?, limit?, params?)— your fillsfetchPositions (outcomes?, params?)/fetchPosition (outcome, params?)— open positionsfetchStatus (params?)— exchange operational status
WebSocket (Pro) — live streams, also keyed by the outcome handle
watchTicker,watchTickers,watchOrderBook,watchTrades,watchOHLCV,watchOrders,watchMyTrades,watchPositions
The rest of this page documents fetchEvents and fetchEvent, the two entry points unique to prediction markets; the remaining methods behave like their crypto counterparts in the Manual, only addressed by outcome.
fetchEvents
fetchEvents (params = {})params— afetchEventsParamsobject; must be scoped by at least one ofquery(a search string),queries(a list of strings),tags,eventIdorslug— an unscoped call throwsArgumentsRequiredon every venue (some venues accept extra scope keys, e.g. Kalshi'scategory/series_ticker). Optional:limit,sort(volume/liquidity/newest),status(active/inactive/closed/all),searchIn(title/description/both). The scope is pushed to the venue's API server-side:tagsresolve to Kalshi series, Polymarkettag_sluglistings (one per tag), Limitless categories, and Myriad keyword searches. For an unscoped "most active markets" browse usefetchMarkets ()— it returns a capped, volume-ordered listing where the venue supports it- returns an array of event structures and caches the discovered events, markets and outcomes on the instance (
exchange.events,exchange.outcomes)
// event structure
{
'id': '903193', // exchange-specific event id
'slug': 'will-x-happen-by-july', // url slug of the event
'event': 'WILL_X_HAPPEN_JULY', // shortened unified event handle (getEvent resolves this)
'title': 'Will X happen by July?', // human-readable title
'markets': [ ... ], // a list of market structures, each with an outcomes list
'active': true, // whether the event is still tradable
'resolved': false, // whether the event has been resolved
'end': 1781234567890, // resolution deadline timestamp in ms
'endDatetime': '2026-07-01T00:00:00Z',
'info': { ... }, // the raw exchange response
}A typical workflow:
const exchange = new ccxt.prediction.polymarket ()
const events = await exchange.fetchEvents ({ 'query': 'Trump' })
const outcome = events[0]['markets'][0]['outcomes'][0]
const ticker = await exchange.fetchTicker (outcome['outcome'])
const orderbook = await exchange.fetchOrderBook (outcome['outcome'])
const candles = await exchange.fetchOHLCV (outcome['outcome'], '1h')
// place a limit buy of 5 YES shares at 0.40 USDC; prices are 0..1 per share
const order = await exchange.createOrder (outcome['outcome'], 'limit', 'buy', 5, 0.40)Outcome-addressed methods (fetchTicker, createOrder, …) auto-load the outcome cache on first use — like loadMarkets() + market(symbol) in regular ccxt — so you don't need to fetchEvents/loadMarkets first. The auto-load is scoped to the requested outcome: a by-id fetch where the venue has one (Kalshi tickers, Polymarket token ids), otherwise a search derived from the handle — never a bulk listing download. A genuinely unknown outcome throws BadSymbol. Call loadOutcomes() to warm the cache explicitly, or getEvent(idOrHandle) to read a cached event.
fetchEvent
fetchEvent (id, params = {})id— the identifier of a single event. Polymarket accepts the numeric event id or its slug, Kalshi the event ticker, Myriad thenetworkId:marketIdmarket id, and Limitless the market slug or address- returns a single event structure (same shape as the entries returned by
fetchEvents)
const exchange = new ccxt.prediction.polymarket ()
const events = await exchange.fetchEvents ({ 'query': 'Trump' })
const event = await exchange.fetchEvent (events[0]['id'])Supported by polymarket, kalshi, myriad and limitless (check exchange.has['fetchEvent']); Hyperliquid has no single-event endpoint.
The outcome cache
Prediction exchanges address an outcome (e.g. TRUMP_WINS:YES, or its raw outcomeId) the way regular ccxt addresses a symbol. The outcome cache (exchange.outcomes / exchange.outcomes_by_id) is loaded automatically on first use, so you rarely touch it directly:
- Auto-load — the first outcome-addressed call (
fetchTicker,fetchOrderBook,createOrder, …) resolves and caches just the requested outcome (a by-id fetch on Kalshi/Polymarket, the venue's search elsewhere); later calls hit the cache with no extra request. This mirrorsloadMarkets()+market(symbol)without ever downloading the whole listing implicitly. loadOutcome (outcome, reload = false)— resolve one outcome explicitly;reload = trueskips the cache and refetches the outcome's metadata (status, tick size, token ids) — the per-outcome refresh knob.loadOutcomes (outcomes = undefined, reload = false, params = {})— with anoutcomeslist: sync-checks the cache and resolves only the misses, batched where the venue has a by-id endpoint (Kalshi: one markets request per 100 tickers; Polymarket: one gamma request per 50 token ids) — a warm cache returns with zero per-outcome awaits. Without a list: warm the whole (capped) cache via the markets listing (the analogue ofloadMarkets);reload = truerefetches. The bulk path only runs when you call it.hasOutcome (idOrHandle)— sync, cache-only membership probe; never throws, never fetches. Use it when you only need to know whether an outcome is cached (safeOutcomereturns a stub on a miss,outcome()throws).getEvent (idOrHandle)— read a cached event by its id, slug, or shortened handle (theeventfield). Cache-only; callfetchEvents(...)first.options['loadAllOutcomes']— defaults tofalse: a cache miss resolves only the requested outcome. Hyperliquid sets ittruebecause its entire outcome universe is a single cheap request. Flip it totrueon any venue to pay the (capped) listing scan on the first miss instead.
An unknown outcome throws BadSymbol; a genuinely cold cache accessed through the sync outcome() resolver (used inside parsers) throws ExchangeError. You never need to pre-load — just call the method.
All-tickers honesty — fetchTickers requires outcomes
One ccxt call maps to a bounded number of venue requests. Kalshi, Polymarket, Limitless and Myriad have no endpoint returning every ticker at once, so fetchTickers () without an outcomes list throws ArgumentsRequired on those venues instead of silently returning a capped subset — pass the outcome handles you actually want (Kalshi batches 100 per request, Polymarket 200 per batched request pair; Limitless costs two requests per market and Myriad one). Hyperliquid serves the whole universe in one allMids request, so its no-arg fetchTickers () remains available. Account-scoped methods (fetchPositions, fetchOrders, fetchMyTrades, …) are always a single self-contained request; when the outcome cache is cold their rows are labelled with raw venue ids, and with unified handles once you've scoped a fetchEvents call.
CLI
Every language CLI can target a prediction exchange with the -p / --prediction flag. Regular ids win for ids present in both namespaces (e.g. hyperliquid); -p forces the prediction namespace, and prediction-only ids (polymarket, kalshi, limitless, myriad) resolve there automatically:
npm run cli.ts -- -p polymarket fetchEvents '{"query":"trump"}'
npm run cli.py -- --prediction kalshi fetchTicker KXBTC-25DEC31-B100000:YES --sandbox
npm run cli.go -- -p hyperliquid fetchBalanceSupported prediction exchanges
- hyperliquid — unified API methods, parameters and endpoints
- kalshi — unified API methods, parameters and endpoints
- limitless — unified API methods, parameters and endpoints
- myriad — unified API methods, parameters and endpoints
- polymarket — unified API methods, parameters and endpoints