# Polymarket × Kalshi Arbitrage Scanner (`fkdg_digital/polymarket-kalshi-arbitrage-scanner`) Actor

Scans Polymarket and Kalshi, matches equivalent binary markets and surfaces risk-free arbitrage opportunities and price divergences.

- **URL**: https://apify.com/fkdg\_digital/polymarket-kalshi-arbitrage-scanner.md
- **Developed by:** [Franclim Diogo](https://apify.com/fkdg_digital) (community)
- **Categories:** Automation, Other
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.10 / scan run

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

## Polymarket × Kalshi Arbitrage Scanner — Prediction Market Arbitrage Finder

Scan **Polymarket** and **Kalshi** in one run, automatically match equivalent binary (Yes/No) prediction markets across both platforms, and get back two kinds of actionable signals:

1. **Risk-free arbitrage opportunities** — pairs where buying YES on one platform and NO on the other costs less than $1.00 total (Kalshi taker fees included), locking in a guaranteed return no matter the outcome.
2. **Price divergences** — pairs where the implied probability differs by 3% or more between platforms, useful for traders with a directional view or for tracking where the "wisdom of crowds" disagrees with itself.

Run it on a schedule (hourly is recommended) and pipe results to webhooks, spreadsheets, or your own alerting.

### Why this Actor?

Cross-platform prediction market arbitrage is real, but the hard part is **matching**: knowing that Polymarket's *"Will the Fed cut rates in September?"* and Kalshi's *"Fed decision — September: Cut"* are the same bet. This Actor takes a deliberately **transparent** approach: every result row shows you the similarity score, the exact shared terms that drove the match, and a confidence level — so you can verify the match yourself before risking a cent. No black-box embeddings silently pairing markets that resolve on different terms.

### How it works

1. Fetches the top Polymarket markets by 24h volume (Gamma API), keeping only strictly binary Yes/No markets above your liquidity threshold.
2. Fetches all open Kalshi markets with live two-sided quotes (public trade API), excluding multivariate parlay combos.
3. Matches each Polymarket market to its best Kalshi counterpart by lexical similarity (Jaccard over normalized title tokens, with a penalty when numeric thresholds/years disagree).
4. For each matched pair, computes both hedge strategies — `YES@Polymarket + NO@Kalshi` and `YES@Kalshi + NO@Polymarket` — with **Kalshi taker fees included**, and keeps the cheaper one.
5. Outputs arbitrage rows (total cost < $1.00) sorted by guaranteed return, and divergence rows (probability gap ≥ 3%) sorted by gap size.

### Input

| Field | Default | Description |
|---|---|---|
| `minSimilarity` | `"0.6"` | Minimum title similarity (0–1) to accept a cross-platform match. Raise to 0.75+ for high-confidence pairs only. |
| `minPmLiquidity` | `500` | Skip Polymarket markets below this liquidity (USD). |
| `minKalshiVolume24h` | `0` | Skip Kalshi markets below this 24h volume. |
| `pmMarketsToScan` | `600` | How many top-volume Polymarket markets to scan (max 2000). |
| `maxResults` | `100` | Cap per category (arbitrage / divergence). |

### Output fields

Each dataset row includes:

| Field | Description |
|---|---|
| `category` | `arbitrage` or `divergence` |
| `strategy` | e.g. `YES@Polymarket + NO@Kalshi` — the cheaper hedge |
| `total_cost` | Total cost per $1 payout, Kalshi fee included |
| `guaranteed_return_pct` | (arbitrage only) locked-in return % |
| `prob_gap_pct` | (divergence only) implied-probability gap % |
| `kalshi_fee` | Kalshi taker fee applied to the Kalshi leg |
| `pm_question`, `pm_url`, `pm_yes_price`, `pm_liquidity`, `pm_volume_24h`, `pm_end_date` | Polymarket side |
| `kalshi_title`, `kalshi_ticker`, `kalshi_url`, `kalshi_yes_ask`, `kalshi_no_ask`, `kalshi_liquidity`, `kalshi_volume_24h`, `kalshi_end_date` | Kalshi side |
| `similarity` | Match score (0–1) |
| `shared_terms` | The actual tokens both titles share — inspect these! |
| `confidence` | `high` (≥ 0.75) or `medium` |
| `warning` | Reminder to verify both markets resolve on identical terms |

A full run report (counts, parameters, errors, all rows) is also saved to the key-value store as `REPORT`.

### Honest limitations

- **Polymarket prices are midpoints.** The Gamma API returns outcome prices, not a live order book; treat them as midpoints and expect some slippage on execution.
- **Matching is lexical (v1).** Jaccard similarity over title tokens catches most equivalent markets but can pair markets that resolve on subtly different terms (different deadlines, different resolution sources). **Always read both markets before trading** — that's why every row carries `shared_terms` and a `warning`.
- **True arbs are rare and close fast.** A single manual run will usually find zero arbitrage rows. Schedule hourly runs and act on alerts quickly.
- **Fees:** Kalshi taker fees are included; Polymarket gas/relayer costs and withdrawal frictions are not.

**This is a market-scanning tool, not financial advice.** Trading prediction markets involves risk, including from mismatched market pairs. Verify everything yourself.

### Roadmap

- Semantic entity matching (v2) — LLM/embedding-assisted pairing with entity and date extraction
- More platforms: PredictIt, Limitless
- Webhook alerts for new arbitrage opportunities
- Order-book depth-aware sizing

### Pricing

Pay-per-event: a small flat fee per scan run, plus a per-row fee for each opportunity found. You only pay for signal.

# Actor input Schema

## `minSimilarity` (type: `string`):

Minimum Jaccard similarity (0-1) between Polymarket and Kalshi market titles for a pair to be considered a match. Higher = fewer, safer matches. Default 0.6.

## `minPmLiquidity` (type: `integer`):

Skip Polymarket markets with liquidityNum below this value.

## `minKalshiVolume24h` (type: `integer`):

Skip Kalshi markets with 24h volume below this value. 0 = no filter.

## `pmMarketsToScan` (type: `integer`):

How many of the highest-24h-volume Polymarket markets to fetch and try to match.

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

Cap on the number of arbitrage rows and divergence rows in the output.

## Actor input object example

```json
{
  "minSimilarity": "0.6",
  "minPmLiquidity": 500,
  "minKalshiVolume24h": 0,
  "pmMarketsToScan": 600,
  "maxResults": 100
}
```

# Actor output Schema

## `opportunities` (type: `string`):

Arbitrage and divergence rows found across Polymarket and Kalshi.

## `report` (type: `string`):

Complete run report: market counts per platform, matched pairs, parameters and errors.

# 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("fkdg_digital/polymarket-kalshi-arbitrage-scanner").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("fkdg_digital/polymarket-kalshi-arbitrage-scanner").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 '{}' |
apify call fkdg_digital/polymarket-kalshi-arbitrage-scanner --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=fkdg_digital/polymarket-kalshi-arbitrage-scanner",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/OHsRBJBPjcE4YtwoR/builds/iujzQBxIBIcHgwgHP/openapi.json
