# Prediction Markets Scraper - Kalshi & Polymarket Odds (`punkrecordsdata/prediction-markets-scraper`) Actor

Scrape real-time prediction market odds from Kalshi and Polymarket: current prices, volumes, order book depth, and cross-platform price gaps when the same question trades on both exchanges.

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

## Pricing

from $5.10 / 1,000 market summaries

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?

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

<p align="center">
  <img src="https://api.apify.com/v2/key-value-stores/AAm3a1h3Z9nYfrvh9/records/banner" alt="PunkRecordsData" width="100%" />
</p>

## 🎯 Prediction Markets Odds Scraper: Kalshi & Polymarket, PunkRecordsData

> 📊 **Real-time prediction market odds from Kalshi and Polymarket in one run.** Yes/no prices, order book depth, and the price gap when the **same question trades on both exchanges**, a cross-platform arb finder, not just a tick feed.

Scrape live prediction market data straight from the exchanges' own public APIs: current yes/no prices, best bid/ask, volumes and open interest for every market, plus the **top ten levels of the order book** per market. When the same question is listed on both exchanges (e.g. *"Will Gavin Newsom be the 2028 Democratic presidential nominee?"*), the Actor matches the pair and reports the exact price gap between them.

Built with the [Apify platform](https://apify.com): run it on a schedule, pull results over the **REST API**, pipe them into Make/Zapier/Google Sheets, and browse the output in a clean table view, no databases or scrapers of your own required.

### 📋 What the Prediction Markets Odds Scraper does

Give it the platform(s) and it returns **one clean row per market** with the normalized price fields both exchanges use (Kalshi calls them *dollars*, Polymarket calls them *prices*, this Actor reconciles them to a single schema). Turn on **Order book depth** for the top ten bids and asks, **Price history** for a time series you can chart, or **Recent trades** for the last individual fills. Turn on **Cross-platform match** and you additionally get one row per question that trades on both Kalshi and Polymarket, with the numeric price gap and which side is cheaper.

You can scrape **everything** ('bitcoin' to 'Will the Fed cut rates'), or **filter by search terms** and only pull markets whose question mentions them. Prices are marked by the exchange at run time; volumes and 24h volumes are included so you can filter for genuinely liquid markets.

> 💡 **Why it matters:** most prediction-market scrapers return one exchange's raw tick dump. This Actor normalizes both exchanges into one schema, attaches order book depth per market, and finds same-question cross-exchange pairs so you can spot a **price gap before a market maker does**.

### 🔀 Cross-platform gap example (verified run)

Real matched pair from a test run: the same question trading on both exchanges:

| Platform | Question | Yes price |
|---|---|---|
| 🏛️ Kalshi | Will Gavin Newsom be the Democratic Presidential nominee in 2028? | 0.140 |
| 🏛️ Polymarket | Will Gavin Newsom win the 2028 Democratic presidential nomination? | 0.139 |

The Actor reports `priceGap: 0.001`, `spreadPercent: 0.72%`, `cheaperPlatform: "polymarket"`.

### 📊 Output

Two record types, identified by `recordType`.

#### `market-summary`: one row per market

| Field | Type | Example |
|---|---|---|
| 🏛️ `platform` | string | `kalshi` or `polymarket` |
| 🆔 `marketId` / `eventId` | string | `KXPRESNOMD-28-GN` / `559652` |
| ❓ `question` | string | `Will Gavin Newsom be the Democratic Presidential nominee in 2028?` |
| 📶 `status` | string | `active` / `closed` |
| ✅ `yesPrice` / ❌ `noPrice` | number | `0.14` / `0.86` |
| 🔼 `yesBid` / 🔽 `yesAsk` | number | `0.14` / `0.15` |
| 🧾 `lastPrice` | number | `0.15` |
| 📊 `volume` / `volume24h` | number | `9900000` |
| 📦 `openInterest` | number | `150000` (Kalshi) |
| 🕒 `closeTime` | string (ISO) | `2028-11-07T15:00:00Z` |
| 🔗 `url` | string | `https://kalshi.com/...` |
| 📚 `orderBook` | object | `{"bids":[10 levels],"asks":[10 levels]}` |
| 📈 `priceHistory` | array | Time series: `{"time":"2026-09-11T06:00:18.000Z","price":0.0385}` |
| 🔁 `recentTrades` | array | Last trades: `{"time":"2026-09-12T05:06:48Z","side":"sell","price":0.959,"size":3.47}` |
| 🕒 `scrapedAt` | string (ISO) | `2026-09-12T01:27:09.328Z` |
| ❌ `error` | string or `N/A` | `N/A` |

#### `cross-match-record`: one row per matched pair

| Field | Type | Example |
|---|---|---|
| ❓ `question` | string | Kalshi question text |
| 🎯 `matchScore` | number (0–100) | `70.1` |
| 🏛️ `kalshi` | object | `{marketId, eventId, question, yesPrice, url}` |
| 🏛️ `polymarket` | object | `{marketId, eventId, question, yesPrice, url}` |
| 🔀 `priceGap` / `absPriceGap` | number | `0.001` |
| 💸 `cheaperPlatform` | string | `kalshi` / `polymarket` / `equal` |
| 📈 `spreadPercent` | number | `0.72` |

Fields that were never requested come back as `"Not Requested"`; fields a market genuinely doesn't expose come back as `"N/A"`. You always know **why** a field is empty.

You can download the dataset in various formats such as **JSON, HTML, CSV, or Excel**, or fetch it over the API.

### 📈 How it compares to alternatives

Measured against the closest published Kalshi/Polymarket actors on the Apify Store:

| Actor | Primary price | Billable events |
|---|---|---|
| Volume leader, Kalshi only | $1.00 / 1,000 | 1 |
| Kalshi-only scraper | $4.00 / 1,000 | 1 |
| Polymarket real-time monitor (46 users/30d) | $12.00 / 1,000 | 1 |
| **This Actor** | $6.00 / 1,000 | **5** (market summary, order book, price history, recent trades, cross-platform gap), both platforms in one run |

### 🚀 How to use

1. Create a free [Apify account](https://console.apify.com). New accounts get $5 in free credit.
2. Open the **Prediction Markets Odds Scraper**. Both platforms are pre-selected; just hit **Start** for a full sweep.
3. Optional: enter **search terms** (e.g. `bitcoin`) to narrow to a topic, toggle **Order book depth**, or adjust `Max markets per platform`.
4. When the run finishes, open the **Output** tab: the *Markets* view lists every market, the *Cross-platform matches* view lists every same-question pair with its gap.
5. Download as CSV/Excel/JSON/XML or pull the dataset via the API.

#### Example input (JSON)

```json
{
  "platforms": ["kalshi", "polymarket"],
  "status": "open",
  "searchTerms": ["bitcoin"],
  "maxPerPlatform": 300,
  "liquidOnly": false,
  "maxItems": 100,
  "includeOrderBook": true,
  "includeCrossMatch": true,
  "crossMatchDepth": 3000
}
```

### 💼 Business use cases

**Arbitrage & market makers**
Cross-platform pairs expose price inefficiencies between Kalshi and Polymarket on the same underlying question.

**Betting-adjacent research**
Track how yes/no probabilities, volumes and open interest move across election, macro and sports markets over time.

**Finance & crypto dashboards**
Feed Fed-rate, inflation, bitcoin and ETF markets into pricing dashboards or LLM pipelines that consume structured prediction data.

**Agencies & data resellers**
Deliver a normalized Kalshi + Polymarket feed to clients without maintaining integrations to two different APIs.

### 🔌 Automating the scraper

Connect it to **Make**, **Zapier**, **Slack** or **Airbyte** to push fresh odds into a spreadsheet, database or channel after every run. Schedule it to run every hour, or every 5 minutes, and route the output dataset anywhere you need.

### 🤖 Ask an AI assistant about this scraper

Paste the Actor's Store page into ChatGPT, Claude or Perplexity and ask: "How do I get cross-platform price gaps between Kalshi and Polymarket?" The README and input schema are structured so AI assistants can answer accurately from the page alone.

### ❓ Frequently Asked Questions

**Do I get every market on both exchanges?**
Kalshi has ~20k+ markets, Polymarket ~5k+. Use `Max markets per platform` (default 300 per platform) and keep in mind Kalshi's listing is ordered so its most-liquid markets sit deep. Raise the limit if you need specific ones, and check `status` if you need closed markets too.

**Why are the prices on the two exchanges a few cents apart?**
They're separate order books. Kalshi displays last price (falling back to the mid of the best bid/ask); Polymarket yes price is the mid of best bid/ask. `cross-match-record` reports the real traded gap between them.

**Do I need an account, cookies or proxy on either exchange?**
No. Every field comes from Kalshi's and Polymarket's own public APIs; no login or session required.

**How do I find the same question on both exchanges?**
Cross-platform matching runs when both platforms are selected and `includeCrossMatch` is on. It matches by shared question keywords plus close resolution dates, and deep-scans both exchanges to find overlaps that sit thousands of markets deep into the listings. Add a search term to focus coverage on a topic.

**How much does it cost?**
Base market summaries: $2.00 per 1,000 market rows. Each extra is billed only when you turn it on, and only for markets where real data came back: `includeOrderBook` adds $0.50 per 1,000 order-book records, `includePriceHistory` adds $0.75 per 1,000 price-history records, `includeTrades` adds $0.60 per 1,000 recent-trades records, and `includeCrossMatch` adds $1.00 per 1,000 matched pairs. Free accounts get a **10-row preview** per run.

### 🔗 Recommended Actors

Browse the rest of the [PunkRecordsData](https://apify.com/punkrecordsdata) catalog for more scrapers covering social media, marketplaces and pricing intelligence.

> 💡 **Pro Tip:** browse the complete [PunkRecordsData collection](https://apify.com/punkrecordsdata).

**🆘 Need Help?** contact.punkrecordsdata@gmail.com

> **⚠️ Disclaimer:** This is an independent tool, not affiliated with, endorsed by, or sponsored by Kalshi or Polymarket. It collects only publicly available market data. Prices are informational and are not financial or trading advice.

# Actor input Schema

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

Which prediction market platforms to scrape. Valid values: "kalshi", "polymarket". Leave both (default) for cross-platform matching.

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

Which markets to collect. Open = currently tradable.

## `searchTerms` (type: `array`):

Optional. Only keep markets whose question contains any of these phrases (case-insensitive). Leave empty for all markets. Examples: "federal reserve", "bitcoin", "world series".

## `maxPerPlatform` (type: `integer`):

Maximum number of markets to fetch from each selected platform. Larger values improve cross-platform match coverage (Kalshi pages from oldest events first, so its liquid markets sit deeper in the listing).

## `liquidOnly` (type: `boolean`):

Drop markets with zero volume (stale or never-traded ticks).

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

Maximum number of market-summary rows to deliver. Free users: limited to 10 (preview). Paid users: optional, max 1,000,000.

## `includeOrderBook` (type: `boolean`):

Top ten bid/ask price levels per market (Kalshi orderbook + Polymarket CLOB book). Billed per market as order-book-record.

## `includeCrossMatch` (type: `boolean`):

Find the same question listed on both Kalshi and Polymarket (requires both platforms selected) and output the price gap between them. With no search term it deep-scans both platforms to locate overlaps. Billed per match as cross-match-record.

## `includePriceHistory` (type: `boolean`):

Historical price time series for the market (Kalshi candlesticks, Polymarket CLOB price history). Useful for charting or backtesting. Billed per market as price-history-record.

## `includeTrades` (type: `boolean`):

The last individual trades for the market: side, price, size, timestamp. Billed per market as recent-trades-record.

## `crossMatchDepth` (type: `integer`):

How many markets per platform to scan when cross-matching without a search term. Larger depth finds more matches but adds requests (Kalshi and Polymarket keep overlaps thousands of markets into their listings).

## Actor input object example

```json
{
  "platforms": [
    "kalshi",
    "polymarket"
  ],
  "status": "open",
  "searchTerms": [],
  "maxPerPlatform": 300,
  "liquidOnly": false,
  "maxItems": 100,
  "includeOrderBook": false,
  "includeCrossMatch": true,
  "includePriceHistory": false,
  "includeTrades": false,
  "crossMatchDepth": 3000
}
```

# Actor output Schema

## `overview` (type: `string`):

Key market fields: platform, question, price, volume, status

## `crossMatches` (type: `string`):

Same question on both Kalshi and Polymarket, with the price gap

## `fullData` (type: `string`):

Complete dataset with every field, including order books when requested

# 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 = {
    "platforms": [
        "kalshi",
        "polymarket"
    ],
    "searchTerms": [],
    "maxPerPlatform": 300,
    "maxItems": 100,
    "crossMatchDepth": 3000
};

// Run the Actor and wait for it to finish
const run = await client.actor("punkrecordsdata/prediction-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 = {
    "platforms": [
        "kalshi",
        "polymarket",
    ],
    "searchTerms": [],
    "maxPerPlatform": 300,
    "maxItems": 100,
    "crossMatchDepth": 3000,
}

# Run the Actor and wait for it to finish
run = client.actor("punkrecordsdata/prediction-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 '{
  "platforms": [
    "kalshi",
    "polymarket"
  ],
  "searchTerms": [],
  "maxPerPlatform": 300,
  "maxItems": 100,
  "crossMatchDepth": 3000
}' |
apify call punkrecordsdata/prediction-markets-scraper --silent --output-dataset

```

## MCP server setup

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