# Kalshi & Polymarket Arbitrage Scanner - Cross-Exchange Gaps (`yawning_manuscript/kalshi-polymarket-market-data`) Actor

Finds the same question listed on both Kalshi and Polymarket and reports the price gap, each side’s spread, and whether the gap actually clears both spreads. Plus every market on both exchanges in one normalized schema. Lexical matching, match score on every pair. No API keys.

- **URL**: https://apify.com/yawning\_manuscript/kalshi-polymarket-market-data.md
- **Developed by:** [SignalData](https://apify.com/yawning_manuscript) (community)
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

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

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

## Kalshi & Polymarket Market Data + Cross-Exchange Price Gaps

Every market on **Kalshi** and **Polymarket** in one normalized schema — prices, implied probability, spread, volume, open interest, close time, resolution rules — plus the part no other actor gives you: **the same question matched across both exchanges, with the price gap between them**.

No API keys. Both exchanges are read through their public endpoints.

### The reason this exists

Kalshi quotes in cents, Polymarket in dollars. Kalshi calls it `yes_bid`, Polymarket calls it `bestBid`. Kalshi says `volume_fp`, Polymarket says `volumeNum`. One returns arrays, the other sometimes returns arrays encoded as strings. If you want to compare the two, you write a normalization layer first and a strategy second.

This actor is that layer, already written and tested against the live response shapes of both APIs.

### Cross-exchange gaps

When the same real-world question trades on both venues, the actor pairs them and reports:

```json
{
  "type": "cross_exchange_match",
  "question_kalshi": "Will the high temperature in NYC be above 79.5 degrees on Sep 8?",
  "question_polymarket": "NYC high temp above 79.5F on September 8?",
  "kalshi_probability": 0.44,
  "polymarket_probability": 0.60,
  "probability_gap": 0.16,
  "combined_spread": 0.06,
  "gap_exceeds_spreads": true,
  "match_score": 0.83,
  "kalshi_url": "https://kalshi.com/markets/kxhighny",
  "polymarket_url": "https://polymarket.com/event/nyc-high-sep-8"
}
```

`gap_exceeds_spreads` is the field to sort on. A 16-point gap when the two spreads together are 6 points is a real dislocation, not just the cost of crossing. Rows come back with the widest gap first.

Match strictness is tunable. The two exchanges word the same event very differently — Kalshi writes "Fed decision in September?" where Polymarket writes "Will the Fed decrease interest rates by 25 bps after the September 2026 meeting?" — so the default sits at a loose 0.35 and every row carries its `match_score`. Read that score before trading on a pair; this is lexical matching, not semantic, and it is deliberately reported rather than hidden.

### Market rows

```json
{
  "type": "market",
  "exchange": "kalshi",
  "market_id": "KXHIGHNY-26SEP08-B79.5",
  "event_id": "KXHIGHNY-26SEP08",
  "series_id": "KXHIGHNY",
  "question": "Will the high temperature in NYC be above 79.5 degrees on Sep 8?",
  "yes_bid": 0.42, "yes_ask": 0.46, "mid_price": 0.44,
  "implied_probability": 0.44, "spread": 0.04,
  "volume": 15320, "volume_24h": 4210, "open_interest": 8800,
  "close_time": "2026-09-08T23:30:00Z",
  "strike_type": "greater", "floor_strike": 79.5,
  "rules": "Settles on the NWS Daily Climate Report for Central Park.",
  "url": "https://kalshi.com/markets/kxhighny"
}
```

Prices are always dollars in the 0–1 range on both exchanges, whichever units the source used. Polymarket rows additionally carry `outcomes` and `outcome_prices` for multi-outcome markets, and `implied_probability` reads the **Yes** leg specifically rather than assuming it is first in the list.

### Example input

Everything liquid on both exchanges, with gaps:

```json
{ "exchanges": ["kalshi", "polymarket"], "status": "open",
  "maxMarkets": 500, "minVolume": 1000, "crossExchangeGaps": true }
```

One theme only — scanning is free, you are billed only for what comes back:

```json
{ "search": ["temperature", "weather", "rain", "snow"], "scanLimit": 5000, "maxMarkets": 200 }
```

A single Kalshi series, e.g. the New York daily high:

```json
{ "exchanges": ["kalshi"], "kalshiSeriesTicker": "KXHIGHNY", "status": "open" }
```

Settled markets for backtesting:

```json
{ "status": "settled", "maxMarkets": 5000, "crossExchangeGaps": false }
```

### Pricing

| Record | Price |
|---|---|
| `market` | $0.001 |
| `cross_exchange_match` | $0.005 |

Scanning is not billed. A daily pull of 500 liquid markets with gap detection runs about **$0.55**.

### Pairs well with

**[Weather Data for Prediction Markets](https://apify.com/store)** — the official NWS settlement values and multi-model forecasts for the stations temperature markets resolve on. Pull the market prices here, the physical data there, and you have both halves of the trade.

### Use from an AI agent

Callable through Apify's MCP server. An agent can ask for open markets matching a theme and get back a clean, sorted, typed table without any exchange-specific glue code.

### Notes and limits

- Question matching is lexical (word overlap), not semantic, and every pair carries its `match_score` so you can judge it yourself. At the default 0.35 you will see some loose pairs; raise the strictness if you want fewer and cleaner. Expect few or no matches when you pull the whole book untargeted — the two exchanges list mostly different events. Search a theme ("fed", "bitcoin", "temperature") to find the overlap.
- Polymarket's Gamma API returns markets sorted by 24-hour volume; Kalshi is paged by cursor. Both are capped by `maxMarkets` so a run cannot bill you unexpectedly.
- Order-book depth is not included — these are top-of-book prices and traded volume.
- If one exchange is down, the run still returns the other and logs the failure rather than dying.

### Support

Open an issue on the actor page. Requests for extra fields, other exchanges, or a different match algorithm are welcome.

# Actor input Schema

## `exchanges` (type: `array`):

Which exchanges to pull. Both are needed for cross-exchange gap detection.

## `search` (type: `array`):

Keep only markets whose question, subtitle, category or ticker contains one of these, e.g. "temperature", "fed", "election", "bitcoin". Leave empty for everything.

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

Open markets are the tradable ones. Closed/settled is for backtesting.

## `maxMarkets` (type: `integer`):

Rows are billed per market, so this is your cost ceiling. Sorted by 24h volume, highest first.

## `minVolume` (type: `integer`):

Drop illiquid markets below this traded volume. Set 0 to keep everything.

## `crossExchangeGaps` (type: `boolean`):

Match questions listed on both exchanges and report the probability gap, flagging the ones where the gap is wider than both spreads combined.

## `matchThreshold` (type: `string`):

How similar two questions must be to count as the same market. The two exchanges word the same event very differently, so 0.5 and above rarely matches anything real. Start at 0.35 and read match\_score on every row before trading on it.

## `scanLimit` (type: `integer`):

When search terms are set, how many markets to scan before filtering. Scanning is free - you are only billed for the rows returned.

## `kalshiSeriesTicker` (type: `string`):

Restrict Kalshi to one series, e.g. KXHIGHNY for the New York daily high temperature market.

## `kalshiEventTicker` (type: `string`):

Restrict Kalshi to a single event.

## Actor input object example

```json
{
  "exchanges": [
    "kalshi",
    "polymarket"
  ],
  "search": [],
  "status": "open",
  "maxMarkets": 500,
  "minVolume": 0,
  "crossExchangeGaps": true,
  "matchThreshold": "0.35",
  "scanLimit": 3000
}
```

# Actor output Schema

## `dataset` (type: `string`):

Normalized market rows from both exchanges plus any cross-exchange matches.

## `csv` (type: `string`):

The same rows as a CSV.

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

The dataset with the Markets and Cross-exchange gaps views.

# 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 = {
    "search": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("yawning_manuscript/kalshi-polymarket-market-data").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 = { "search": [] }

# Run the Actor and wait for it to finish
run = client.actor("yawning_manuscript/kalshi-polymarket-market-data").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 '{
  "search": []
}' |
apify call yawning_manuscript/kalshi-polymarket-market-data --silent --output-dataset

```

## MCP server setup

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

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/8DqyZmWTPAYbeCQ7t/builds/BvH4Vnxssknztmc8T/openapi.json
