# Kalshi Scraper — Prediction Markets, Prices & History (`s-r/kalshi-scraper`) Actor

Collect Kalshi prediction markets by category, series, event or market ticker. Export numeric yes/no quotes, traded volume, open interest, rules, settlement outcomes, optional order books and price history.

- **URL**: https://apify.com/s-r/kalshi-scraper.md
- **Developed by:** [SR](https://apify.com/s-r) (community)
- **Categories:** Business, News
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$1.00 / 1,000 kalshi markets

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?

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 Scraper — Prediction Markets, Prices and History

Kalshi scraper exports prediction markets with yes/no quotes, traded volume, open interest,
resolution rules and settlement outcomes. Select a category, search event text, or provide
series, event and market tickers. Each delivered market becomes one dataset row that you can
download as JSON, CSV or Excel and join to your own model using the market ticker.

The actor is useful for analysts tracking economic expectations, newsrooms comparing election
outcomes, and weather modellers matching forecast brackets with observed temperatures. It
collects public market data without requiring a Kalshi account. It does not place orders,
manage a trading account or produce forecasts. An optional order book and price candles help
you inspect the quoted spread and previous trading activity alongside each market record.

### What the Kalshi scraper collects

A market is a single traded contract within an event. An event can contain several brackets
or outcomes, and a series groups recurring events. Those identifiers have separate fields:
`ticker`, `event_ticker` and `series_ticker`. The dataset also includes the full title,
yes/no outcome labels, primary and secondary rules, opening and closing times, expected
expiration, settlement time and the resolved result when the source publishes it.

Prices are numeric amounts in US dollars. A quote of 0.26 means $0.26 for that contract,
not 26 dollars. Volume, volume over the last 24 hours, bid sizes and open interest are numeric
contract quantities and can contain fractions. The source status is preserved, so an open
market may report `active`, while a settled market may report `finalized`.

The `symbol` field equals the market ticker and `asset_class` is `prediction_market`.
`price` is the last traded price when volume is positive. A never-traded market has
`price: null`, even when the source's raw last-price value is zero. The separate
`last_price` field preserves that numeric source value. Retrieval timestamps let you
distinguish a newly collected quote from the market's own update and resolution timestamps.

### Input

Start with `categories`, such as Economics, Elections, Financials, Sports or Weather. Category
names are case-insensitive, and Weather means Climate and Weather. Other supported categories
include Politics, Crypto, Commodities, Companies, Entertainment, Mentions, Science and
Technology, Social and Business. Use `query` to search event text, for example `fed`.

For recurring contracts, provide `series_tickers`, such as `KXHIGHNY`. For a particular
occurrence, use `event_tickers`, such as `KXHIGHNY-26OCT10`. For exact contracts, use
`market_tickers`. Explicit series, events and market tickers form a combined selection;
overlapping markets are delivered once. Categories can narrow that selection. With explicit
tickers, a query matches the market title rather than the event search text. Supply tickers
as arrays of strings, with at most 300 explicit tickers in total.

`status` defaults to `open`. Choose `closed` for closed but unsettled markets, `settled` for
resolved contracts, or `all` to include any status. `unopened` is supported for explicit
series and events; category and text browsing do not support that status. Exact market
lookups return the requested contracts regardless of the status setting, including settled
contracts. Older resolved contracts are included in settled series searches.

Without selectors the actor browses the market index. Combo markets are excluded by default;
set `include_combos` to true to include multivariate contracts in browsing results. Exact
`market_tickers` always return the explicitly requested contracts, including combos.
`min_volume` discards markets
below your chosen total traded contract count. `max_markets` defaults to 100 and allows up
to 50,000 delivered rows. Smaller limits make broad searches easier to inspect.

### Optional order books and price history

Set `include_orderbook` to true to attach yes and no bid levels. `orderbook_depth` defaults
to 10 and supports 1 to 100 levels per side. Levels are sorted from the highest bid down,
with a numeric `price` and `quantity`. An empty book contains two empty lists. These are
bid books: a yes ask can be inferred from one dollar minus the best no bid. The main row's
yes/no bid and ask fields remain the market's quoted values.

Set `include_price_history` to true to attach candles. `history_interval` accepts `minute`,
`hour` or `day` and defaults to day. `history_days` defaults to 7 and supports up to 365 days,
provided the window fits within 5,000 periods. Minute candles support up to 3 days and hourly
candles up to 208 days. Choose daily candles for longer windows. Each candle contains a
period-end timestamp, price open/high/low/close/mean, closing yes bid and ask, volume and
open interest. Candles arrive oldest first and remain inside the requested lookback window.

The window ends at collection time for open markets. When a market closed earlier, it ends
at that market's close time, using settlement time if the close time is absent. This also
retrieves the final trading days of contracts resolved months ago. A valid time window can
have no trading activity; in that case its history is an empty list. Missing candle prices
remain null instead of being filled with an invented quote. If an optional request is
unavailable, the market row still appears with a null block and a warning.

### Output

The default dataset contains one row per delivered market. Order books and price history
stay nested on that row and do not become additional dataset records. The `OUTPUT` record
in the default key-value store holds the query, rows, warnings and collection status.
Errors are recorded separately with a stable code and an English message.

The actor automatically follows continuation pages up to your requested row limit. Unknown
market tickers in a mixed selection produce warnings while found markets are delivered.
A selection with no matching market returns an empty dataset. Invalid categories or history
windows are reported as input errors. Review `warnings` and `partial` when a source error
or the run's collection time limit prevents complete retrieval. Prices can change during
collection, so use `fetched_at` when comparing quotes from different rows or runs.

The collection budget is 1,800 seconds. Start with 100–500 markets when requesting order
books or history. Large enriched selections may return fewer rows than `max_markets`;
50,000 is an input ceiling and does not guarantee that volume within one run. Check
`partial` and the continuation cursor before using a large selection as a complete snapshot.

For a real resolved-market example, see the included example for the October 10, 2026
New York temperature event. Its rows preserve the fetched resolution values, settlement
timestamps and rules from Kalshi. The weather example shows open contracts from the same
recurring series. These examples describe captured source data, rather than a current quote.

### Pricing

The billing unit is one delivered market. The current price per market is shown in the
actor's Pricing tab. Order books and price history are included in that market's row and
do not incur separate product events. Empty results incur no market charge, and this actor
does not charge a product start event. Only rows actually delivered to the dataset count
towards market billing; repeated tickers across continuation pages count once.

Non-paying users have a delivery cap of 10 market rows per run. This is a row limit, not a
claim that charged rows are free. Use a small `max_markets` value to check a selection before
requesting a broad collection. Market volume and order-book quantities describe exchange
activity and do not affect the number of billed market rows.

### FAQ

#### Do I need a Kalshi account or API key?

No. The actor collects public market information. Private account balances, positions,
orders and personal trading history are outside its output.

#### Why do input and output statuses have different names?

Kalshi's status filters and market record statuses use different vocabularies. The actor
accepts filter names such as open and settled while preserving source record values such
as active and finalized, so downstream code can inspect the original status.

#### Why is the price null for a market that has quotes?

The core price field represents an executed trade. If the market has never traded, quoted
bids and asks can exist while there is no last-trade price. Inspect the yes/no quote fields
or implied probability for the quoted market view.

#### Can I collect older settled markets?

Yes. Settled and all-status series searches include the historical market archive. Exact
market tickers can also retrieve resolved contracts. Price history ends at the earlier market
close, so an old contract can return its final trading days even when collection happens
months later. A window without activity still has an empty candle list.

#### Can I use the output to compare probabilities?

The actor supplies the published quotes, spread and an implied-probability estimate. Compare
equivalent rules, closing times and settlement sources before treating two contracts as the
same outcome. A contract's price and volume are market observations, and do not guarantee
that an event will resolve in either direction.

# Actor input Schema

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

Category names, case-insensitive. Weather is an alias for Climate and Weather. Combined with explicit tickers this filters their markets.

## `query` (type: `string`):

Search event text. With explicit tickers, match the market title instead.

## `series_tickers` (type: `array`):

Series tickers. Explicit series, events and market tickers form a union; overlapping markets are delivered once. At most 300 explicit tickers in total.

## `event_tickers` (type: `array`):

Event tickers. Every event is fetched separately; comma-separated event filters are not used.

## `market_tickers` (type: `array`):

Exact market tickers. Direct lookups return the requested markets regardless of status, including historical and combo markets even when include_combos is false.

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

Status for browsing, series and events. The output preserves the original market status, such as active or finalized.

## `include_combos` (type: `boolean`):

Include multivariate combo markets when browsing series, events or categories. Exact market_tickers always return the explicitly requested contracts.

## `min_volume` (type: `integer`):

Minimum total traded contract volume; applied after retrieval.

## `include_orderbook` (type: `boolean`):

Attach yes and no bid levels to each market. Empty books have empty lists. Start with 100–500 enriched markets; the 1800-second collection budget can truncate larger runs.

## `orderbook_depth` (type: `integer`):

Maximum levels per side, highest bid first.

## `include_price_history` (type: `boolean`):

Attach price OHLC candles for the requested lookback window, ending at retrieval time or an earlier market close. Empty windows have empty lists. Start with 100–500 enriched markets; larger runs may be partial.

## `history_interval` (type: `string`):

Candle interval. The requested window must fit within 5000 periods per market.

## `history_days` (type: `integer`):

Lookback days ending at retrieval time for open markets, or at market close for closed and settled markets (settlement time if close is absent). Minute history supports up to 3 days; hourly up to 208 days.

## `max_markets` (type: `integer`):

Maximum market rows for this run. Apify follows pages up to this total within 1800 seconds. This is an upper limit; source availability and enrichment can reduce delivery. Start with 100–500 markets with order books or history.

## `cursor` (type: `string`):

Opaque continuation token. Reuse the same selection and options; max_markets may be reduced to the remaining count.

## Actor input object example

```json
{
  "categories": [],
  "query": "",
  "series_tickers": [],
  "event_tickers": [],
  "market_tickers": [],
  "status": "open",
  "include_combos": false,
  "min_volume": 0,
  "include_orderbook": false,
  "orderbook_depth": 10,
  "include_price_history": false,
  "history_interval": "day",
  "history_days": 7,
  "max_markets": 100,
  "cursor": ""
}
```

# Actor output Schema

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

One row per delivered prediction market.

## `output` (type: `string`):

Normalized query, warnings, collection status and market rows.

## `errors` (type: `string`):

English error messages with stable error codes.

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("s-r/kalshi-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 = {}

# Run the Actor and wait for it to finish
run = client.actor("s-r/kalshi-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 '{}' |
apify call s-r/kalshi-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,s-r/kalshi-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/xmb0SGiJ47qbPM2cj/builds/upCYLCfepeha0lDhC/openapi.json
