# Prediction Market Data Suite (Polymarket + Kalshi) (`ichigowa/prediction-market-data-suite`) Actor

Unified, normalized prediction-market data from Polymarket and Kalshi: market listings, live prices, orderbooks, recent trades and official resolutions in one cross-platform schema. No auth required.

- **URL**: https://apify.com/ichigowa/prediction-market-data-suite.md
- **Developed by:** [kyle herman](https://apify.com/ichigowa) (community)
- **Categories:** Business, News
- **Stats:** 2 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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/platform/actors/running/actors-in-store#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 Data Suite — Polymarket API + Kalshi API in one unified schema

Normalized, cross-platform **prediction market data API**: market listings, live prices, orderbooks, recent trades and official resolutions from **Polymarket** and **Kalshi** — all in a single unified schema, with no API keys or authentication required.

Whether you need a **Polymarket scraper**, a **Kalshi API** client, or a combined **prediction market data api** for research, dashboards, arbitrage monitoring or model training, this actor gives you clean, comparable rows from both platforms:

- Prices always expressed as **probabilities in `[0, 1]`** (Kalshi's cent/dollar prices are normalized for you)
- Timestamps in **ISO-8601 UTC**
- One `market_id` convention per platform (Polymarket `conditionId`, Kalshi `ticker`)
- Pagination, retries with backoff, and rate-limit courtesy built in

### Modes

Set the `mode` input field:

| Mode | What it returns | Key inputs |
|---|---|---|
| `markets` *(default)* | Active/closed/settled market listings with current outcome prices | `platforms`, `status`, `keyword`, `maxResults` |
| `orderbook` | Bid/ask depth per market | `marketIds` |
| `trades` | Recent trades per market | `marketIds`, `limit` |
| `resolutions` | Settled markets with their official outcome | `platforms`, `keyword`, `maxResults` |

### Unified output schemas

#### `markets` / `resolutions` rows

| Field | Type | Notes |
|---|---|---|
| `platform` | string | `polymarket` or `kalshi` |
| `market_id` | string | Polymarket `conditionId` (`0x…`) or Kalshi `ticker` |
| `slug_or_ticker` | string | Polymarket slug / Kalshi ticker |
| `question` | string | Human-readable market question |
| `outcomes` | array | `[{name, price_prob}]`, `price_prob` ∈ \[0, 1] |
| `volume_usd` | number | Polymarket USD volume; Kalshi contract volume (each contract settles at $1) |
| `liquidity_usd` | number | null | `null` for Kalshi (not exposed by public API) |
| `status` | string | `active` | `closed` | `settled` |
| `close_time_utc` | string | ISO-8601 |
| `resolved_outcome` | string | null | e.g. `Yes` / `No` / `Over` — only for settled markets |
| `url` | string | Public market page |
| `fetched_at` | string | ISO-8601 fetch timestamp |

Polymarket rows additionally carry `clob_token_ids` (use these for `orderbook` mode); Kalshi rows carry `event_ticker` and `open_interest`.

#### `orderbook` rows

| Field | Type | Notes |
|---|---|---|
| `platform`, `market_id` | string | CLOB token id (Polymarket) or ticker (Kalshi) |
| `side` | string | `bid` or `ask`, in **YES-probability space** |
| `price_prob` | number | \[0, 1]. Kalshi NO bids are converted to YES asks at `1 − p` |
| `size` | number | Shares (Polymarket) / contracts (Kalshi) |
| `ts` | string | Book timestamp (ISO-8601) |

#### `trades` rows

| Field | Type | Notes |
|---|---|---|
| `platform`, `market_id` | string | conditionId (Polymarket) or ticker (Kalshi) |
| `side` | string | `buy`/`sell`. Kalshi normalized to YES-space: taker bought YES → `buy` |
| `outcome` | string | Traded outcome name |
| `price_prob` | number | \[0, 1] |
| `size` | number | Shares / contracts |
| `taker` | string | null | Polymarket proxy wallet; `null` on Kalshi (identity not public) |
| `ts` | string | ISO-8601 |
| `trade_id` | string | Tx hash (Polymarket) / trade id (Kalshi) |

### Example inputs

Top 200 active markets on both platforms (this is also the default `{}` input):

```json
{ "mode": "markets", "platforms": ["polymarket", "kalshi"], "status": "active", "maxResults": 200 }
```

Bitcoin-related markets only:

```json
{ "mode": "markets", "keyword": "bitcoin", "maxResults": 100 }
```

Orderbooks — mix Polymarket CLOB token ids and Kalshi tickers freely (auto-detected by format):

```json
{
  "mode": "orderbook",
  "marketIds": [
    "71062767620603017016963228301278360791423313061716076409359577603631031433339",
    "KXHIGHNY-26JUL22-T91"
  ]
}
```

Recent trades (Polymarket accepts `0x…` conditionIds or CLOB token ids):

```json
{ "mode": "trades", "marketIds": ["0xc0b7319f73f2…", "KXHIGHNY-26JUL22-T91"], "limit": 100 }
```

Settled markets with official outcomes:

```json
{ "mode": "resolutions", "maxResults": 100 }
```

### Run via API (curl)

```bash
curl -X POST "https://api.apify.com/v2/acts/<username>~prediction-market-data-suite/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"mode": "markets", "keyword": "bitcoin", "maxResults": 50}'
```

### Use in n8n

1. Add the **Apify** node (or a generic **HTTP Request** node).
2. HTTP Request: `POST https://api.apify.com/v2/acts/<username>~prediction-market-data-suite/run-sync-get-dataset-items`
3. Auth: query param `token={{ $env.APIFY_TOKEN }}`.
4. JSON body: `{ "mode": "markets", "keyword": "fed", "maxResults": 100 }`.
5. The node output is the dataset rows — pipe straight into Sheets, Postgres, Slack alerts, etc.

### Pagination & limits

- **Polymarket** (Gamma API): offset pagination, 100 markets/page, sorted by volume.
- **Kalshi**: cursor pagination, up to 1000 markets/page.
- `maxResults` caps rows **per platform**; a hard cap of 40 pages per platform bounds runtime on narrow keyword searches.
- A 0.2 s courtesy delay is inserted between pages and per-market requests; 429/5xx responses are retried up to 4 times with exponential backoff.
- Kalshi's list endpoint includes thousands of auto-generated multivariate parlay markets (near-zero liquidity). They are excluded by default; set `kalshiExcludeMultivariate: false` to include them.

### Monetization events (PPE)

The actor charges per pushed row: `market-row`, `orderbook-row`, `trade-row`. Local runs are unaffected.

### Disclaimer

This is an **unofficial** tool. It is not affiliated with, endorsed by, or supported by Polymarket or Kalshi. It only calls their **public, unauthenticated** data endpoints, sends a descriptive User-Agent, and rate-limits itself. Endpoints and schemas may change without notice.

**Kalshi data notice:** Kalshi's Terms of Service restrict commercial redistribution of their market data by users. This actor is provided as **personal-research tooling** — you are responsible for ensuring your use of the retrieved data (especially republishing or commercial redistribution) complies with each platform's terms. Polymarket data is on-chain/public but the same diligence applies.

Nothing produced by this actor is financial advice.

### FAQ

**Do I need a Polymarket or Kalshi account or API key?**
No. All endpoints used are public and unauthenticated.

**Why are Kalshi prices between 0 and 1 instead of cents?**
All prices are normalized to probability space `[0, 1]` so both platforms are directly comparable. Multiply by 100 for cents.

**Where do I get the IDs for `orderbook`/`trades` mode?**
Run `markets` mode first: Polymarket rows include `clob_token_ids` and `market_id` (conditionId); Kalshi rows include `market_id` (ticker). If you pass no `marketIds`, the actor auto-picks one liquid market per platform as a demo.

**How fresh is the data?**
Every run fetches live. Schedule the actor (e.g. every 5 minutes) for a continuous feed.

**Why is `liquidity_usd` null for Kalshi?**
Kalshi's public market list doesn't expose a comparable USD liquidity figure; use `open_interest` or the `orderbook` mode depth instead.

**Can I get price history?**
Not yet — current snapshot, book, trades and resolutions are covered. Scheduled runs of `markets` mode build a history in your dataset.

**What about other platforms (Manifold, Metaculus, PredictIt)?**
The schema is platform-extensible; open an issue on the actor page if you need one added.

# Actor input Schema

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

What to fetch. <b>markets</b>: normalized market listings with current prices (default). <b>orderbook</b>: bid/ask depth for specific markets (provide <code>marketIds</code>). <b>trades</b>: recent trades for specific markets (provide <code>marketIds</code>). <b>resolutions</b>: settled markets with their official outcomes.

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

Which platforms to query. Used by <b>markets</b> and <b>resolutions</b> modes (orderbook/trades modes infer the platform from each market ID).

## `keyword` (type: `string`):

Case-insensitive substring filter applied to the market question/title, slug and ticker (markets & resolutions modes). Example: <code>bitcoin</code>.

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

Filter for <b>markets</b> mode. <b>active</b> = currently tradable, <b>closed</b> = trading ended (may or may not be resolved yet), <b>settled</b> = officially resolved. The <b>resolutions</b> mode always uses settled markets.

## `maxResults` (type: `integer`):

Maximum number of markets returned per platform (markets & resolutions modes). Pagination (Gamma offset / Kalshi cursor) is handled automatically.

## `marketIds` (type: `array`):

For <b>orderbook</b> and <b>trades</b> modes. Mixed list, auto-detected by format: Polymarket CLOB token IDs (long numeric strings, orderbook), Polymarket condition IDs (<code>0x…</code>, trades/orderbook) or Kalshi tickers (e.g. <code>KXHIGHNY-26JUL22-T91</code>). If empty, the actor auto-discovers one liquid market per platform as a demo.

## `limit` (type: `integer`):

Maximum number of recent trades fetched per market in <b>trades</b> mode.

## `kalshiExcludeMultivariate` (type: `boolean`):

Kalshi's market list contains thousands of auto-generated multivariate parlay combinations (MVE) with little or no liquidity. Keep this on to return only regular markets.

## Actor input object example

```json
{
  "mode": "markets",
  "platforms": [
    "polymarket",
    "kalshi"
  ],
  "status": "active",
  "maxResults": 200,
  "marketIds": [],
  "limit": 100,
  "kalshiExcludeMultivariate": true
}
```

# 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 = {
    "mode": "markets",
    "platforms": [
        "polymarket",
        "kalshi"
    ],
    "keyword": "",
    "status": "active",
    "maxResults": 200,
    "marketIds": [],
    "limit": 100
};

// Run the Actor and wait for it to finish
const run = await client.actor("ichigowa/prediction-market-data-suite").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 = {
    "mode": "markets",
    "platforms": [
        "polymarket",
        "kalshi",
    ],
    "keyword": "",
    "status": "active",
    "maxResults": 200,
    "marketIds": [],
    "limit": 100,
}

# Run the Actor and wait for it to finish
run = client.actor("ichigowa/prediction-market-data-suite").call(run_input=run_input)

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

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

```

## CLI example

```bash
echo '{
  "mode": "markets",
  "platforms": [
    "polymarket",
    "kalshi"
  ],
  "keyword": "",
  "status": "active",
  "maxResults": 200,
  "marketIds": [],
  "limit": 100
}' |
apify call ichigowa/prediction-market-data-suite --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=ichigowa/prediction-market-data-suite",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/acts/P9KjOxwTEzBJUN6Rn/builds/Ju219Pe3F7Wsn8wee/openapi.json
