# Polymarket Scraper: Markets, Odds & Price History (`mersetti/polymarket-markets`) Actor

Scrape Polymarket prediction markets: questions, outcome odds (probability %), volume, liquidity, bid/ask, end dates, resolved outcomes and price history. Top by volume, trending, ending soon, keyword search, tags or event URLs. Monitor mode reports new markets and odds swings.

- **URL**: https://apify.com/mersetti/polymarket-markets.md
- **Developed by:** [Max Prozapas](https://apify.com/mersetti) (community)
- **Categories:** News, AI, Automation
- **Stats:** 2 total users, 1 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/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

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

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Polymarket Scraper — Markets, Odds & Price History

Scrape **Polymarket prediction markets** into clean, structured data: market questions, **outcome odds
(probability %)**, prices, best bid/ask, **volume (total, 24h, 7 days)**, liquidity, start and end dates,
categories/tags, **resolved outcomes** and optional **price history** time series. No login, API key, wallet
or browser needed. The Actor uses Polymarket's public Gamma and CLOB APIs.

**Monitor mode** remembers every market and its odds between runs and outputs **only what changed**: new markets,
**odds swings above your threshold** (in percentage points) and markets that closed or resolved. Schedule it hourly
and get a prediction-market alert feed. You pay only for the changes.

### Why this Polymarket scraper

- **Six ways to pick markets**: top by total volume, trending (24h volume), ending soon, keyword search,
  tags/categories (politics, crypto, sports, ...) and specific event or market URLs.
- **Odds ready to use**: Polymarket returns outcomes, prices and token IDs as JSON strings. The Actor parses them into
  an `outcomes` array with `price` (0–1) and `probability` (%) for each outcome, plus `leadingOutcome`.
- **Price history**: add a probability time series per market (last hour up to all time, custom resolution).
- **Resolved markets**: closed markets include `resolvedOutcome` (e.g. `"Yes"`, a team name, or `"50-50"`).
- **Odds-change monitoring** with a free, silent baseline run, a configurable threshold and "compare with previous
  run" or "compare with last alert" logic.
- **Filters**: only open markets, minimum volume, minimum liquidity, tags, max items.
- **Fast and polite**: official public JSON APIs, cursor pagination, retries with backoff and small delays.

### Use cases

- **Research**: study forecasting accuracy, market efficiency, calibration and crowd wisdom with structured data.
- **News & media**: quote live odds on elections, Fed decisions, geopolitics, sports and crypto; spot big moves for stories.
- **Trading models & quant**: feed odds, spreads, volume and price history into models, backtests and dashboards.
- **AI agents & LLM tools**: give agents current probabilities on world events as JSON (works with Apify MCP / API).
- **Sentiment dashboards**: track what the market thinks about elections, rates, wars or launches in Google Sheets,
  Looker Studio, Power BI or your database.
- **Alerts**: get a Slack/email/webhook message when odds on a market you follow swing by 10 points.

### Input

| Field | Description |
|---|---|
| `mode` | `topByVolume` (default), `trending` (24h volume), `endingSoon`, `search`, `byTagSlug`, `byEventSlugs`. |
| `keywords` | Search terms for `search` mode, e.g. `bitcoin`, `fed rates`. `maxItems` is split across keywords. |
| `tags` | Tag slugs such as `politics`, `crypto`, `sports`, `geopolitics`, `economy`. Required for `byTagSlug`; in other modes they narrow the results. |
| `eventSlugs` | Event URLs (`https://polymarket.com/event/<slug>`), market URLs or bare slugs for `byEventSlugs`. |
| `onlyActive` | Only open markets (default on). Turn off to include closed/resolved markets. |
| `minVolume` | Minimum total volume in USD (default 0). |
| `minLiquidity` | Minimum liquidity in USD (default 0). |
| `maxItems` | Maximum number of markets (default 100, up to 10,000). In monitor mode: size of the watched list. |
| `includePriceHistory` | Add `priceHistory` (probability % of the first outcome over time). One extra request per market. |
| `priceHistoryInterval` | `1h`, `6h`, `1d`, `1w` (default), `1m`, `max`. |
| `priceHistoryFidelity` | Minutes between points; 0 = automatic. |
| `monitorMode` | Output only changes since the previous run. |
| `monitorId` | Separate saved state per watchlist (default `default`). |
| `oddsChangeThreshold` | Minimum move in percentage points to report an odds change (default 5). |
| `compareTo` | `lastRun` (default) or `lastAlert` (also catches slow drifts). |
| `baselineOnFirstRun` | First run of a watchlist saves odds silently (no output, no cost). |
| `reportNewMarkets`, `reportOddsChanges`, `reportResolved` | Change types to output (default on). |

#### Example input: trending markets with price history

```json
{
  "mode": "trending",
  "maxItems": 50,
  "minLiquidity": 10000,
  "includePriceHistory": true,
  "priceHistoryInterval": "1d"
}
```

#### Example input: keyword search in a category

```json
{
  "mode": "search",
  "keywords": ["bitcoin", "fed rates"],
  "minVolume": 5000,
  "maxItems": 100
}
```

#### Example input: specific events

```json
{
  "mode": "byEventSlugs",
  "eventSlugs": [
    "https://polymarket.com/event/will-the-us-invade-iran-before-2027",
    "presidential-election-winner-2024"
  ],
  "onlyActive": false
}
```

#### Example input: hourly odds-swing monitor on politics

```json
{
  "mode": "byTagSlug",
  "tags": ["politics", "geopolitics"],
  "maxItems": 300,
  "minVolume": 100000,
  "monitorMode": true,
  "monitorId": "politics-swings",
  "oddsChangeThreshold": 5
}
```

### Output

One row per market. Real example from a test run (`mode: "trending"`, price history `1d` at 240-minute resolution):

```json
{
  "marketId": "5184010",
  "question": "Will Manchester City FC win on 2026-10-14?",
  "slug": "ucl-mnc-psg-2026-10-14-mnc",
  "url": "https://polymarket.com/event/ucl-mnc-psg-2026-10-14",
  "eventId": "1114982",
  "eventTitle": "Manchester City FC vs. Paris Saint-Germain FC",
  "eventSlug": "ucl-mnc-psg-2026-10-14",
  "groupItemTitle": "Manchester City FC",
  "category": "Sports",
  "tags": ["Sports", "Games", "Soccer", "UCL", "UCL Matchday"],
  "outcomes": [
    { "name": "Yes", "price": 0.475, "probability": 47.5, "tokenId": "30251429612449430455264370878544256883870033672058898853863549939939326972421" },
    { "name": "No", "price": 0.525, "probability": 52.5, "tokenId": "78954184754912276237571294773010091711341505525591694748702579984199183887817" }
  ],
  "leadingOutcome": "No",
  "leadingProbability": 52.5,
  "volume": 875707.65,
  "volume24h": 857624.68,
  "volume1wk": 873415.78,
  "volume1mo": 875707.65,
  "liquidity": 612908.42,
  "lastTradePrice": 0.48,
  "bestBid": 0.47,
  "bestAsk": 0.48,
  "spread": 0.01,
  "oneDayPriceChange": 0.03,
  "oneWeekPriceChange": 0.06,
  "startDate": "2026-10-01T18:00:15.000Z",
  "endDate": "2026-10-14T19:00:00.000Z",
  "createdAt": "2026-10-01T18:00:05.703Z",
  "closedTime": null,
  "active": true,
  "closed": false,
  "acceptingOrders": true,
  "resolved": false,
  "resolvedOutcome": null,
  "negRisk": true,
  "conditionId": "0xf8f3e450b7c01e9d8876f831eeb8955690af9a62121f9341ff30ebb60cc996f5",
  "resolutionSource": "https://www.uefa.com/",
  "image": "https://polymarket-upload.s3.us-east-2.amazonaws.com/champions-league-pic-QIUFsL8vaDdq.png",
  "scrapedAt": "2026-10-14T09:53:08.926Z",
  "priceHistoryOutcome": "Yes",
  "priceHistory": [
    { "t": "2026-10-13T12:00:38.000Z", "p": 44.5 },
    { "t": "2026-10-13T16:00:26.000Z", "p": 44.5 },
    { "t": "2026-10-13T20:00:27.000Z", "p": 44.5 },
    { "t": "2026-10-14T00:00:39.000Z", "p": 44.5 },
    { "t": "2026-10-14T04:00:27.000Z", "p": 45.5 },
    { "t": "2026-10-14T08:00:27.000Z", "p": 46.5 },
    { "t": "2026-10-14T09:52:23.000Z", "p": 47.5 }
  ]
}
```

Units: `outcomes[].probability`, `leadingProbability` and `priceHistory[].p` are **percent** (0–100).
`outcomes[].price`, `lastTradePrice`, `bestBid`, `bestAsk`, `spread`, `oneDayPriceChange` and `oneWeekPriceChange`
are in Polymarket **price units** (0–1 USDC per share, so 0.03 = +3 percentage points). Volumes and liquidity are in USD.
In `search` mode each row also has `searchKeyword`.

Closed markets (with `onlyActive: false`) have `closed: true`, `closedTime`, `resolved` and `resolvedOutcome`
(the winning outcome, or `"50-50"`).

In monitor mode each row also has:

```json
{
  "changeType": "oddsChange",
  "changes": ["oddsChange"],
  "previousOutcomes": [{ "name": "Yes", "probability": 71.5 }, { "name": "No", "probability": 28.5 }],
  "probabilityChangePp": 10,
  "maxProbabilityChangePp": 10,
  "previousCheckedAt": "2026-10-14T09:51:59.307Z",
  "firstSeenAt": "2026-10-14T09:51:48.380Z"
}
```

`changeType` is one of `new`, `oddsChange`, `resolved`. `probabilityChangePp` is the signed move of the first outcome
(e.g. "Yes"), `maxProbabilityChangePp` the largest move of any outcome.

A run report (inputs, tags resolved, events found, markets matched, errors, monitor counts) is saved to the `SUMMARY`
record of the run's key-value store. Invalid slugs or tags are listed there with an error and the run continues.

### How monitoring works

- The state is saved in the named key-value store `polymarket-monitor` under `state-<monitorId>` (gzipped JSON).
- The first run of a watchlist (mode + tags + keywords + events) only saves the odds (if `baselineOnFirstRun` is on).
- **New** = a market this monitor has never seen. With `topByVolume`/`trending`, a market that enters your top-N list for
  the first time counts as new.
- **Odds change** = any outcome moved by at least `oddsChangeThreshold` percentage points since the previous run
  (`compareTo: "lastRun"`) or since it was last reported (`"lastAlert"`).
- **Resolved** = a watched market closed. Markets that drop out of your list are checked by ID, so you are told when
  they resolve even with `onlyActive` on.
- If the run's cost limit is reached, unreported changes stay pending for the next run. Markets not seen for 30 days
  are forgotten.

### Pricing

**Pay per market**: you pay a small fee for each market row in the dataset (event `market`). 100 trending markets cost
100 results. In monitor mode you pay only for reported changes, so an hourly watch on hundreds of markets usually costs
cents per day. Price history is included in the same per-market price. Set a maximum cost per run and the Actor stops
cleanly when it is reached.

### FAQ

**Do I need a Polymarket account, API key or crypto wallet?**
No. The Actor reads Polymarket's public market data APIs, the same data the website shows to every visitor.

**How fresh are the odds?**
They are live at run time. `outcomes[].price` is Polymarket's displayed price (midpoint or last trade), and
`bestBid`/`bestAsk` show the order book. Schedule the Actor (every 5–60 minutes) for near-real-time tracking.

**How do I find a tag slug?**
Open a category on polymarket.com; the last part of the URL is the slug (`/predictions/crypto` → `crypto`).
Common ones: `politics`, `crypto`, `sports`, `geopolitics`, `economy`, `tech`, `culture`, `elections`.

**Why does an event return several markets?**
Polymarket events with several possible answers (e.g. "Fed decision in October") are made of one Yes/No market per
answer. Each one is a row; `eventTitle` and `groupItemTitle` tell them apart. Use a market URL to get just one.

**Can I get historical prices for resolved markets?**
Yes. Use `onlyActive: false`, `includePriceHistory: true` and `priceHistoryInterval: "max"`.

**Can I export to Excel, CSV or Google Sheets?**
Yes. Apify datasets download as JSON, CSV, Excel, XML or HTML, and integrate with Google Sheets, Zapier, Make, Slack
and webhooks.

### Limitations

- Price history covers the first outcome of each market (e.g. "Yes"); for Yes/No markets the other side is 100 − p.
- Keyword search uses Polymarket's own search ranking; results per keyword are capped at 20 result pages.
- Markets restricted in some regions are still listed (the data is public), but trading rules are up to Polymarket.
- Volumes and liquidity are as reported by Polymarket's API and may differ slightly from the website's rounding.

### Disclaimer

This Actor provides data only. **Nothing here is financial, investment or betting advice.** Prediction market prices
reflect traders' bets, not certainties, and can be wrong, illiquid or manipulated. Do your own research.

### Legal

The Actor reads only public market data that Polymarket publishes through its public APIs. It does not log in, trade,
bypass protections or collect personal data. Market texts and images remain the property of their owners. Make sure
your use of the data and any trading complies with Polymarket's terms and the laws of your jurisdiction.

### Related tools

- [Substack to Markdown for AI & RAG](https://apify.com/mersetti/substack-to-markdown)
- [Career Sites Jobs Feed (11 ATS)](https://apify.com/mersetti/ats-jobs-feed)
- [Shopify Store Products & Price Change Monitor](https://apify.com/mersetti/shopify-products-monitor)

# Actor input Schema

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

Which markets to load. "topByVolume": highest total volume. "trending": highest 24h volume. "endingSoon": markets closing next. "search": keyword search (uses Keywords). "byTagSlug": markets in Polymarket tags/categories (uses Tags). "byEventSlugs": specific events or markets (uses Event URLs or slugs).

## `keywords` (type: `array`):

Search terms for mode "search", e.g. "bitcoin", "fed rates", "election". Each keyword runs a Polymarket search; maxItems is split across keywords.

## `tags` (type: `array`):

Polymarket tag slugs, e.g. "politics", "crypto", "sports", "geopolitics", "economy" (a URL like https://polymarket.com/predictions/crypto also works). Required for mode "byTagSlug". In topByVolume, trending, endingSoon and search modes they narrow results to these tags.

## `eventSlugs` (type: `array`):

Polymarket event URLs (https://polymarket.com/event/<slug>), market URLs (https://polymarket.com/event/<event>/<market> or /market/<slug>) or bare slugs. All markets of an event are returned; a market URL returns just that market.

## `onlyActive` (type: `boolean`):

Only markets that are active and not closed. Turn off to include closed and resolved markets (with resolvedOutcome).

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

Skip markets with lower total traded volume. 0 = no limit.

## `minLiquidity` (type: `integer`):

Skip markets with lower order book liquidity. 0 = no limit.

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

Maximum number of markets to load. In monitor mode this is the size of the watched list (e.g. top 200 trending markets); you are charged only for reported changes.

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

Adds priceHistory (time series of the first outcome's probability in %, e.g. "Yes") from Polymarket's CLOB API. One extra request per market.

## `priceHistoryInterval` (type: `string`):

Time span of the price history.

## `priceHistoryFidelity` (type: `integer`):

Minutes between data points. 0 = automatic (1h: 1 min, 6h: 5 min, 1d and 1w: 60 min, 1m: 6 h, all time: 1 day).

## `monitorMode` (type: `boolean`):

Remembers markets and odds between runs and outputs only changes: new markets, odds changes of at least the threshold, and markets that closed/resolved. Schedule the Actor (e.g. hourly) for an alert feed. You pay only for changes.

## `monitorId` (type: `string`):

Name of the saved state. Use different IDs for different watchlists.

## `oddsChangeThreshold` (type: `integer`):

Report a market when any outcome's probability moved by at least this many percentage points (e.g. 5 = from 40% to 45%).

## `compareTo` (type: `string`):

"lastRun": odds from the previous run (detects fast swings). "lastAlert": odds when the market was last reported or first seen (also catches slow drifts).

## `baselineOnFirstRun` (type: `boolean`):

The first run of a watchlist only saves the current markets and odds (no output, no cost), so later runs report real changes.

## `reportNewMarkets` (type: `boolean`):

Output markets this monitor has not seen before (changeType = new).

## `reportOddsChanges` (type: `boolean`):

Output markets whose odds moved by at least the threshold (changeType = oddsChange).

## `reportResolved` (type: `boolean`):

Output watched markets that closed since the previous run, with the winning outcome (changeType = resolved).

## Actor input object example

```json
{
  "mode": "topByVolume",
  "keywords": [
    "bitcoin",
    "fed rates"
  ],
  "eventSlugs": [
    "https://polymarket.com/event/will-the-us-invade-iran-before-2027"
  ],
  "onlyActive": true,
  "minVolume": 0,
  "minLiquidity": 0,
  "maxItems": 50,
  "includePriceHistory": false,
  "priceHistoryInterval": "1w",
  "priceHistoryFidelity": 0,
  "monitorMode": false,
  "monitorId": "default",
  "oddsChangeThreshold": 5,
  "compareTo": "lastRun",
  "baselineOnFirstRun": true,
  "reportNewMarkets": true,
  "reportOddsChanges": true,
  "reportResolved": true
}
```

# Actor output Schema

## `results` (type: `string`):

No description

## `summary` (type: `string`):

No description

# 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": "topByVolume",
    "keywords": [
        "bitcoin",
        "fed rates"
    ],
    "eventSlugs": [
        "https://polymarket.com/event/will-the-us-invade-iran-before-2027"
    ],
    "maxItems": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("mersetti/polymarket-markets").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": "topByVolume",
    "keywords": [
        "bitcoin",
        "fed rates",
    ],
    "eventSlugs": ["https://polymarket.com/event/will-the-us-invade-iran-before-2027"],
    "maxItems": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("mersetti/polymarket-markets").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 '{
  "mode": "topByVolume",
  "keywords": [
    "bitcoin",
    "fed rates"
  ],
  "eventSlugs": [
    "https://polymarket.com/event/will-the-us-invade-iran-before-2027"
  ],
  "maxItems": 50
}' |
apify call mersetti/polymarket-markets --silent --output-dataset

```

## MCP server setup

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

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/kNuoMkW3Lr4LDMvkg/builds/GAOpFtvlfYaAgoMlT/openapi.json
