Prediction Market Order Book API (2026): Depth Across Every Venue

The Short Answer
A prediction market order book API returns the bids and asks resting on each outcome, which tells you the real price and how much size you can trade at it. Every venue publishes its book in a different shape. Polymarket's CLOB returns bids and asks per outcome token at GET /book, with the best price last. Kalshi returns only bids, for Yes and for No, and a No bid at X is a Yes ask at 1 − X. The Predictefy API normalizes books from 15+ venues into one { bids, asks } shape with probability prices, through fetchOrderBook.
Key Takeaways
- The order book, not the headline price, decides what you actually pay once size matters.
- Polymarket:
GET https://clob.polymarket.com/book?token_id=, orPOST /booksfor many tokens at once, no key needed. - Kalshi:
GET /markets/{ticker}/orderbookreturns Yes bids and No bids only, so derive asks from the opposite side. - A small normalizer, shown below and run live, turns both into the same best-first
{ bids, asks }shape. - Predictefy returns every venue's book in one shape, with bulk reads on Builder and stored snapshots you can query by time.
Why does the order book matter more than the price?
The price you see on a market page is usually a midpoint or the last trade. The order book shows what is actually available: every resting bid and ask with its size. If the best ask is $0.13 for 500 shares and you want 5,000, you will walk up the book and pay more on average. Any bot, arbitrage check or execution tool has to read depth, not just the top price.
How do order books differ between venues?
| Polymarket CLOB | Kalshi Trade API | |
|---|---|---|
| Endpoint | GET /book?token_id=, POST /books | GET /markets/{ticker}/orderbook |
| Keyed by | Outcome token ID | Market ticker |
| Sides returned | Bids and asks | Yes bids and No bids only |
| Level format | { "price": "0.13", "size": "29184.77" } | ["0.1400", "10.00"] pairs |
| Order | Best price last | Ascending by price |
| Extras | tick_size, min_order_size, neg_risk, last_trade_price | Optional depth from 1 to 100; 0 returns all levels |
Kalshi's design follows from binary contracts: buying Yes at $0.14 is the same trade as selling No at $0.86, so one list of No bids is also the list of Yes asks. Kalshi's docs state it directly: a Yes bid at price X is equivalent to a No ask at 100 − X cents.
How do you read and normalize books from Polymarket and Kalshi?
This Node 18+ script reads both venues' books with no keys and returns the same best-first shape for each:
// Normalize Polymarket and Kalshi books to { bids, asks } with probabilities, best first
const pmBook = async (tokenId) => {
const b = await (await fetch(`https://clob.polymarket.com/book?token_id=${tokenId}`)).json();
const lv = (xs) => xs.map((x) => ({ price: Number(x.price), size: Number(x.size) })).reverse();
return { bids: lv(b.bids), asks: lv(b.asks) }; // Polymarket lists the best price last
};
const kalshiBook = async (ticker) => {
const { orderbook_fp: ob } = await (await fetch(`https://external-api.kalshi.com/trade-api/v2/markets/${ticker}/orderbook?depth=10`)).json();
const yes = (ob.yes_dollars ?? []).map(([p, q]) => ({ price: Number(p), size: Number(q) }));
const no = (ob.no_dollars ?? []).map(([p, q]) => ({ price: Number(p), size: Number(q) }));
return {
bids: yes.sort((a, b) => b.price - a.price),
// A No bid at X is a Yes ask at 1 - X
asks: no.map((l) => ({ price: +(1 - l.price).toFixed(4), size: l.size })).sort((a, b) => a.price - b.price),
};
};
const [pm] = await (await fetch('https://gamma-api.polymarket.com/markets?limit=1&active=true&closed=false&order=volume24hr&ascending=false&liquidity_num_min=50000&end_date_min=2026-11-01')).json();
const token = pm.version === 'v2' ? pm.positionIds[0] : JSON.parse(pm.clobTokenIds)[0];
const p = await pmBook(token);
console.log('Polymarket', pm.question, '\n best bid', p.bids[0], 'best ask', p.asks[0]);
const { markets: [km] } = await (await fetch('https://external-api.kalshi.com/trade-api/v2/markets?status=open&limit=1&series_ticker=KXFEDDECISION')).json();
const k = await kalshiBook(km.ticker);
console.log('Kalshi', km.ticker, '\n best bid', k.bids[0], 'best ask', k.asks[0], '| market record ask', km.yes_ask_dollars);
Save it as books.mjs and run node books.mjs. In our checks on 16 October 2026, the Polymarket market showed a best bid of $0.12 and a best ask of $0.13, and the Kalshi Fed market showed a best Yes bid of $0.10 with a derived best ask of $0.14, matching the yes_ask_dollars on Kalshi's market record. Markets created on Polymarket's Protocol V2 carry positionIds instead of clobTokenIds, which the script handles. To read many Polymarket books in one request, POST /books takes an array of { "token_id": "..." } objects.
How do you get order books from every venue in one shape?
The normalizer above is two venues' worth of edge cases. Predictefy does it for 15+ venues: every book comes back as bids and asks of { price, size }, prices as probabilities from 0 to 1, keyed by the same outcomeId the market record gives you.
import Predictefy from '@predictefy/sdk';
const client = new Predictefy({ apiKey: process.env.PREDICTEFY_API_KEY });
// One book
const [market] = await client.kalshi.fetchMarkets({ limit: 1, status: 'active' });
const outcomeId = market.outcomes[0].outcomeId;
const book = await client.kalshi.fetchOrderBook({ outcomeId });
console.log(book.bids[0], book.asks[0], book.asOf);
// Many books in one call (Builder and above)
const many = await client.polymarket.fetchOrderBooks(['OUTCOME_ID_1', 'OUTCOME_ID_2']);
for (const [id, b] of Object.entries(many)) console.log(id, b.bids[0]?.price, b.asks[0]?.price);
// Stored snapshots for a past window
const series = await client.kalshi.fetchOrderBook({ outcomeId, since: '2026-10-15T00:00:00Z', until: '2026-10-16T00:00:00Z' });
This type-checks against @predictefy/sdk 1.0.0-beta.11. A book snapshot costs 5 credits. Bulk reads charge the base cost per 100 books, so 100 books in one fetchOrderBooks call cost the same 5 credits as one. Passing at, or since and until, returns stored snapshots from that time, which is useful for backtests and post-trade checks.
How do you use an order book to price a trade?
Walk the asks until your size is filled and average what you paid. That average, plus the venue's fee, is your real entry price, and it is the number to compare across venues or against a fair value. If the book cannot fill your size at a price you accept, the trade is smaller than you planned. Trading involves risk, and the book can move between the moment you read it and the moment you order.
Frequently Asked Questions
Does Kalshi's order book API need a key?
Not for reading. In our checks on 16 October 2026, GET /markets/{ticker}/orderbook returned the book without authentication.
Why does Kalshi's order book have no asks?
Kalshi returns Yes bids and No bids. Buying Yes at X is the same as selling No at 1 − X, so each side's bids are the other side's asks.
Where is the best price in Polymarket's order book?
At the end of each array. Polymarket's /book lists bids and asks with the best price as the last entry.
Can I get many order books in one request?
Yes. Polymarket's POST /books takes many token IDs, and Predictefy's fetchOrderBooks takes many outcome IDs on Builder and above.
Can I get historical order books?
Predictefy stores book snapshots you can query by time with at, or since and until, on fetchOrderBook, within your plan's history window.