Kalshi Sports Market Data API
Real-time sports prediction market data from Kalshi. Gzip-compressed, Redis-cached, structured with Pino logging.
Quick Start
No API key needed. Just start making requests:
# Get all NBA game lines in one call curl http://localhost:4000/api/prices/summary?sport=NBA # Get live contract prices (ultra-slim, lowest latency) curl http://localhost:4000/api/prices?sport=NBA&side=YES # Search for a team curl http://localhost:4000/api/search?q=Boston # Full event details with all markets and contracts curl http://localhost:4000/api/events?sport=NBA&limit=5
Data Model
Each sport has a simple hierarchy:
Sport (NBA, MLB, NFL, NHL) └── Event (a game — e.g. "Brooklyn at Toronto") └── Market (a prediction — e.g. "Winner?", one per team) └── Contract (YES/NO side — price is 0.00–1.00) Prices are in dollar terms. A YES price of $0.60 = 60% implied probability. Each game typically has 2 markets × 2 contracts = 4 contracts total.
Response Format
All list endpoints return a standardized envelope:
{
"data": [ ... ], // Array of results
"meta": {
"count": 15, // Items in this response
"total": 86, // Total matching (for pagination)
"cached": false, // true if served from Redis cache
"took_ms": 3.2 // Server processing time
}
}
Response Headers
| Header | Description |
|---|---|
| X-Response-Time | Server processing time (e.g. 2.3ms) |
| X-Cache | HIT = served from Redis, MISS = fresh from DB |
| X-Cache-TTL | Cache time-to-live for this endpoint (e.g. 5s) |
| Content-Encoding | gzip when client sends Accept-Encoding: gzip |
Errors
All errors follow a consistent shape:
{
"error": {
"code": "NOT_FOUND",
"message": "Market 'abc' not found"
},
"meta": { "cached": false, "took_ms": 1.2 }
}
| HTTP Status | Code | When |
|---|---|---|
| 400 | INVALID_QUERY | Search query too short or invalid parameters |
| 404 | NOT_FOUND | Resource doesn't exist |
| 500 | INTERNAL_ERROR | Unexpected server error |
Endpoints
Returns service status. Not cached.
Returns available sports with active market and event counts.
Response
| Field | Type | Description |
|---|---|---|
| sport | string | Sport code (NBA, MLB, NFL, NHL) |
| market_count | integer | Number of open markets |
| event_count | integer | Total events tracked |
Lowest-latency endpoint. One flat row per active contract with computed mid-price and spread. No nesting. Designed for ticker displays.
Parameters
| Param | Type | Description |
|---|---|---|
| sport | string | Filter by sport: NBA, MLB, NFL, NHL |
| side | string | Filter by contract side: YES or NO |
Response fields
| Field | Type | Description |
|---|---|---|
| symbol | string | Full contract symbol |
| team | string | Team name extracted from market subtitle |
| last | number | Last trade price (0.00–1.00) |
| bid | number | Best bid price |
| ask | number | Best ask price |
| mid | number | Mid-point of bid/ask |
| spread | number | Bid-ask spread |
| vol24h | integer | 24-hour volume |
| oi | integer | Open interest |
One row per game with both team lines. Includes last, bid, ask, mid for each team.
Parameters
| Param | Type | Description |
|---|---|---|
| sport | string | Filter by sport |
Search by team name, event title, market subtitle, or ticker. Minimum 2 characters.
Parameters
| Param | Type | Required | Description |
|---|---|---|---|
| q | string | Yes | Search query (min 2 chars) |
Returns { events: [...], markets: [...] } inside the data field.
Paginated list of games with nested markets and contracts.
Parameters
| Param | Type | Default | Description |
|---|---|---|---|
| sport | string | — | Filter by sport |
| status | string | — | Filter: scheduled, open, closed, settled |
| limit | integer | 50 | Results per page (max 200) |
| offset | integer | 0 | Pagination offset |
Get a single event by UUID or Kalshi event ticker (e.g. KXNBAGAME-26APR12BKNTOR).
Paginated list of prediction markets. Same parameters as /api/events.
Get a single market by UUID. Includes full contract details, event context, rules, and game info.
Get a single YES/NO contract by UUID. Returns last price, bid, ask, volume, open interest, and parent market title.
Time series of price snapshots for a contract.
Parameters
| Param | Type | Default | Description |
|---|---|---|---|
| period | string | 24h | Lookback: 1h, 6h, 24h, 7d, 30d |
Response
| Field | Type | Description |
|---|---|---|
| price | number | Trade price at this point |
| bid_price | number | Bid at time of recording |
| ask_price | number | Ask at time of recording |
| volume | integer | Volume at time of recording |
| recorded_at | datetime | ISO 8601 timestamp |
WebSocket API
Connect to ws://localhost:4000/ws/v1 for real-time streaming.
Channels
| Channel | Description |
|---|---|
| sport:NBA | All NBA market updates |
| event:<ID> | All markets for a specific game |
| market:<ID> | Single market updates |
| all | Everything |
Actions
// Subscribe to NBA {"action": "subscribe", "channel": "sport:NBA"} // Unsubscribe {"action": "unsubscribe", "channel": "sport:NBA"} // Request a full snapshot {"action": "snapshot", "channel": "sport:NBA"} // Switch to delta mode (only changed fields) {"action": "set_mode", "mode": "delta"} // Heartbeat {"action": "ping"}
Message Types
| Type | When |
|---|---|
| welcome | On connect — includes protocol docs |
| snapshot | Full channel data after subscribe/snapshot |
| quote | Real-time price update |
| info | Subscription confirmations |
| pong | Response to ping |
| error | Error messages |