# Kalshi Market Scraper (`leorochasantos/kalshi-markets`) Actor

A scraper for public Kalshi prediction markets: prices, volume, open interest, close times and settlement rules for open or settled markets, by series, by event or by ticker. No login, no API key, no personal data.

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

## Pricing

from $1.63 / 1,000 kalshi market rows

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Kalshi Market Scraper

Scrape public Kalshi prediction markets: walk the open event listing for a
series or the whole board, or resolve specific markets and events by ticker.
Every row is flat, typed and null-safe for pricing research, market monitoring,
spreadsheets and agent pipelines.

### Why this actor?

- Two buyer-shaped modes: `search` (the public event listing, one row per
  market) and `detail` (resolve exact market or event tickers).
- One flat row per market: ticker, event and series tickers, market and event
  titles, category, status, yes/no bids and asks, last and previous price,
  volume, 24h volume, open interest, liquidity, open/close/expiration times,
  settlement result, primary rules and the canonical record URL.
- Real pagination with a hard page bound: the listing is walked in 10-event
  pages via its cursor until `maxItems` markets are delivered or the listing
  ends, and duplicates never double-deliver.
- Blank or malformed tickers, tickers Kalshi does not know, filters that match
  nothing and blocked or throttled fetches are free error rows.
- Public JSON surface only: no login, no API key, no personal data, no
  challenge bypass.

### Input

```json
{"mode":"search","seriesTicker":"KXNFLGAME","status":"open","maxItems":100}
```

For one exact instrument, set `mode` to `detail` and provide `tickers`:

```json
{"mode":"detail","tickers":["KXNFLGAME-26SEP20CLETB"]}
```

| Input | Meaning |
|---|---|
| `mode` | `search` (walk the public event listing) or `detail` (resolve exact tickers). |
| `seriesTicker` | `search` mode: restrict the listing to one Kalshi series, e.g. `KXNFLGAME` (NFL games), `KXBTCD` (Bitcoin price), `KXFED` (Fed decisions). Empty lists the whole board. |
| `status` | `search` mode: `open` (default), `unopened`, `closed`, `settled` or `any`. |
| `tickers` | `detail` mode: market tickers (`KXNFLGAME-26SEP20CLETB-TB`) or event tickers (`KXNFLGAME-26SEP20CLETB`); an event ticker returns every market in that event. Deduplicated and capped at 25 per run. |
| `maxItems` | Hard local cap of delivered market rows (1–1000, default 100). |
| `proxyConfiguration` | Proxy for the public Kalshi trade API; datacenter is the default. |

### Output

One row per market. This one was captured from the public record
`KXNFLGAME-26SEP20CLETB-TB` on 2026-09-20:

```json
{"input":"KXNFLGAME-26SEP20CLETB","mode":"detail","source_type":"detail","source":"KXNFLGAME-26SEP20CLETB","ticker":"KXNFLGAME-26SEP20CLETB-TB","event_ticker":"KXNFLGAME-26SEP20CLETB","series_ticker":"KXNFLGAME","title":"Tampa Bay wins","subtitle":"Tampa Bay","event_title":"CLE Browns vs TB Buccaneers","event_subtitle":"CLE vs TB (Sep 20)","category":"Sports","status":"active","market_type":"binary","strike_type":"structured","yes_bid":0.78,"yes_ask":0.79,"no_bid":0.21,"no_ask":0.22,"last_price":0.79,"previous_price":0.79,"volume":254522.52,"volume_24h":131022.43,"open_interest":217288.11,"liquidity":0,"open_time":"2026-08-25T15:33:00Z","close_time":"2026-09-22T17:00:00Z","expiration_time":"2026-09-22T17:00:00Z","expected_expiration_time":"2026-09-20T20:00:00Z","result":null,"can_close_early":true,"rules_primary":"If Tampa Bay wins the CLE Browns vs TB Buccaneers Pro Football game originally scheduled for Sep 20, 2026, then the market resolves to Yes.","url":"https://api.elections.kalshi.com/trade-api/v2/markets/KXNFLGAME-26SEP20CLETB-TB","error":null,"scraped_at":"2026-09-20T10:00:00.000Z"}
```

| Field | Meaning |
|---|---|
| `mode` | Which mode produced the row: `search` or `detail`. |
| `input` | The input exactly as provided (series ticker, market/event ticker) — or the listing filter a search row came from. |
| `source_type` | `search` for listing rows, `detail` for a resolved ticker. |
| `source` | Normalized source: the series filter, or the ticker that was resolved. |
| `ticker` | Kalshi market ticker — the row's unique key. |
| `event_ticker` | The event this market belongs to. |
| `series_ticker` | The series this market belongs to. |
| `title` | Market title, e.g. `Tampa Bay wins`. |
| `subtitle` | The market's yes-side subtitle, e.g. `Tampa Bay` or `$71,600 or above`. |
| `event_title` | Event title, e.g. `CLE Browns vs TB Buccaneers`. |
| `event_subtitle` | Event subtitle, e.g. `CLE vs TB (Sep 20)`. |
| `category` | Event category, e.g. `Sports`, `Economics`, `World`. |
| `status` | Market status as upstream reports it (`active`, `closed`, `settled`). |
| `market_type` | Market type, e.g. `binary`. |
| `strike_type` | Strike type, e.g. `structured`, `custom`, `greater`, `less`. |
| `yes_bid` / `yes_ask` | Best yes bid / ask in dollars (0–1). |
| `no_bid` / `no_ask` | Best no bid / ask in dollars (0–1). |
| `last_price` | Last traded price in dollars (0–1). |
| `previous_price` | Previous traded price in dollars (0–1). |
| `volume` | Total traded contracts. |
| `volume_24h` | Contracts traded in the last 24 hours. |
| `open_interest` | Open interest in contracts. |
| `liquidity` | Quoted liquidity in dollars. |
| `open_time` | Market open time, ISO 8601. |
| `close_time` | Market close time, ISO 8601. |
| `expiration_time` | Expiration time, ISO 8601. |
| `expected_expiration_time` | Expected expiration time, ISO 8601. |
| `result` | Settlement result (`yes` / `no`); null while the market is open. |
| `can_close_early` | Whether the market may close before its close time. |
| `rules_primary` | The market's primary settlement rule as published upstream. |
| `url` | Canonical public record URL for this market. |
| `error` | Free per-input error message; null on market rows. |
| `scraped_at` | Extraction timestamp. |

### Pricing

Pay per event, and **the platform usage is on us** — the price you see is the price you pay, with no compute bill on top.

| Event | Price | What one charge buys |
|---|---|---|
| Kalshi market row | **$0.0025** | One delivered public Kalshi market row: market, event and series tickers, market and event titles, category, status, yes/no bids and asks, last and previous price, volume, 24h volume, open interest, liquidity, open, close and expiration times, settlement result, primary rules and the canonical record URL. Free and never charged: a blank or malformed ticker, a ticker Kalshi does not know, a listing filter that matches no market, and any blocked, throttled, 5xx or timeout fetch. |
| Actor Start | **$0.002** | One run, whatever it returns. |

Higher Apify subscription tiers pay less on every event (Silver −20%, Gold −35%).

### Notes

- The surface is Kalshi's public trade API (`https://api.elections.kalshi.com/trade-api/v2`),
  the same open JSON surface the radar preflight measured from datacenter space
  (one 200 `application/json`, 71176 bytes for `GET /markets?limit=20`). The
  `kalshi.com` website is deliberately **not** part of the surface: every
  request to it is answered `HTTP 429` with `X-Vercel-Mitigated: challenge`. A
  challenge marker is a finding, not something to work around.
- `search` mode walks `GET /events?status=…&series_ticker=…&limit=10&with_nested_markets=true`
  in 10-event pages (each event carries its nested markets), following the
  response `cursor` until `maxItems` markets are delivered, the cursor runs out,
  or 20 pages have been fetched. A market is delivered once: rows are
  deduplicated by ticker across the whole run.
- `detail` mode resolves a market ticker through `GET /markets/{ticker}`. An
  event ticker answers `404` there and is resolved through
  `GET /events/{ticker}?with_nested_markets=true`, which yields one row per
  market in that event. A bare market ticker also gets one best-effort, cached
  and capped (10 per run) event lookup for its event metadata; if that lookup
  fails the row is still delivered, with the event metadata `null`.
- Free error rows, never charged: a blank or malformed ticker, a `kalshi.com`
  website URL (rejected by name, no request spent), a well-formed ticker Kalshi
  does not know (`404` from both endpoints), a listing filter that matches no
  market (`200` with an empty event list), and any throttled (`429`), refused
  (`403`/`412`), `5xx` or timed-out fetch.
- Requests go through the Apify proxy configured in `proxyConfiguration`
  (datacenter by default, recorded as `proxyTier` in `.actor/publish.json`).

### Use cases

- Track the odds of an event series over time (elections, Fed decisions, game
  lines) by pulling its markets on a schedule.
- Price a whole open board for research, dashboards or external-market
  monitoring.
- Resolve one market or event ticker on demand for an agent or a spreadsheet.

***

*This Actor is an independent tool. It is not affiliated with, endorsed by or
sponsored by Kalshi; it reads the same public JSON records the Kalshi web app
reads, and publishes only publicly available market data.*

# Actor input Schema

## `mode` (type: `string`):

search: walk the public Kalshi event listing and deliver one row per market. detail: resolve explicit market or event tickers.

## `seriesTicker` (type: `string`):

search mode: restrict the listing to one Kalshi series, e.g. KXNFLGAME (NFL games), KXBTCD (Bitcoin price), KXFED (Fed decisions). Leave empty for the whole open board.

## `status` (type: `string`):

search mode: which markets to list. open (default), unopened, closed, settled, or any.

## `tickers` (type: `array`):

detail mode: Kalshi market tickers (KXNFLGAME-26SEP20CLETB-TB) or event tickers (KXNFLGAME-26SEP20CLETB). An event ticker returns every market in that event. Deduplicated and capped at 25 per run.

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

Hard local cap on delivered market rows per run (1-1000). The listing is walked page by page until this many markets are delivered.

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

Proxy for the public Kalshi trade API. Datacenter is the validated default.

## Actor input object example

```json
{
  "mode": "search",
  "seriesTicker": "KXNFLGAME",
  "status": "open",
  "tickers": [
    "KXNFLGAME-26SEP20CLETB"
  ],
  "maxItems": 100,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `results` (type: `string`):

No description

# 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 = {
    "seriesTicker": "KXNFLGAME",
    "tickers": [
        "KXNFLGAME-26SEP20CLETB"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("leorochasantos/kalshi-markets").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 = {
    "seriesTicker": "KXNFLGAME",
    "tickers": ["KXNFLGAME-26SEP20CLETB"],
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("leorochasantos/kalshi-markets").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 '{
  "seriesTicker": "KXNFLGAME",
  "tickers": [
    "KXNFLGAME-26SEP20CLETB"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call leorochasantos/kalshi-markets --silent --output-dataset

```

## MCP server setup

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

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/EPcLDL1uuOgChYLdO/builds/CmR1AjJC98Vs4EN0b/openapi.json
