# Polymarket Markets Scraper & Monitor - Prices, Volume, New (`neverempty/polymarket-markets-monitor`) Actor

Polymarket Markets in one table: question, price per outcome, best bid and ask, volume, liquidity, end date, tags and URL. Filter by keyword, tag, volume or days to close. Monitoring returns only new markets, price moves past your threshold and resolved markets.

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

## Pricing

from $4.00 / 1,000 market returneds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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 Markets Scraper & Monitor - Prices, Volume, New

Get **Polymarket markets** as one clean row per market: the question, every outcome and its price, best bid and ask, last trade, total and 24-hour volume, liquidity, end date, tags, the event it belongs to and its URL. Find markets by **keyword**, **tag**, **minimum volume or liquidity** and **days until the end date**. A **monitoring mode** returns only what changed since the last run: new markets, markets whose price moved past your threshold, and markets that resolved.

It reads Polymarket's official public market-data API (`gamma-api.polymarket.com`), the one Polymarket's own documentation points developers and AI agents to. No login, no API key, no wallet, no proxy. It only reads; it never places orders.

### Why this Actor

- **A monitor with your own threshold.** Run it on a schedule with `monitoringMode` and `priceChangePoints` (for example 5): you get only the markets where the price of any outcome moved 5 percentage points or more, with `previousOutcomePrices` and `priceChangePoints` next to the new price. Small moves add up, because each market is compared with the price you last received, not with the previous run.
- **New and resolved markets in the same feed.** A market that appears under your filters comes back once as `new-market`. A market you were watching that closes comes back once as `resolved`, with the final prices and `winningOutcome`.
- **Says whose number each price is.** `outcomePrices` is Polymarket's order-book midpoint (checked against Polymarket's order-book midpoint for 6 markets on 2026-10-05: identical). The "Buy" price on a Polymarket page is the best ask, which is a different number, so `bestBid`, `bestAsk` and `lastTradePrice` are separate columns and nothing is blended.
- **Filters are applied twice.** Volume, liquidity and end-date filters are sent to Polymarket and checked again on every row, so a market outside your filters is never returned or charged.
- **Honest empty results.** An unknown tag, a search with no match, a refusal, a rate limit and a list cut off part-way each come back as a separate free row that says what happened. A value Polymarket does not give is `null`, never 0.

Sister Actors: **[Kalshi Markets Scraper](https://apify.com/neverempty/kalshi-markets-scraper)** for the same table from Kalshi, and **[Polymarket Weather Markets](https://apify.com/neverempty/polymarket-weather-markets)** for temperature markets with the weather forecast on the same row.

### Input

| Field | Type | Default | What it does |
|---|---|---|---|
| `keywords` | list | (none; prefilled: bitcoin) | Words sent to Polymarket's own search, one per line. The search matches events; every open market of a matching event is returned. |
| `tags` | list | (none) | Polymarket tag slugs: `crypto`, `politics`, `sports`, `economy`, `geopolitics` ... (the last part of a topic page address). A market is returned when it carries any of them. |
| `minVolumeUsd` | integer | (none) | Only markets with at least this total volume. |
| `minLiquidityUsd` | integer | (none) | Only markets with at least this liquidity. |
| `endsWithinDays` | integer | (none) | Only markets whose end date is between now and this many days from now. |
| `sortBy` | select | `volume24hr` | `volume24hr`, `volume`, `liquidity`, `newest` or `endingSoon`. Used when no keyword is given. |
| `maxResults` | integer | 1000 | The most market rows returned and charged in one run (up to 50,000). |
| `monitoringMode` | boolean | false | Return only new markets, price moves past the threshold and resolved markets. See below. |
| `priceChangePoints` | integer | 5 | Monitoring: the move, in percentage points, that makes a market come back (1 to 100). |
| `resetMonitoringState` | boolean | false | Forget the remembered markets for these exact filters. |

With every field empty the Actor lists all open markets, most traded in the last 24 hours first, up to `maxResults`.

#### Example: crypto markets ending this week with real volume

```json
{ "tags": ["crypto"], "minVolumeUsd": 50000, "endsWithinDays": 7, "sortBy": "endingSoon" }
```

#### Example: alert when any politics market over $1M moves 5 points

```json
{ "tags": ["politics"], "minVolumeUsd": 1000000, "monitoringMode": true, "priceChangePoints": 5 }
```

### Output

One row per market:

```json
{
  "source": "polymarket-gamma-api",
  "status": "ok",
  "change": "price-moved",
  "marketId": "573656",
  "question": "Will Bitcoin hit $150k by December 31, 2026?",
  "outcomes": ["Yes", "No"],
  "outcomePrices": [0.0235, 0.9765],
  "bestBid": 0.02,
  "bestAsk": 0.027,
  "lastTradePrice": 0.028,
  "spread": 0.007,
  "oneDayPriceChange": -0.002,
  "previousOutcomePrices": [0.075, 0.925],
  "priceChangePoints": 5.15,
  "volumeUsd": 2863045.46,
  "volume24hUsd": 18250.11,
  "volume1wkUsd": 96113.5,
  "liquidityUsd": 51422.07,
  "endDate": "2027-01-01T05:00:00.000Z",
  "closed": false,
  "winningOutcome": null,
  "eventTitle": "When will Bitcoin hit $150k?",
  "eventSlug": "when-will-bitcoin-hit-150k",
  "tags": ["Crypto", "Bitcoin", "Crypto Prices"],
  "tagSlugs": ["crypto", "bitcoin", "crypto-prices"],
  "url": "https://polymarket.com/event/when-will-bitcoin-hit-150k/will-bitcoin-hit-150k-by-december-31-2026",
  "conditionId": "0x02deb9538f5c123373adaa4ee6217b01745f1662bc902e46ac92f3fe6f8741e8",
  "clobTokenIds": ["9369...6213", "5511...0538"],
  "scrapedAt": "2026-10-05T09:00:00.000Z"
}
```

(The example is shortened and its monitoring values are illustrative. Rows also carry `startDate`, `createdAt`, `updatedAt`, `active`, `closedTime`, `acceptingOrders`, `resolutionStatus`, `eventId`, `groupItemTitle`, `negRisk`, `slug`, `resolutionSource` and the full `description` with the resolution rules.)

- Prices are between 0 and 1: 0.62 means 62¢, an implied chance near 62%.
- `change` is `null` outside monitoring mode, and `new-market`, `price-moved` or `resolved` inside it.
- `winningOutcome` is filled only when the market is closed and exactly one outcome is priced at 1. Otherwise it is `null`.
- `tags` come from the market, or from its event when the row was found by keyword.

Rows whose `status` is not `ok` are free and explain themselves in `note`: `invalid-input`, `unknown-tag`, `no-markets-matched`, `blocked`, `unreadable`, `incomplete`, `baseline-recorded`, `no-change-since-last-run`, `budget-reached`.

### Monitoring mode

1. **First run** for a set of filters: the matching open markets and their prices are remembered. No market row is returned and nothing is charged; one free row says how many markets were remembered. Run once without monitoring mode if you also want the full list.
2. **Every later run** reads all matching open markets again and returns only:
   - `new-market`: a market that matches now and was not remembered;
   - `price-moved`: the price of any outcome is `priceChangePoints` or more away from the remembered price (exactly the threshold counts);
   - `resolved`: a remembered market that has closed, looked up by its ID.
3. The remembered price of a market changes only when that market is returned to you. A market that drifts 2 points per run comes back at the run where the total reaches your threshold.
4. A run that finds nothing returns one free row (`no-change-since-last-run`) and is charged one `scan-checked` event.
5. Changed markets that did not fit under `maxResults` or your maximum charge are not remembered as returned: the next run returns them.

The memory is kept per set of filters (keywords, tags, minimums, end-date window and threshold), in a named key-value store on your account. Two schedules with different filters do not disturb each other. Two runs with the same filters at the same moment can overwrite each other's memory, so schedule them apart.

### Pricing

Pay per event, no start fee:

- `market-returned`: one per market row. Free rows (anything whose `status` is not `ok`) are never charged.
- `scan-checked`: one per monitoring run that compared the markets and found nothing to return. The first monitoring run and runs that could not read the list are not charged.

Set a maximum charge per run in Apify and the Actor stops inside it and says so.

### Notes and limits

- **Freshness.** Polymarket serves this API through a cache of up to 5 minutes (`cache-control: max-age=300`), so a price can be up to about 5 minutes old. For tick-by-tick data use Polymarket's WebSocket feeds; this Actor is for lists, screening and alerts on a schedule of minutes or hours.
- **Only open markets are listed.** Closed markets appear only as `resolved` rows in monitoring mode.
- A market that leaves your filters while still open (for example its end date passes before it resolves) is kept in memory and reported as `resolved` when it closes.
- Keyword search is Polymarket's own search and matches events, so a market is returned because its event matched the word.
- A monitoring scan reads every matching market (100 per request). Narrow filters make it faster; the hard ceiling is 60,000 markets per tag or 5,000 events per keyword, and a list cut by the ceiling says so in a free row.
- The Actor sends at most about 8 requests per second; Polymarket's published limit for this endpoint is 300 requests per 10 seconds.
- Not affiliated with Polymarket. Prices are set by traders and change continuously; nothing here is financial advice.

### Thanks for using this Actor

We build these tools for people who run them every day, and we improve them from what users tell us.

- **Missing a field, or need another filter?** Tell us in the **Issues** tab. If the data is there, we add it.
- **Found a bug or a wrong value?** Post the run ID in the **Issues** tab. Wrong data is the thing we fix first.

If this Actor saved you time, a short review helps other people find it.

# Actor input Schema

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

Words to search Polymarket for, one per line (for example bitcoin, fed, election). Each keyword is sent to Polymarket's own search, which matches events; every open market of a matching event is returned. Leave empty to list markets without a search.

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

Polymarket tag slugs, one per line: the last part of a topic page address, for example crypto, politics, sports, economy, geopolitics. A market is returned when it carries any of the tags. A slug Polymarket does not know comes back as a free row that says so.

## `minVolumeUsd` (type: `integer`):

Only markets whose total traded volume is at least this many dollars. Leave empty for no minimum.

## `minLiquidityUsd` (type: `integer`):

Only markets whose order-book liquidity is at least this many dollars. Leave empty for no minimum.

## `endsWithinDays` (type: `integer`):

Only markets whose end date is between now and this many days from now. Leave empty for any end date.

## `sortBy` (type: `string`):

Order of the list when no keyword is given, and which markets come first when there are more than the maximum.

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

The most market rows this run returns (and charges for). In monitoring mode it limits the changed markets returned in one run; the rest come at the next run.

## `monitoringMode` (type: `boolean`):

Run the same filters on a schedule. The first run only remembers the matching markets and their prices (no rows, no charge). Every later run returns only new markets, markets whose price moved by the threshold or more since it was last returned, and markets that resolved.

## `priceChangePoints` (type: `integer`):

Monitoring mode: return a market when the price of any outcome moved at least this many percentage points from the remembered price (5 means for example 30¢ to 35¢). From 1 to 100.

## `resetMonitoringState` (type: `boolean`):

Clears the monitoring memory for these exact filters before the run, so the run starts again as a first run.

## Actor input object example

```json
{
  "keywords": [
    "bitcoin"
  ],
  "sortBy": "volume24hr",
  "maxResults": 1000,
  "monitoringMode": false,
  "priceChangePoints": 5,
  "resetMonitoringState": false
}
```

# Actor output Schema

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

One row per Polymarket market: question, outcomes and the price of each, best bid and ask, last trade, volume, liquidity, end date, tags, event and URL; in monitoring mode only new markets, markets whose price moved past the threshold (with the remembered price) and resolved markets. Unknown tags, searches with no match, refusals and limits come back as free rows that say why.

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

// Run the Actor and wait for it to finish
const run = await client.actor("neverempty/polymarket-markets-monitor").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 = { "keywords": ["bitcoin"] }

# Run the Actor and wait for it to finish
run = client.actor("neverempty/polymarket-markets-monitor").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 '{
  "keywords": [
    "bitcoin"
  ]
}' |
apify call neverempty/polymarket-markets-monitor --silent --output-dataset

```

## MCP server setup

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

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/BavR3zYVSMzNDTszW/builds/5utPnvOYZ1A6j4QWE/openapi.json
