# Prediction Market Odds Scraper – Kalshi + Polymarket (`rowfeed/prediction-market-odds-scraper`) Actor

Kalshi and Polymarket prediction markets in one call and one schema: YES probability, bid/ask, spread, volume, liquidity, close time, category and optional order books. Search, filter, sort. Clean JSON for traders, researchers and AI agents.

- **URL**: https://apify.com/rowfeed/prediction-market-odds-scraper.md
- **Developed by:** [Rowfeed](https://apify.com/rowfeed) (community)
- **Categories:** Developer tools, AI
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.50 / 1,000 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?

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

## Prediction Market Odds Scraper – Kalshi + Polymarket

Get Kalshi and Polymarket prediction-market odds in one call and one schema: every market's YES probability, bid and ask, spread, lifetime and 24 h volume, liquidity, close time, category and, on request, the live order book, with a `platform` column so rows from both exchanges sit side by side.
Built for traders comparing venues, researchers building cross-platform datasets, AI agents that need one tool for "what are the odds on X" and dashboards that track both exchanges without maintaining two integrations.
Plain HTTPS calls to Kalshi's and Polymarket's public APIs with retries and a silent-failure check; one exchange having a bad day never stops the other.

### What you get

- **One row per market, same columns on both exchanges** – `platform`, id, title, page link, `yes_prob` (0–1), YES/NO bid and ask, spread, `volume_usd`, `volume_24h_usd`, `liquidity_usd`, open interest, ISO close time, status, category, both outcomes with prices, the parent event and the scrape timestamp. Compare a Fed-decision market on Kalshi against its Polymarket twin without translating field names.
- **Order books on demand** – the top N price levels on the YES and the NO side as `[price, size]` pairs from Kalshi's order book endpoint and Polymarket's CLOB, read at the same moment as the row.
- **Filters that work the same everywhere** – a search term, a status (active, closed, all), a minimum volume, a sort order (24 h volume, volume, liquidity, close time) and a per-platform row cap, so a scheduled run costs the same every time.

### Sample rows

Two rows from one run with `includeOrderbook: true` and `orderbookDepth: 3`, one per platform.

```json
{
  "platform": "kalshi",
  "market_id": "KXAAAGASD-26SEP10-4.2400",
  "ticker_or_slug": "KXAAAGASD-26SEP10-4.2400",
  "title": "Will average **gas prices** be above $4.2400?",
  "url": "https://kalshi.com/markets/kxaaagasd",
  "yes_prob": 0.99,
  "yes_bid": 0.98,
  "yes_ask": 1.0,
  "no_bid": 0.0,
  "no_ask": 0.02,
  "spread": 0.02,
  "volume_usd": 8568.64,
  "volume_24h_usd": 8568.64,
  "liquidity_usd": 0.0,
  "open_interest": 8094.64,
  "close_time": "2026-09-10T03:59:00Z",
  "status": "active",
  "category": "Economics",
  "outcomes": [{ "name": "Yes", "price": 0.99 }, { "name": "No", "price": 0.01 }],
  "source_event": "KXAAAGASD-26SEP10",
  "scraped_at": "2026-09-09T15:21:16+00:00",
  "orderbook": { "yes": [[0.96, 18.91], [0.97, 872.34], [0.98, 2000.0]], "no": [] }
}
```

```json
{
  "platform": "polymarket",
  "market_id": "2252244",
  "ticker_or_slug": "will-there-be-no-change-in-fed-interest-rates-after-the-september-2026-meeting-615",
  "title": "Will there be no change in Fed interest rates after the September 2026 meeting?",
  "url": "https://polymarket.com/event/fed-decision-in-september-762/will-there-be-no-change-in-fed-interest-rates-after-the-september-2026-meeting-615",
  "yes_prob": 0.465,
  "yes_bid": 0.46,
  "yes_ask": 0.47,
  "no_bid": 0.53,
  "no_ask": 0.54,
  "spread": 0.01,
  "volume_usd": 25074718.13,
  "volume_24h_usd": 690706.29,
  "liquidity_usd": 408917.26,
  "open_interest": null,
  "close_time": "2026-09-16T00:00:00Z",
  "status": "active",
  "category": "Politics",
  "outcomes": [{ "name": "Yes", "price": 0.465 }, { "name": "No", "price": 0.535 }],
  "source_event": "fed-decision-in-september-762",
  "scraped_at": "2026-09-09T15:21:16+00:00",
  "orderbook": {
    "yes": [[0.44, 47928.09], [0.45, 28019.27], [0.46, 41567.22]],
    "no": [[0.51, 90932.08], [0.52, 100092.28], [0.53, 129408.82]]
  }
}
```

### How the columns map

| Unified column | Kalshi | Polymarket |
|---|---|---|
| `market_id`, `ticker_or_slug` | market ticker | Gamma market id, market slug |
| `title` | `title` | `question` |
| `url` | `kalshi.com/markets/<series>` | `polymarket.com/event/<event>/<market>` |
| `yes_prob` | last trade price in dollars (0 until the first trade; use bid/ask then) | price of the first outcome (the YES side, or the first named side on sports markets) |
| `yes_bid`, `yes_ask` | `yes_bid_dollars`, `yes_ask_dollars` | `bestBid`, `bestAsk` |
| `no_bid`, `no_ask` | `no_bid_dollars`, `no_ask_dollars` | `1 - bestAsk`, `1 - bestBid` |
| `spread` | `yes_ask - yes_bid` | `spread` |
| `volume_usd`, `volume_24h_usd` | contracts traded (each settles at $1) | USD |
| `liquidity_usd` | `liquidity_dollars` | `liquidityNum` |
| `open_interest` | open contracts | `null` |
| `close_time` | `close_time` | `endDate` |
| `status` | active / closed / resolved (settled) | active / closed / resolved (an outcome priced 1) |
| `category` | series category (Sports, Politics, Economics, Climate and Weather…) | top-level event tag (Politics, Sports, Crypto, Geopolitics…) |
| `outcomes` | Yes / No from the last price | both outcome names and prices |
| `source_event` | event ticker | event slug |
| `orderbook.yes` / `.no` | YES bids / NO bids, best level last | YES bids / NO bids (mirrored from the CLOB asks), best level last |

Rows carry the unified columns only. If you need every raw field of one exchange (rules text, sub-titles, strike values, token ids, tags), use the single-platform Kalshi Markets Scraper or Polymarket Markets Scraper.

### Filters

| Input | Default | What it does |
|---|---|---|
| `platforms` | `["kalshi", "polymarket"]` | Which exchanges to scrape. Each is scraped independently with the same settings. |
| `searchTerm` | `""` | Keep only markets whose title contains this text (case-insensitive). On Kalshi the ticker is searched too, so `NFL` finds the `KXNFLGAME` markets. |
| `status` | `active` | `active` (trading now), `closed` (Kalshi closed + settled, Polymarket closed) or `all`. |
| `minVolume` | `0` | Skip markets whose lifetime `volume_usd` is below this number. |
| `sortBy` | `volume_24h` | `volume_24h`, `volume`, `liquidity` (highest first), `close_time` (soonest first) or `none`. |
| `maxMarketsPerPlatform` | `100` | Rows kept per platform after filtering and sorting. |
| `includeOrderbook` | `false` | Fetch the live YES/NO order book for every row. |
| `orderbookDepth` | `10` | Price levels per side (1–100). |

Kalshi's API lists markets in no useful order, so a Kalshi run without a search term ranks a pool of up to 3,000 open markets by `sortBy`; with a search term it walks the whole exchange (up to 50,000 markets, about 15 seconds) so rare terms are found. Polymarket is sorted server-side and cut at `maxMarketsPerPlatform`.

### Pricing

Pay per event, no subscription: **$1.50 per 1,000 markets** and **$2 per 1,000 order books**. A default run (100 markets per platform, no order books) costs $0.30; the same run with order books costs $0.70. Set a maximum charge on the run and the Actor stops cleanly when it is reached, charging only for rows that were actually saved.

### Use it from your tools

- **API and SDKs** – call it via the Apify API or the official Python/JavaScript clients: one call to start the run, one to fetch the merged dataset as JSON or CSV.
- **Schedules** – run it hourly or daily inside Apify and push new odds to Google Sheets, a webhook or your own storage automatically.
- **n8n, Make and Zapier** – trigger runs and pipe cross-platform odds into a workflow through Apify's integration for each.
- **AI agents and MCP** – this Actor is eligible for agentic use via Apify's MCP server and supports pay-per-event pricing, so an agent can call it mid-task and pay only per market it actually reads.
- **Webhooks** – fire on run finished to kick off the next step in a pipeline as soon as fresh odds land.

### Details

- **Sources**: Kalshi's public trade API v2 (`api.elections.kalshi.com`) and Polymarket's public Gamma and CLOB APIs (`gamma-api.polymarket.com`, `clob.polymarket.com`). No authentication, no proxies, no browser, no personal data (Polymarket wallet addresses are never output).
- **Freshness**: `scraped_at` is the UTC timestamp of the run. Kalshi prices are read live. Polymarket Gamma prices come from a CDN that caches for up to 5 minutes; order books are read live from the CLOB, so turn them on when you need the exact current bid and ask.
- **Reliability**: 429 and 5xx responses are retried with exponential backoff (5 tries) and a 200 without the expected data counts as a failure. Each platform runs on its own: if one fails, the run pushes an error row (`platform`, `error`, `errorMessage`) and continues with the other. A run fails only when it produced no rows *and* hit errors; a filter with no matching markets is a successful, empty run.
- **Run stats**: the `STATS` record in the run's key-value store holds market, order-book, request and error counts per platform and per category (`network`, `rate_limit`, `blocked`, `not_found`, `other`).
- **Speed**: a default run finishes in about 10 seconds (five API calls). Order books add roughly a quarter of a second per market.
- **Output**: one dataset row per market. The Overview table shows platform, title, YES probability, 24 h volume, close time and link. Export as JSON, CSV or Excel, fetch through the Apify API, or schedule runs and pipe them into Google Sheets, Make, Zapier or your own code.

# Actor input Schema

## `platforms` (type: `array`):

Which exchanges to scrape. Every platform is scraped independently and returns the same unified columns; one platform failing never stops the other.

## `searchTerm` (type: `string`):

Keep only markets whose title contains this text (case-insensitive), e.g. bitcoin, Fed, Champions League. On Kalshi the ticker is searched too, so NFL finds the KXNFLGAME markets. Empty = no filter.

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

active (trading now), closed (trading ended or resolved; Kalshi closed + settled, Polymarket closed) or all.

## `minVolume` (type: `number`):

Skip markets whose lifetime volume (volume\_usd) is below this number. 0 keeps every market, including ones that have never traded.

## `sortBy` (type: `string`):

Row order within each platform, so maxMarketsPerPlatform keeps the most relevant rows: 24 h volume, lifetime volume or liquidity (highest first), close time (soonest first), or none for the API order. Kalshi's API lists markets in no useful order, so a Kalshi run ranks a pool of up to 3,000 markets (the whole exchange when a search term is set).

## `maxMarketsPerPlatform` (type: `integer`):

Keep this many rows per platform after filtering and sorting. Each row is one `market` event ($1.50 per 1,000).

## `includeOrderbook` (type: `boolean`):

Fetch the live YES/NO order book for every row (Kalshi order book endpoint, Polymarket CLOB). One extra request and one `orderbook` event ($2 per 1,000) per market.

## `orderbookDepth` (type: `integer`):

Number of price levels per side to include when the order book is on.

## Actor input object example

```json
{
  "platforms": [
    "kalshi",
    "polymarket"
  ],
  "searchTerm": "bitcoin",
  "status": "active",
  "minVolume": 0,
  "sortBy": "volume_24h",
  "maxMarketsPerPlatform": 100,
  "includeOrderbook": false,
  "orderbookDepth": 10
}
```

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

// Run the Actor and wait for it to finish
const run = await client.actor("rowfeed/prediction-market-odds-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("rowfeed/prediction-market-odds-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 rowfeed/prediction-market-odds-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,rowfeed/prediction-market-odds-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/TuyVLuIQRpMQiUINr/builds/vhs5NQNfHv35UhLmf/openapi.json
