5 Biggest Differences Between Kalshi and Polymarket APIs
Prediction market chatter is everywhere. It’s definitely a large part of my X feed - with analyses, flow tracking and new products popping up everywhere. I’ve been impressed at the genuine interest in the space, both from traders and builders.
I’ve been tinkering with the Polymarket and Kalshi APIs and have noticed that there are significant differences between the two. I made a few technical and product assumptions about how these would work before going deep, and it wasted some time.
Here’s 5 quick differences between the two to help you get up and running.
Difference #1: User-level Data Visibility
Kalshi: Self-only (authenticated)
This is the biggest thing to note. On Polymarket, every significant transaction is public on the Polygon blockchain. All traders can be identified pseudonymously via their Polygon address (ex. 0x568…) or their Polymarket username. You can see timestamps of trades, exact amounts, etc.
On Kalshi, you can only see specific trade or position-level data for yourself. There’s no user_id
or address
parameter anywhere in the API.
// Your positions only (requires authentication)
GET https://api.elections.kalshi.com/trade-api/v2/portfolio/positions
Response:
{
"event_positions": [{
"event_ticker": "KXMAYORNYCPARTY-25",
"event_exposure": 123,
"realized_pnl": 45
}],
"market_positions": [{
"ticker": "KXMAYORNYCPARTY-25-D",
"position": 250,
"market_exposure": 180
}]
}
// No way to query other users' positions or trades
This is by design - Kalshi is a U.S. CFTC-regulated Designated Contract Market (DCM), operating under the same regulatory framework as options/futures exchanges like CME. DCMs are legally required to maintain confidentiality of customer identities, positions, and transactions. This is the same reason why NASDAQ can’t show you which trading account bought specific shares.
The goals are to prevent market integrity and front-running. This is a bummer, but we’ll get into the cool stuff you can still do on Kalshi.
Polymarket: Any wallet (public)
You can query positions and trades for any wallet address using the Data API, (/positions?address=...
and /trades?user=...
). No authentication required, just paste in a Polygon address.
// Any user's positions (add address parameter)
GET https://data-api.polymarket.com/positions?address=0x6af75d4e4aaf700450efbac3708cce1665810ff1
Response:
[{
"conditionId": "0xdd22...",
"asset": "71321045...",
"size": 90548.087076,
"avgPrice": 0.020628,
"currentValue": 5840.35,
"cashPnl": 3972.53
}]
// Same pattern works for trades:
GET https://data-api.polymarket.com/trades?user=0x6af75d4e...
Response includes: wallet address, trade size, side (buy/sell), timestamp
Trader-level attribution unlocks use cases like identifying profitable traders, copy-trading, unusual flow investigation, social features, etc. For the foreseeable future, that will have to live on web3 platforms like Polymarket. lets you analyze a given trader’s trades, do copy trading, etc.
For personal trading dashboards or trackers, trading bots and the like: both APIs work.
Difference #2: Market Data Schema Depth
The market data responses for Kalshi and Polymarket are fairly different. Kalshi has some nice binary yes-no metrics and structures things trade-by-trade referencing a sequential order of trades (ex. last_price
). Polymarket has more fields that are particularly helpful for building your own charts or UI.
Kalshi: 47 fields (trading-focused)
GET https://api.elections.kalshi.com/trade-api/v2/markets/KXMAYORNYCPARTY-25-D
Response:
{
"ticker": "KXMAYORNYCPARTY-25-D",
"title": "Will a representative of the Democratic party win the NYC Mayor race in 2025?",
"status": "active",
// Binary-specific pricing
"yes_bid": 52,
"yes_ask": 53,
"no_bid": 47,
"no_ask": 48,
"last_price": 52,
"previous_price": 50, // ← Built-in for change calculation
// Volume/liquidity (2 windows)
"volume": 8958794,
"volume_24h": 224141,
"liquidity": 168632792,
"open_interest": 3779791,
// Event reference
"event_ticker": "KXMAYORNYCPARTY-25"
}
Key properties Kalshi has that Polymarket doesn’t
previous_price
included - Kalshi gives youprevious_price: 50
andlast_price: 52
out of the box. Polymarket makes you track previous prices yourself in your database, though it compensates with pre-computed percentage changes across multiple timeframes.- Binary-native fields -
yes_bid
,yes_ask
,no_bid
,no_ask
are cleaner for binary markets than parsing outcome arrays - Kalshi’s lean responses are likely an explicit design decision - faster to parse.
Polymarket: 72 fields
GET https://gamma-api.polymarket.com/markets/will-democratic-party-win-nyc-mayor-2025
Response:
{
"id": "502517",
"question": "Will Democratic party win NYC Mayor 2025?",
"slug": "will-democratic-party-win-nyc-mayor-2025",
// UI/Discovery assets
"image": "https://polymarket-upload.s3.us-east-2.amazonaws.com/...",
"icon": "https://...",
"description": "This market will resolve to Yes if...",
// Multi-outcome support
"outcomes": ["Yes", "No"],
"outcomePrices": ["0.52", "0.48"],
// Current best prices
"bestBid": 0.52,
"bestAsk": 0.53,
"lastTradePrice": 0.52,
// Multi-window volumes (4 timeframes)
"volume": "125000.50",
"volume24hr": "2500.75",
"volume1wk": "15000.00", // ← Weekly volume
"volume1mo": "45000.00", // ← Monthly volume
"volume1yr": "125000.00", // ← Yearly volume
// Price change indicators (5 timeframes)
"oneHourPriceChange": 0.02, // ← 2% change in last hour
"oneDayPriceChange": 0.05, // ← 5% change in last day
"oneWeekPriceChange": 0.12, // ← 12% change in last week
"oneMonthPriceChange": 0.08,
"oneYearPriceChange": 0.15,
// Split by liquidity source
"liquidityNum": 85000,
"liquidityAmm": 15000, // ← AMM liquidity
"liquidityClob": 70000, // ← CLOB liquidity
"volume24hrAmm": 500, // ← AMM volume
"volume24hrClob": 2000, // ← CLOB volume
// Market status flags
"featured": false,
"new": false,
"archived": false,
"restricted": false,
"active": true,
"closed": false,
// Trading configuration
"orderMinSize": 5,
"orderPriceMinTickSize": 0.01,
"spread": 0.01,
// Event embedding (full object, not just reference)
"events": [{
"id": "11120",
"title": "NYC Mayoral Election 2025",
"volume1wk": 50000, // ← Event-level aggregates
"liquidityAmm": 25000,
"liquidityClob": 125000,
"commentCount": 47
}],
// Developer conveniences
"startDate": "2024-06-17T03:51:23.112Z",
"startDateIso": "2024-06-17" // ← Date-only format
}
Key properties Polymarket has that Kalshi doesn’t
Most Valuable:
Pre-computed analytics across multiple timeframes - Polymarket provides ready-to-use price changes across 5 timeframes (1hr, 1day, 1wk, 1mo, 1yr) and volumes across 4 timeframes (24hr, 1wk, 1mo, 1yr). Kalshi is more lean and gives you 24h volume and a single previous_price
reference point (which is just “last trade vs trade before that”, not time-bounded).
For event-level stats, Polymarket embeds them with an extra API call. With Kalshi you’ll call /events/{event_ticker}
separately.
If you’re building “market movers” or price alert features, Polymarket’s multi-timeframe approach helps you filter and sort markets by hourly momentum vs weekly trends without tracking any history yourself. With Kalshi, it seems like you need to store historical snapshots in your own database for more complex analyses beyond “did the last trade move the price?”
Other unique Polymarket data
- Liquidity source splits (
liquidityAmm
vsliquidityClob
,volumeAmm
vsvolumeClob
) - FYI for understanding Polymarket’s dual matching engine architecture. Since Kalshi only has one order book, this distinction doesn’t exist there. - UI/discovery assets (
image
,icon
,slug
,description
) - Build market discovery UIs, aggregators, embeds, etc easily with all of the assets that Polymarket shows on its own web experience - Market status flags (
featured
,new
,archived
,restricted
) - Filter and badge markets in your UI - Embedded event data - Event-level volume/liquidity without a second API call. Kalshi provides
event_ticker
as a reference; you’d call/events/{event_ticker}
separately to get event-level aggregates.
Difference #3: API Architecture (1 vs 3 APIs)
Kalshi: Single unified API
Kalshi has a single unified API at https://api.elections.kalshi.com/trade-api/v2
. You can fetch markets at /markets
, trades at /markets/{ticker}/trades
, and your positions at /portfolio/positions
. Everything lives at one endpoint with consistent auth and response formats.
The unified design means error handling (one API to monitor), caching (one set of rate limits) are easier. Less orchestration code. If you need market data + your positions, it’s 2 calls to the same base URL with the same auth headers.
Polymarket: Three separate APIs
Polymarket splits functionality across three APIs with different purposes: Gamma for metadata at https://gamma-api.polymarket.com
, CLOB (Central Limit Order Book) for trading at https://clob.polymarket.com
, and Data API for analytics at https://data-api.polymarket.com
. Gamma and Data API are public (read, no auth), while CLOB requires authentication for its write actions.
This split architecture means you’ll write more code to stitch data together, but it also enables more granular caching (metadata changes slowly, prices change fast) and you only auth when actually trading, which is nice. If you’re building a read-only data dashboard, you never have to touch the CLOB API.
Real-world example: Getting complete market picture
// KALSHI: One call gets everything
async function getMarketData(ticker) {
const market = await fetch(
`https://api.elections.kalshi.com/trade-api/v2/markets/${ticker}`
);
// Has: metadata, current prices, volume, previous_price, liquidity
return market;
}
// POLYMARKET: Must stitch together 2-3 APIs
async function getMarketData(slug) {
// Step 1: Gamma for metadata
const market = await fetch(
`https://gamma-api.polymarket.com/markets/${slug}`
);
// Step 2: CLOB for current orderbook (if needed)
const prices = await clobClient.getOrderBook(market.tokens[0].token_id);
// Step 3: Data API for recent trades (if needed)
const trades = await fetch(
`https://data-api.polymarket.com/trades?market=${market.condition_id}`
);
// Step 4: Stitch together
return { ...market, orderbook: prices, recentTrades: trades };
}
Trade-offs
Aspect | Kalshi (Unified) | Polymarket (Split) |
---|---|---|
Initial complexity | Lower - one API to learn | Higher - three APIs to understand |
Round trips | Fewer - one call gets more data | More - orchestrate 2-3 calls for complete picture |
Payload size | Lean responses | Fat responses (embedded events, metadata) |
Caching strategy | Simple - one endpoint | Complex - different TTLs per API |
Failure handling | Single point of failure | Partial degradation possible |
Scaling | Must scale everything together | Can scale APIs independently |
Auth overhead | Consistent across all operations | Only CLOB needs auth; others are public |
Difference #4: Authentication Complexity
Kalshi is centralized, in that they custody your funds like Robinhood or Bank of America or Coinbase. So a standard API key-based auth system works. I personally use their TypeScript SDK, which abstracts away some complexity.
Kalshi: One-step (RSA signature)
import crypto from 'crypto';
import fs from 'fs';
const privateKey = fs.readFileSync('private_key.pem');
const timestamp = Date.now().toString();
const method = 'GET';
const path = '/trade-api/v2/portfolio/positions';
// Create signature
const message = `${timestamp}${method}${path}`;
const signature = crypto.sign('sha256', Buffer.from(message), {
key: privateKey,
padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
}).toString('base64');
// Make authenticated request (one signature works for all endpoints)
const response = await fetch(`https://api.elections.kalshi.com${path}`, {
headers: {
'KALSHI-ACCESS-KEY': 'your-api-key-id',
'KALSHI-ACCESS-SIGNATURE': signature,
'KALSHI-ACCESS-TIMESTAMP': timestamp
}
});
On the other hand, Polymarket is decentralized. Your web3 wallet (ex. Phantom, Metamask) holds your funds and you must sign for every “write” transaction to prove ownership. Almost all of these signatures are free of charge to the developer (off-chain transactions that are settled by Polymarket on-chain).
Polymarket: Two-step (blockchain signature + API credentials)
import { ethers } from 'ethers';
// STEP 1: Prove wallet ownership via blockchain signature (EIP-712)
const wallet = new ethers.Wallet(privateKey);
const timestamp = Math.floor(Date.now() / 1000);
const domain = {
name: "ClobAuthDomain",
version: "1",
chainId: 137 // Polygon
};
const types = {
ClobAuth: [
{ name: "address", type: "address" },
{ name: "timestamp", type: "string" },
{ name: "nonce", type: "uint256" },
{ name: "message", type: "string" }
]
};
const value = {
address: wallet.address,
timestamp: timestamp.toString(),
nonce: 0,
message: "This message attests that I control the given wallet"
};
const signature = await wallet._signTypedData(domain, types, value);
// STEP 2: Exchange blockchain signature for API credentials
const response = await fetch('https://clob.polymarket.com/auth/api-key', {
method: 'POST',
headers: {
'POLY_ADDRESS': wallet.address,
'POLY_SIGNATURE': signature,
'POLY_TIMESTAMP': timestamp.toString(),
'POLY_NONCE': '0'
}
});
const apiCreds = await response.json();
// Returns: { apiKey: "...", secret: "...", passphrase: "..." }
// STEP 3: Use API credentials for subsequent trading operations
It’s confusing if you’re new to web3, but you also need a one-time permission signature for Polymarket’s smart contracts to move your tokens before you can trade. This costs about ten cents of gas on Polygon and you don’t need to do it again unless you revoke the permission via your wallet.
// This is the approval I was talking about
const usdcContract = new ethers.Contract(USDC_ADDRESS, ERC20_ABI, wallet);
await usdcContract.approve(EXCHANGE_ADDRESS, MaxUint256);
// ✅ Now Polymarket can move your USDC when orders fill
Both platforms require authentication for:
- Placing orders
- Canceling orders
- Viewing YOUR positions
- Viewing YOUR trade history
Neither requires auth for:
- Viewing market data (prices, volumes)
- Viewing orderbook depth
- Polymarket only: Viewing ANY user’s positions/trades via Data API (public blockchain data)
I’d say that both are relatively straightforward. Kalshi’s one-step auth (RSA signature) is simpler to implement. If you’re new to web3, the steps around approvals and ongoing signatures for Polymarket can be a chore - but as someone who’s been in web3 for 8 years, they’ve abstracted a lot of the complexities away.
For managing credentials, with Kalshi you delete/rotate API keys like any REST API. On Polymarket you have to manage wallet private keys and handle credential expiration.
Difference #5: Historical Data Granularity
Both platforms provide trade-by-trade historical data, but with a critical difference in attribution.
Kalshi: Trade-by-trade (anonymized)
GET https://api.elections.kalshi.com/trade-api/v2/markets/KXMAYORNYCPARTY-25-D/trades?limit=100
Response:
{
"trades": [
{
"trade_id": "abc123",
"ticker": "KXMAYORNYCPARTY-25-D",
"yes_price": 52,
"no_price": 48,
"count": 10,
"timestamp": 1704067215,
"taker_side": "yes"
}
],
"cursor": "next_page_token"
}
// Available: price, size, timestamp, side
// NOT available: user_id, wallet address, any trader identification
Kalshi’s market data goes back to June 2021. “Will new U.S. home sales be above 750,000?” (HOME-21JUN-T750
, resolved No) was the first market I could find, back when Kalshi was not live and testing with small numbers of users. You have the full market history from then until now.
Polymarket: Trade-by-trade (with wallet addresses)
GET https://data-api.polymarket.com/trades?market=0xdd22...&limit=100
Response:
[
{
"id": "xyz789",
"market": "0xdd22...",
"asset": "71321045...",
"price": 0.52,
"size": 100.5,
"side": "BUY",
"timestamp": 1704067215,
"maker": "0x1a2b3c...", // ← Maker wallet address
"taker": "0x4d5e6f...", // ← Taker wallet address
"makerAddress": "0x1a2b3c...",
"takerAddress": "0x4d5e6f...",
"transactionHash": "0x7g8h9i..."
}
]
// Available: price, size, timestamp, side, maker/taker addresses, tx hash
Polymarket’s web3 architecture means historical data extends back to at least October 2021. The oldest market I found was a simple “dodgers” test market from the platform’s soft launch period. Polymarket is a step more robust - every trade has an on-chain pointer on Polygon and is queryable indefinitely through their Data API or subgraph.
Both also provide aggregated candlestick data
Kalshi provides a /markets/{ticker}/candlesticks?period_interval=60
endpoint that returns ready-made OHLC bars at 1, 5, 15, 60 minute, or daily intervals: [{ open: 52, high: 55, low: 50, close: 53, volume: 12500, timestamp: 1704067200 }]
. It has maker/taker wallet addresses on every trade too, a nice convenience.
Polymarket requires you to aggregate from individual trades yourself, or use their chart data endpoints that I haven’t looked into. On the plus side, every trade links to an on-chain transaction hash that you can find on Polygonscan.
© 2025 Kevin D. Kim