# Polymarket Real-Arb Scanner (`ebag73/polymarket-arb-scanner`) Actor

Guaranteed, executable arbitrage on Polymarket, priced off real CLOB order-book depth at your stake, fee-adjusted per category. No phantom edges.

- **URL**: https://apify.com/ebag73/polymarket-arb-scanner.md
- **Developed by:** [Gabe](https://apify.com/ebag73) (community)
- **Categories:** Developer tools, Automation, AI
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.02 / actor start

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.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js/docs.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python/docs.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/integrations/mcp.md).

If your project is in a different language, use 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 Real-Arb Scanner — executable, fee-adjusted, depth-checked

**Finds guaranteed arbitrage on [Polymarket](https://polymarket.com) prediction markets — and only the arbitrage you can actually execute.** Every opportunity is priced off the **real CLOB order book walked to your stake size**, **fee-adjusted for the exact market category**, with the **depth available at each leg** reported. No phantom edges off last-trade or mid prices — the thing that makes every generic "arbitrage scanner" useless the moment you try to fill.

- **Polymarket arbitrage scanner** with real order-book depth, not top-of-book teasers
- **Prediction market odds API** output: clean JSON per opportunity
- Handles Polymarket's **negRisk multi-outcome markets** (elections, futures, "how many" markets) correctly
- Per-category **taker-fee model** (crypto 7%, sports 5%, politics/finance 4%, geopolitics free…)
- **Pay-per-event** pricing — you pay for the run plus each real opportunity found, nothing for phantom noise

---

### Why most "arb scanners" lie, and this one doesn't

A prediction-market arb is only real if you can **fill every leg, in size, right now, after fees**. Almost every scanner computes edges from *last-trade* or *Gamma mid* prices — numbers you can never trade at. They surface dozens of "arbs" that vanish on contact.

This actor does the opposite:

1. **Reconstructs the complete outcome set.** A "buy every outcome" arb is only guaranteed over the *full* mutually-exclusive set. We pull every leg of each `negRisk` group from the event API — never a volume-filtered subset — and **disqualify any set with a residual "Other / None" outcome**, even an inactive one. (Summing three of thirty World Series teams and calling it an arb is the #1 way these tools lie.)
2. **Walks the real CLOB book to your stake.** The reported price is the **VWAP** you'd actually pay to fill `stakeUsd`, across as many levels as it takes — not the untradeable best-ask tick.
3. **Deducts the real fee.** `fee = shares × rate × p × (1−p)`, with `rate` set per Polymarket category. Geopolitics is free; crypto is 7%.
4. **Filters bad data.** Crossed books, sub-cent ask walls, and dead books are treated as *bad data, not free money*.

The honest consequence: **on efficient, high-volume markets this actor usually returns few or zero arbs — because there genuinely aren't any.** That is the point. Fewer, real opportunities beat a screen full of fiction.

### What it finds

| Type | Structure | Guarantee |
|---|---|---|
| `negrisk-buy-all-no` | Buy NO on every leg of a mutually-exclusive set | N−1 legs always pay $1 — guaranteed by mutual exclusivity (safe even if the set is incomplete) |
| `negrisk-buy-all-yes` | Buy YES on every leg of a **complete, no-Other** set | Exactly one leg pays $1 — guaranteed only over an exhaustive set |
| `merge-yes-no` | Buy YES + NO of one market for < $1 | They merge to $1 — rare and transient (market makers enforce ≥ $1) |

### Input

```jsonc
{
  "stakeUsd": 100,            // capital per opportunity; prices are VWAPs to fill THIS much
  "minNetEdgePct": 1,         // only return arbs ≥ this % profit AFTER fees
  "categories": [],           // e.g. ["sports","politics"]; empty = all
  "minVolume": 1000,          // skip markets below this lifetime volume
  "maxResults": 200,
  "includeWeatherEvents": false, // daily-temperature markets, off by default
  "maxMarketsScanned": 2000,  // cost guard
  "maxBooksFetched": 400      // cost guard (order-book requests)
}
````

### Output (one dataset item per opportunity)

```jsonc
{
  "type": "negrisk-buy-all-no",
  "guarantee": "mutual-exclusivity",
  "eventTitle": "EPL: 2027 Champion",
  "legCount": 20,
  "sizeShares": 312.5,
  "capitalUsd": 18.42,
  "feesUsd": 0.31,
  "payoutUsd": 19.00,
  "netProfitUsd": 0.27,
  "grossEdgePct": 3.15,
  "netEdgePct": 1.47,          // profit after fees, as % of capital
  "staleness_ms": 640,         // age of the oldest order book used
  "legs": [
    {
      "market": "Will Arsenal win the 2026-27 EPL?",
      "side": "BUY_NO",
      "executablePrice": 0.61, // VWAP to fill your stake, not top-of-book
      "shares": 312.5,
      "depthWorstPrice": 0.63, // deepest level you'd consume
      "levelsConsumed": 4,
      "feeUsd": 0.02
    }
    // …one leg per outcome
  ],
  "scannedAt": "2026-07-22T23:40:00.000Z"
}
```

### Pricing (pay-per-event)

| Event | Price |
|---|---|
| Actor start | $0.02 — covers discovery + book screening, even on zero-arb runs |
| Arb found (per result) | $0.02 per executable opportunity returned |

You are never charged for phantom opportunities, because none are emitted — the Actor only ever writes a dataset item when it finds a real, executable, fee-cleared arb.

### Limitations (read these)

- **Executable arbs are rare on liquid markets.** Efficient books are arbitraged away by bots in seconds. Expect most runs on top-volume markets to return 0. Value comes from scheduling frequent runs to catch transient windows, and from scanning mid-liquidity markets.
- **Snapshot, not a guarantee of fill.** `staleness_ms` tells you how fresh each book was; by the time you act, the book may have moved. Treat every result as a lead to verify, not a filled trade.
- **Depth is top-of-run.** Books are fetched once per run. For sub-second opportunities you need your own live connection; this is a scanner, not an execution bot.
- **Single-venue.** Cross-venue (Polymarket ↔ Kalshi) arbitrage is a separate actor.
- **Weather/temperature markets are excluded by default** — thin books, specialized resolution; enable with `includeWeatherEvents` if you know what you're doing.

### How it works (pipeline)

`Gamma market discovery (prices) → negRisk event reconstruction (complete sets) → cheap price screen → CLOB order-book walk (executable VWAP + depth) → per-category fee deduction → guarantee & sanity checks → dataset`

Built by a developer who runs a live prediction-market trading system and has paid the tuition on exactly which prices are real and which are fiction.

# Actor input Schema

## `stakeUsd` (type: `integer`):

Capital you would deploy into a single arb. Every executable price is a VWAP walked across the real order book to fill this much — so the reported edge is what you'd actually get at this size, not at the untradeable top-of-book tick.

## `minNetEdgePct` (type: `integer`):

Only return arbs whose profit AFTER fees, as a percent of capital deployed, is at least this. 1 = a guaranteed 1% return. Raise it to cut noise.

## `categories` (type: `array`):

Restrict to these market categories (e.g. sports, crypto, politics, finance, economics, culture, tech, mentions). Leave empty to scan all. Geopolitics markets are fee-free and always eligible.

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

Skip markets below this lifetime volume. Higher = fewer, more-liquid markets (less depth risk).

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

Stop after emitting this many opportunities.

## `includeWeatherEvents` (type: `boolean`):

Off by default. Weather temperature markets have thin books and a specialized resolution structure; a sum-of-legs arb on them reconstructs a separate signal and rarely clears the depth filter anyway. Turn on only if you specifically want them.

## `maxMarketsScanned` (type: `integer`):

Upper bound on Gamma markets pulled (ordered by volume, highest first). Bounds run time and cost.

## `maxBooksFetched` (type: `integer`):

Upper bound on CLOB order-book requests. Only groups that pass the cheap Gamma price screen consume this budget.

## `minGrossScreenPct` (type: `integer`):

How promising a group must look on free Gamma touch prices before spending order-book requests to confirm it. Lower catches more but costs more.

## Actor input object example

```json
{
  "stakeUsd": 100,
  "minNetEdgePct": 1,
  "categories": [
    "sports",
    "politics"
  ],
  "minVolume": 1000,
  "maxResults": 200,
  "includeWeatherEvents": false,
  "maxMarketsScanned": 2000,
  "maxBooksFetched": 400,
  "minGrossScreenPct": 0
}
```

# 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 = {
    "categories": [
        "sports",
        "politics"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("ebag73/polymarket-arb-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 = { "categories": [
        "sports",
        "politics",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("ebag73/polymarket-arb-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 '{
  "categories": [
    "sports",
    "politics"
  ]
}' |
apify call ebag73/polymarket-arb-scanner --silent --output-dataset

```

## MCP server setup

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

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Polymarket Real-Arb Scanner",
        "description": "Guaranteed, executable arbitrage on Polymarket, priced off real CLOB order-book depth at your stake, fee-adjusted per category. No phantom edges.",
        "version": "0.1",
        "x-build-id": "f4807wtQV6i8tPepX"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/ebag73~polymarket-arb-scanner/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-ebag73-polymarket-arb-scanner",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/ebag73~polymarket-arb-scanner/runs": {
            "post": {
                "operationId": "runs-sync-ebag73-polymarket-arb-scanner",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/ebag73~polymarket-arb-scanner/run-sync": {
            "post": {
                "operationId": "run-sync-ebag73-polymarket-arb-scanner",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "properties": {
                    "stakeUsd": {
                        "title": "Stake per opportunity (USD)",
                        "minimum": 1,
                        "maximum": 100000,
                        "type": "integer",
                        "description": "Capital you would deploy into a single arb. Every executable price is a VWAP walked across the real order book to fill this much — so the reported edge is what you'd actually get at this size, not at the untradeable top-of-book tick.",
                        "default": 100
                    },
                    "minNetEdgePct": {
                        "title": "Minimum net edge (%)",
                        "minimum": 0,
                        "maximum": 100,
                        "type": "integer",
                        "description": "Only return arbs whose profit AFTER fees, as a percent of capital deployed, is at least this. 1 = a guaranteed 1% return. Raise it to cut noise.",
                        "default": 1
                    },
                    "categories": {
                        "title": "Categories to scan",
                        "type": "array",
                        "description": "Restrict to these market categories (e.g. sports, crypto, politics, finance, economics, culture, tech, mentions). Leave empty to scan all. Geopolitics markets are fee-free and always eligible.",
                        "default": [],
                        "items": {
                            "type": "string"
                        }
                    },
                    "minVolume": {
                        "title": "Minimum market volume (USD)",
                        "minimum": 0,
                        "type": "integer",
                        "description": "Skip markets below this lifetime volume. Higher = fewer, more-liquid markets (less depth risk).",
                        "default": 1000
                    },
                    "maxResults": {
                        "title": "Max arbs to return",
                        "minimum": 1,
                        "maximum": 5000,
                        "type": "integer",
                        "description": "Stop after emitting this many opportunities.",
                        "default": 200
                    },
                    "includeWeatherEvents": {
                        "title": "Include daily-temperature (weather) events",
                        "type": "boolean",
                        "description": "Off by default. Weather temperature markets have thin books and a specialized resolution structure; a sum-of-legs arb on them reconstructs a separate signal and rarely clears the depth filter anyway. Turn on only if you specifically want them.",
                        "default": false
                    },
                    "maxMarketsScanned": {
                        "title": "Max markets to discover (cost guard)",
                        "minimum": 100,
                        "maximum": 20000,
                        "type": "integer",
                        "description": "Upper bound on Gamma markets pulled (ordered by volume, highest first). Bounds run time and cost.",
                        "default": 2000
                    },
                    "maxBooksFetched": {
                        "title": "Max order books to fetch (cost guard)",
                        "minimum": 10,
                        "maximum": 5000,
                        "type": "integer",
                        "description": "Upper bound on CLOB order-book requests. Only groups that pass the cheap Gamma price screen consume this budget.",
                        "default": 400
                    },
                    "minGrossScreenPct": {
                        "title": "Screen threshold (%)",
                        "minimum": 0,
                        "maximum": 50,
                        "type": "integer",
                        "description": "How promising a group must look on free Gamma touch prices before spending order-book requests to confirm it. Lower catches more but costs more.",
                        "default": 0
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
