# Polymarket Markets Scraper (`scrapyx/polymarket-markets-scraper`) Actor

Prediction markets from Polymarket's public API: every active event and its Yes/No markets with live prices, bid/ask, spread, volume, liquidity, end date and resolution state, plus event tags. Walk by tag, search by text, or fetch specific events. Flags markets that have already ended.

- **URL**: https://apify.com/scrapyx/polymarket-markets-scraper.md
- **Developed by:** [Ibnu Adzim](https://apify.com/scrapyx) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.10 / 1,000 results

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

Prediction markets from **Polymarket**, straight from its public Gamma API:
every active event and each of its Yes/No markets with live prices (outcome
prices, best bid/ask, spread, last trade, 1-day and 1-week change), volume
(total, 24h, 1-week), liquidity, start/end dates, resolution state, CLOB token
ids, and the event's title, slug, tags and market count on every row.

HTTP only, no key, no login, no browser. One request per 100 events.

### What it is for

- **Odds monitoring** — run on a schedule, keep `yesPrice` by `marketId`.
- **Category dashboards** — walk `politics`, `crypto`, `sports`, … by tag.
- **Arbitrage / research feeds** — `clobTokenIds` and `conditionId` on every
  row link straight to the order book.

### Input

| field | what it does |
| --- | --- |
| `tags` | Tag slugs to walk (`politics`, `crypto`, `nba`, …). Empty = every active event. |
| `searchTerms` | Free-text search over event titles. |
| `eventSlugs` | Specific events, by slug or URL. |
| `includeClosed`, `excludeEnded` | Status filters — see below. |
| `sortBy`, `ascending`, `minVolume`, `minLiquidity`, `endingAfter`, `endingBefore` | Ordering and filters. |
| `includeEventRows` | Also emit one `EVENT` row per event. |
| `maxItems`, `maxConcurrency`, `minRequestInterval`, `proxyConfiguration` | Limits. |

Each tag, search term and event slug is its own target with its own
`SEARCH_SUMMARY`.

### Four things about this API worth knowing before you trust a run

#### 1. "Active" includes markets that have already ended

`active=true&closed=false` is the API's live set — and 17 of the top 100
markets by 24-hour volume on the day this was built had an `endDate` in the
past: the day's Fed-decision markets, ended, awaiting resolution, still
trading. A monitor that reads "active" as "open question" reports settled
questions as live odds. Every row carries `hasEnded` and `acceptingOrders`;
`excludeEnded` drops them and the summary counts them.

#### 2. A multi-outcome question is N binary markets, not one market

Every Polymarket market has exactly two outcomes. "Fed Decision in
September?" is an **event** with seven Yes/No markets ("50+ bps decrease",
"25 bps decrease", …) whose Yes prices sum to ~1. Read flat, they look like
seven unrelated questions. Every `MARKET` row carries `eventId`,
`eventTitle`, `eventSlug`, `outcomeLabel` and `eventMarketCount` so you can
regroup; `includeEventRows` adds an `EVENT` row with the outcome/price
summary in one place.

#### 3. Page size is capped at 100 and offset stops at 2,000 — silently, then loudly

Ask for `limit=5000`, get 100 with HTTP 200. Ask for `offset=2001`, get a
422\. This Actor uses the API's keyset cursor instead, which walks all ~21,000
active events (210 pages, measured), so a tag walk is never truncated.

#### 4. Nine fields are JSON inside JSON

`outcomes`, `outcomePrices` and `clobTokenIds` arrive as strings:
`"[\"0.115\", \"0.885\"]"`. Read naively, the first outcome price is the
character `[`. Parsed here; `outcomePrices` is a list of floats, `yesPrice`
and `noPrice` are floats, and a row whose price list does not parse is
refused and counted rather than shipped with a probability of `[`.

### Other things measured

- `sortBy` takes only the seven fields the API accepts; anything else is
  answered with a 422 upstream and refused here.
- An unknown tag returns nothing (`no_matches`), not everything.
- The API's own `liquidity_min` parameter answers HTTP 500; `minLiquidity` is
  applied locally instead. `volume_min`, `end_date_min/max` work upstream.
- On the search endpoint the `active` parameter is silently ignored (16 of 20
  results came back closed); `events_status=active` is what narrows it, and
  the Actor also drops any closed event the search still returns.
- `volume` and `liquidity` arrive as 18-decimal strings beside float twins;
  floats are emitted. `volume` is null on brand-new markets with no trades.
- `bestBid`/`bestAsk` are null on ~10% of rows (no order book).

### Output

- **`MARKET`** — `marketId`, `conditionId`, `question`, `slug`, `url`,
  `outcomeLabel`, `outcomes`, `outcomePrices`, `yesPrice`, `noPrice`,
  `impliedProbability`, `bestBid`, `bestAsk`, `spread`, `lastTradePrice`,
  `oneDayPriceChange`, `oneWeekPriceChange`, `volume`, `volume24hr`,
  `volume1wk`, `liquidity`, `startDate`, `endDate`, `hasEnded`,
  `acceptingOrders`, `active`, `closed`, `negRisk`, `resolutionSource`,
  `umaResolutionStatus`, `clobTokenIds`, `description`, `eventId`,
  `eventTitle`, `eventSlug`, `eventUrl`, `eventMarketCount`, `eventVolume`,
  `eventVolume24hr`, `eventLiquidity`, `eventEndDate`, `eventTags`.
- **`EVENT`** (optional) — `eventId`, `title`, `slug`, `url`, `marketCount`,
  `outcomes` (label + yesPrice per market), `volume`, `volume24hr`,
  `liquidity`, `startDate`, `endDate`, `hasEnded`, `negRisk`, `tags`.
- **`SEARCH_SUMMARY`** — one per target: `eventsSeen`, `eventsReturned`,
  `marketsReturned`, `pagesFetched`, `stoppedReason`, `filteredOut`,
  `endedRows`, `negRiskRows`, `rowsWithoutOrderBook`,
  `rowsRefusedUnparsablePrices`, `totalClaimedBySite` (search only).
- **`ERROR`** — `invalid_input`, `bad_request`, `payload_shape_changed`,
  `fetch_failed`, with detail.

### Known limits

- Prices are the API's snapshot at fetch time; no order-book depth or price
  history (the CLOB API has both — a natural follow-up).
- Search is Polymarket's own title search; it does not search descriptions.
- Closed markets are walkable with `includeClosed` but there are far more of
  them than active ones; set `maxItems` accordingly.

# Actor input Schema

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

Polymarket tag slugs to walk: politics, crypto, sports, business, science, pop-culture, geopolitics, elections, nba, bitcoin... Each becomes its own target. Empty (with no search terms or event slugs) walks every active event on the site, ordered by sortBy. An unknown tag returns nothing.

## `searchTerms` (type: `array`):

Free-text search over event titles (Polymarket's own search). Each term is its own target. Results are restricted to active events unless includeClosed is on.

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

Specific events, by slug or full URL: 'fed-decision-in-september-762' or https://polymarket.com/event/fed-decision-in-september-762. One request each.

## `includeClosed` (type: `boolean`):

Off by default: only active, unresolved markets. On: walks closed markets too (there are far more of them).

## `excludeEnded` (type: `boolean`):

Polymarket's 'active' set includes markets that have already ended and await resolution (17 of the top 100 by volume on the day this was built). Every row carries hasEnded; this drops them.

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

Listing order for tag walks. Descending unless ascending is on. Only these fields are accepted by the API.

## `ascending` (type: `boolean`):

Reverse the order (e.g. endDate ascending = markets resolving soonest first).

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

Skip events with less lifetime volume than this. Applied by the API.

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

Skip markets with less liquidity than this. Applied locally (the API's own liquidity filter answers HTTP 500).

## `endingAfter` (type: `string`):

Only events ending on or after this date, YYYY-MM-DD.

## `endingBefore` (type: `string`):

Only events ending on or before this date, YYYY-MM-DD.

## `includeEventRows` (type: `boolean`):

Off by default: rows are MARKETs (one per binary market, with its event's id, title, slug, tags and market count on every row). On: an EVENT row with the aggregate and an outcome/price summary precedes each event's markets.

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

Overall cap on MARKET rows across every target. ~21,000 active events exist (most are single-match sports markets); a multi-outcome event alone can carry 100+ markets.

## `maxConcurrency` (type: `integer`):

Parallel requests across targets. Event pages are ~4.5 MB each.

## `minRequestInterval` (type: `integer`):

Politeness delay between request starts. The API tolerated 40 unpaced requests in 16 s; leave at 0 unless a run reports rate limiting.

## `proxyConfiguration` (type: `object`):

Optional. Not needed: the API answered from a plain IP. Enable Apify's free datacenter proxy only if a cloud run reports fetch\_failed.

## Actor input object example

```json
{
  "tags": [
    "politics"
  ],
  "includeClosed": false,
  "excludeEnded": false,
  "sortBy": "volume24hr",
  "ascending": false,
  "includeEventRows": false,
  "maxItems": 500,
  "maxConcurrency": 2,
  "minRequestInterval": 0,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `items` (type: `string`):

One row per scraped record. See the dataset's default view for field definitions.

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

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

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

```

## MCP server setup

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

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/KY4425fbcspKiv4BA/builds/1UHzlkVO2qTitu6MP/openapi.json
