# Kalshi Prediction Markets Scraper — Prices, Volume, Odds (`scrapersdelight/kalshi-markets-scraper`) Actor

Scrape every Kalshi prediction market with live yes/no prices, last trade, 24h volume, open interest and close time — joined to its event, series and category. Filter by category, status or volume. No API key, no login.

- **URL**: https://apify.com/scrapersdelight/kalshi-markets-scraper.md
- **Developed by:** [Scrapers Delight](https://apify.com/scrapersdelight) (community)
- **Categories:** Automation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$0.80 / 1,000 market scrapeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Kalshi Prediction Markets Scraper

Every market on [Kalshi](https://kalshi.com) — the CFTC-regulated US prediction exchange — with live yes/no prices, last trade, volume, open interest and close time, joined to the event, series and category it belongs to.

### Why this one

Kalshi's own API exposes markets and events on **separate** endpoints, and the market records carry **no category**. Scrape `/markets` directly and you get thousands of tickers you can't group or filter by topic. This actor walks the event layer instead, so every row arrives already labelled `Economics`, `Elections`, `Financials`, `Sports`, `Climate and Weather` and so on — and you still get every market, because the events carry them nested.

Measured 2026-09-01: one request returned 200 events carrying **1,481 markets**, and all 1,481 came back priced.

### Quick start

```jsonc
{
  "categories": ["Economics", "Financials"],
  "tradedOnly": true,
  "maxItems": 0        // 0 = every matching market
}
```

Leave `categories` empty to take the whole exchange.

### Output

One row per market, **53 fields**. Measured over 1,481 live markets on 2026-09-01: **42 fields populate on 100% of rows**.

```json
{
  "ticker": "KXELONMARS-99",
  "marketTitle": "Will Elon Musk visit Mars before Aug 1, 2099?",
  "eventTitle": "Will Elon Musk visit Mars in his lifetime?",
  "seriesTicker": "KXELONMARS",
  "category": "World",
  "status": "active",
  "marketType": "binary",

  "yesBid": 0.10, "yesAsk": 0.12,
  "yesBidSize": 41.96, "yesAskSize": 391.89,
  "noBid": 0.88,  "noAsk": 0.90,
  "lastPrice": 0.10, "previousPrice": 0.11,
  "previousYesBid": 0.10, "previousYesAsk": 0.12,

  "volume": 118113.87, "volume24h": 4, "openInterest": 40035.19,
  "liquidity": 0, "notionalValue": 1,

  "customStrike": { "Holder": "Klaus Iohannis" },
  "floorStrike": null, "capStrike": null,
  "priceRanges": [{ "start": "0.0000", "end": "1.0000", "step": "0.0100" }],
  "priceLevelStructure": "linear_cent",

  "openTime": "...", "closeTime": "2099-08-01T04:59:00Z",
  "expirationTime": "...", "expectedExpirationTime": "...", "latestExpirationTime": "...",
  "createdTime": "...", "updatedTime": "...",
  "canCloseEarly": true,
  "earlyCloseCondition": "This market will close and expire early if the event occurs.",
  "settlementTimerSeconds": 1800,

  "result": null, "expirationValue": null,
  "settlementValue": null, "settlementTime": null,

  "marketUrl": "https://kalshi.com/markets/kxelonmars#kxelonmars-99",
  "scrapedAt": "2026-09-01T00:00:00.000Z"
}
```

**Prices** are decimal dollars per contract (`0.10` = 10¢ = a 10% implied probability). Kalshi returns them as strings; this actor converts them to numbers so the dataset is arithmetic-ready.

**Both sides of the book come with their resting size** (`yesBidSize` / `yesAskSize`). A 2¢ spread on 5 contracts is not the same market as a 2¢ spread on 5,000, and most scrapers in this lane ship the price without the size.

**Strike definition** — `customStrike` (51% of rows) carries what the market is actually about, e.g. `{"Holder": "Klaus Iohannis"}` for "who will be next NATO Secretary General". `floorStrike` (40%) and `capStrike` (7%) cover scalar markets. Without these a row's title alone can be ambiguous.

Fields that are intentionally sparse, because the underlying market has no value for them:

| Field | Fill | Why |
| --- | --- | --- |
| `strikeType` | 88% | not every market type has a strike |
| `earlyCloseCondition` | 84% | only markets that can close early |
| `occurrenceDatetime` | 83% | event-dated markets only |
| `collateralReturnType` | 62% | set per event |
| `customStrike` | 51% | markets whose strike is a named entity |
| `floorStrike` / `capStrike` | 40% / 7% | scalar / ranged markets |
| `result`, `expirationValue`, `settlementValue`, `settlementTime` | 5% | **only settled markets resolve** |

Set `includeRules` to add `rulesPrimary`, `rulesSecondary` and the event's settlement sources. It makes rows considerably larger, so it is off by default.

### Filters

- **`categories`** — event category. Live values include Economics, Elections, Politics, Financials, Sports, Climate and Weather, Science and Technology, Entertainment, World, Social.
- **`marketStatus`** — Kalshi's own lifecycle value (`active`, `settled`, `closed`…). `any` returns everything. Use `settled` to pull resolved history.
- **`tradedOnly`** / **`minVolume`** — Kalshi lists many markets that have never traded (281 of the 1,481 in our sample). Switch `tradedOnly` on for a book of live, priced contracts only.
- **`seriesTickers`** / **`eventTickers`** — pin to specific questions.

### Pricing

Pay per result — you only pay for markets delivered.

| Event | Price | When it fires |
| --- | --- | --- |
| `market-scraped` | **$0.0008** | One per market pushed to the dataset |

That is **$0.80 per 1,000 markets**, the cheapest in this lane at time of writing (the next cheapest charges $0.99/1,000). Rows are billed through Apify's gated `pushData` form, so at a spend cap the run stops cleanly and you are never charged for rows you didn't receive.

Worked examples: the whole exchange at ~1,500 active markets ≈ **$1.20**. One category — say Financials, ~90 markets — ≈ **$0.07**. A daily snapshot of every traded market for a month ≈ **$36**.

### Notes / honest limits

- **No login, no API key, no anti-bot.** This is Kalshi's own public trade API. A proxy is optional and off by default; add one only if you're running at high volume.
- **This is market data, not a trading interface.** No order placement, no portfolio, no authenticated endpoints — all of that needs your own Kalshi credentials and is out of scope.
- **Order-book DEPTH is not included.** You get top of book — best bid, best ask, and the size resting on each — plus the last trade and the previous quote. The full ladder is a separate per-market request and would multiply run cost; ask if you need it.
- **`strike_date` / `strike_period`** exist on Kalshi's event schema but came back empty on all 1,481 markets sampled, so they are deliberately not shipped rather than added as always-null columns.
- **Not investment advice.** Prices are a snapshot at `scrapedAt` and move continuously.

# Actor input Schema

## `categories` (type: `array`):

Only return markets whose event sits in these Kalshi categories. Leave empty for every category. Live values include: Economics, Elections, Politics, Financials, Sports, Climate and Weather, Science and Technology, Entertainment, World, Social, Health, Companies.

## `marketStatus` (type: `string`):

Kalshi's own lifecycle value for the market. `any` returns everything. Most listed markets are `active`; use `settled` to pull resolved history.

## `seriesTickers` (type: `array`):

Restrict to specific Kalshi series, e.g. `KXELONMARS`. A series is the recurring question; its events are the individual instances. Case-insensitive.

## `eventTickers` (type: `array`):

Restrict to specific event tickers, e.g. `KXELONMARS-99`. Case-insensitive.

## `tradedOnly` (type: `boolean`):

Skip markets with zero lifetime volume. Kalshi lists many markets that have never traded; switch this on for a book of live, priced contracts only.

## `minVolume` (type: `integer`):

Only return markets whose lifetime contract volume is at least this. 0 = no minimum.

## `includeRules` (type: `boolean`):

Add the market's primary/secondary settlement rules and the event's settlement sources. Useful for resolution research; it makes each row considerably larger.

## `maxItems` (type: `integer`):

Stop after this many markets. Kalshi lists thousands, so the default is deliberately small — raise it once you know the scope you want. 0 = no limit.

## `proxyConfiguration` (type: `object`):

Optional. Kalshi's public API has no anti-bot and needs no proxy; add one only if you are running at high volume and want to spread requests.

## Actor input object example

```json
{
  "categories": [
    "Economics"
  ],
  "marketStatus": "any",
  "seriesTickers": [],
  "eventTickers": [],
  "tradedOnly": false,
  "minVolume": 0,
  "includeRules": false,
  "maxItems": 50,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `markets` (type: `string`):

The dataset of scraped Kalshi markets (one item per market).

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "categories": [
        "Economics"
    ],
    "maxItems": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapersdelight/kalshi-markets-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "categories": ["Economics"],
    "maxItems": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("scrapersdelight/kalshi-markets-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "categories": [
    "Economics"
  ],
  "maxItems": 50
}' |
apify call scrapersdelight/kalshi-markets-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,scrapersdelight/kalshi-markets-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/pot1rt7AILlXBkSaN/builds/NHbtlAkLM2v68hThy/openapi.json
