Prediction Market Odds Scraper (Polymarket & Kalshi) avatar

Prediction Market Odds Scraper (Polymarket & Kalshi)

Pricing

from $1.00 / 1,000 market scrapeds

Go to Apify Store
Prediction Market Odds Scraper (Polymarket & Kalshi)

Prediction Market Odds Scraper (Polymarket & Kalshi)

Scrape Polymarket and Kalshi prediction market odds: 0-1 prices, implied probability, bid/ask, volume, liquidity and orderbook depth. No login. Pay per market.

Pricing

from $1.00 / 1,000 market scrapeds

Rating

0.0

(0)

Developer

Romas Jurevicius

Romas Jurevicius

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

10 days ago

Last modified

Categories

Share

Prediction Market Odds Scraper (Polymarket & Kalshi)

Prediction Market Odds turns the two largest prediction exchanges into one clean, machine-readable feed. It returns one JSON record per market - "Fed cut in December?", "Bitcoin above $110k on October 11?", "Who will be the next NATO Secretary General?", sports and esports matchups - with every price normalized to a 0-1 probability (0.0565 = 5.65%), the implied probability in percent, the best bid and ask, the last trade, lifetime and 24-hour volume, resting liquidity, open interest, the close date, the resolution rules and the exchange's own URL. Polymarket and Kalshi rows have the same shape, so a Kalshi contract at 6 cents and a Polymarket share at $0.06 compare directly. No account, API key or login is needed: both exchanges publish this data through official public endpoints.

Typical uses: feeding an AI trading agent that cross-references macro odds (rate decisions, CPI prints, elections) before placing trades, scanning for price disagreements between Polymarket and Kalshi on the same question, tracking a watchlist of markets every few minutes into a database, alerting a Telegram or Slack channel when a probability crosses a threshold, or backfilling a research dataset of resolved markets with their final prices.

Two sources are supported and can be combined in one run:

  • polymarket - the Gamma API (gamma-api.polymarket.com) for events, markets and tags, plus the CLOB API (clob.polymarket.com) for orderbooks. Prices are USDC per share, already 0-1.
  • kalshi - the trade API v2 (api.elections.kalshi.com/trade-api/v2) for events, markets and orderbooks. Prices are published in dollars per $1 contract and arrive as 0-1 floats; legacy integer-cent fields are converted too.

How to scrape prediction market odds

  1. Open the Actor in Apify Console and click Try for free.
  2. Pick the Sources (default: both) and the Market status (default: active, i.e. tradeable now; closed adds resolved and settled markets; all for both).
  3. Narrow the output with Search (bitcoin, fed rate, nato), Polymarket tags (crypto, politics, fed) or Kalshi series tickers (KXBTCD, KXFED, KXCPI). Filtering happens before anything is charged.
  4. Set Max markets per source (default 30) and turn on Include orderbook if you need depth beyond the top of book.
  5. Click Start. Results appear in the Output tab within seconds and can be downloaded as JSON, CSV or Excel, or read through the API.

How the fetching works

For Polymarket the Actor lists events with their nested markets from GET /events (100 per page - the API caps limit there - ordered by 24-hour volume for active markets and by end date for closed ones), optionally one request series per tag slug, and pages with offset until the cap or the end of the list. Each nested market carries its outcomes, quoted prices, best bid/ask, last trade, volume and liquidity, so no second request is needed per market. With Include orderbook on, the CLOB POST /books endpoint is called with batches of 20 token ids, and the best bid/ask are refreshed from the live book.

For Kalshi, active and closed markets come from GET /events?with_nested_markets=true (200 events per page, with category and settlement sources), paged with the API's cursor; settled markets come from GET /markets?status=settled (1,000 per page) because the events listing does not nest markets once an event has settled. Multivariate "parlay" combinations (mve_collection_ticker) are skipped: they carry no odds of their own. Each market's quote is read from yes_bid/yes_ask/no_bid/no_ask; the orderbook endpoint is called once per market when Include orderbook is on.

Requests are serialized with a 250 ms gap and retried on network errors, 429 and 5xx responses. Markets are pushed most-traded first, and every market id is remembered in the run state so a migrated or resurrected run never charges the same market twice.

Prediction Market Odds Scraper pricing

This Actor uses Apify's pay-per-event model. There is no subscription and no per-result platform fee on top:

  • Actor start: a flat fee of $0.00005 per run.
  • Market scraped: $0.001 per market pushed to your dataset ($1 per 1,000 markets).
  • Orderbook fetched: $0.0005 per market whose orderbook was fetched (only with Include orderbook on, and only when the exchange returned a book).

Search, status, tag and series filters are free and applied before charging - a run for search: "fed" on both exchanges is typically 20-60 markets, well under $0.10. Status records for a source that was unavailable or returned nothing are free. You can cap spending with Maximum cost per run; when the cap is reached the Actor stops cleanly and keeps everything scraped so far.

Input example

{
"sources": ["polymarket", "kalshi"],
"status": "active",
"search": "bitcoin",
"tags": ["crypto"],
"seriesTickers": ["KXBTCD"],
"maxMarketsPerSource": 200,
"includeOrderbook": false,
"proxyConfiguration": { "useApifyProxy": true }
}

Input fields

FieldTypeDefaultDescription
sourcesarray of polymarket / kalshibothWhich exchanges to read. Each record carries its source.
statusactive / closed / allactiveactive = tradeable now; closed = trading ended, including resolved/settled; all = both.
searchstringnoneCase-insensitive filter on question, event title, slug, ticker and event/series ticker. All words must appear.
tagsarray of stringsnonePolymarket only: event tag slugs such as crypto, politics, fed, bitcoin, sports. One request series per tag.
seriesTickersarray of stringsnoneKalshi only: series such as KXBTCD, KXFED, KXCPI, KXNEXTNATOSECGEN.
maxMarketsPerSourceinteger30Stop a source once this many markets matched the filters (max 20,000).
includeOrderbookbooleanfalseFetch live books: top 10 levels per outcome, depth, resting dollars; refreshes best bid/ask.
proxyConfigurationobjectApify ProxyStandard Apify proxy settings.

Output example

One dataset item per market (Kalshi example, captured 2026-10-11):

{
"source": "kalshi",
"marketId": "KXNEWPOPE-70-LANT",
"eventId": "KXNEWPOPE-70",
"eventTitle": "Who will the next Pope be?",
"question": "Who will the next Pope be? (Luis Antonio Tagle)",
"slug": null,
"ticker": "KXNEWPOPE-70-LANT",
"seriesTicker": "KXNEWPOPE",
"url": "https://kalshi.com/markets/kxnewpope/who-will-the-next-pope-be/kxnewpope-70",
"outcomes": [
{ "name": "Yes", "price": 0.0565, "impliedProbabilityPct": 5.65, "bestBid": 0.041, "bestAsk": 0.072, "tokenId": null },
{ "name": "No", "price": 0.9435, "impliedProbabilityPct": 94.35, "bestBid": 0.928, "bestAsk": 0.959, "tokenId": null }
],
"isBinary": true,
"yesPrice": 0.0565,
"noPrice": 0.9435,
"lastTradePrice": 0.04,
"spread": 0.031,
"volumeUsd": 21292.58,
"volume24hUsd": 1376.83,
"volumeContracts": 21292.58,
"volume24hContracts": 1376.83,
"liquidityUsd": null,
"openInterest": 4703.57,
"status": "active",
"sourceStatus": "active",
"resolvedOutcome": null,
"createdAt": "2025-07-18T08:05:49.322Z",
"endDate": "2070-01-01T15:00:00.000Z",
"resolutionSource": "The New York Times; the Associated Press; Reuters; Axios; Politico; ...",
"description": "If Luis Antonio Tagle becomes the first person elected Pope before Jan 1, 2070, then the market resolves to Yes.",
"category": "Elections",
"tags": [],
"orderbookFetched": false,
"scrapedAt": "2026-10-11T05:49:05.178Z"
}

A Polymarket record has the same keys: marketId is the Gamma market id, eventId the Gamma event id, slug the URL slug, ticker/seriesTicker are null, each outcome carries its CLOB tokenId, tags holds the event's tag slugs (["bitcoin", "crypto", "weekly"]) and category is null.

Field notes:

  • Prices are 0-1 everywhere. outcomes[].price is the exchange's quoted price: Polymarket's outcomePrices, or on Kalshi the midpoint of the Yes book with 0 / 1 standing in for an empty side (the same convention Polymarket uses). impliedProbabilityPct is price x 100. bestBid/bestAsk are null when that side of the book is empty - the Actor never invents a quote.
  • yesPrice / noPrice are filled only for Yes/No questions. Polymarket matchups with team names as outcomes (["MOUZ", "Spirit"]) keep them null; read outcomes instead. isBinary is true for any two-outcome market.
  • status is normalized: active (tradeable), closed (trading ended, not yet resolved), resolved (settled; resolvedOutcome names the winner and the winning outcome's price is 1), upcoming (listed, not yet open). sourceStatus keeps the exchange's own label (finalized, determined, closed/resolved, ...).
  • Volume. Polymarket reports USDC traded. Kalshi reports contract counts; volumeUsd / volume24hUsd are those counts times the $1 notional per contract (the raw counts stay in volumeContracts / volume24hContracts). Treat Kalshi's figure as notional, not dollars that changed hands.
  • liquidityUsd is Polymarket's resting liquidity figure. Kalshi does not publish one in the listing, so it is null unless Include orderbook is on, in which case it is the dollars resting on both sides of the fetched book (Yes bids pay p, No bids pay 1 - p).
  • openInterest is Kalshi's open contracts; Polymarket publishes open interest only per event, so it is null there.
  • With Include orderbook on, each outcome gets orderbook: { bids, asks, bidDepth, askDepth, restingUsd } with up to 10 levels per side, best first, prices in 0-1 and sizes in shares (Polymarket) or contracts (Kalshi). On Kalshi the asks of Yes are the mirrored No bids (1 - price) and vice versa, which is exactly how the exchange matches orders. orderbookFetched tells you whether the book came back; only those markets are charged the orderbook event.
  • url is the market page. Kalshi pages have the form kalshi.com/markets/{series}/{slug}/{event}; the middle segment is cosmetic and the Actor builds it from the event title.
  • resolutionSource is Polymarket's resolution source text, or Kalshi's settlement sources joined with ; . description holds Polymarket's resolution text or Kalshi's primary rules.

A source that could not be read produces a single free record with status: "unavailable" (or "empty") and a message containing the HTTP status and a body snippet, so your pipeline can tell "no markets" from "exchange down".

Integrations

  • API: start runs and read results with the Apify API or the JavaScript and Python clients. Run the Actor synchronously with run-sync-get-dataset-items to get a fresh odds snapshot in one call.
  • MCP / AI trading agents: the Actor is exposed through the Apify MCP server at https://mcp.apify.com. Add it to Claude Desktop, Claude Code, Cursor, ChatGPT or any MCP-capable trading agent, enable this Actor as a tool, and the agent can call it with the same input - for example {"search": "fed", "status": "active"} before an FOMC decision, or {"seriesTickers": ["KXBTCD"], "includeOrderbook": true} to read the Bitcoin strike ladder with depth. Because prices are already probabilities, the agent can compare them with its own model or with the other exchange without any unit conversion. No custom MCP server is needed; the Actor's input schema becomes the tool schema.
  • n8n and Make: use the Apify node or module, pick this Actor, pass the input JSON above and map the dataset items into your workflow.
  • Schedules and webhooks: run it every few minutes from Apify Schedules and let a webhook push the snapshot to Slack, Telegram, Google Sheets, Postgres or your trading system.

Sources and limitations

Polymarket (polymarket). Read from the public Gamma and CLOB APIs. Verified on 2026-10-11: GET /events?active=true&closed=false&limit=100 returned 100 events with 1,242 nested markets, tag_slug=crypto worked, POST /books returned full books, and limit values above 100 were silently capped at 100. Gamma's /markets endpoint does not carry tags, which is why the Actor lists through /events. Polymarket markets are always two-outcome (multi-choice questions are events with one market per choice), so isBinary is always true there. openInterest is not published per market. The quoted outcomePrices can lag the live book by a few seconds; turn on Include orderbook when you need the exact top of book.

Kalshi (kalshi). Read from the public trade API v2, which serves market data without authentication (trading endpoints need keys and are not used). Verified on 2026-10-11: /events?status=open&with_nested_markets=true&limit=200 returned 200 events with 1,821 markets, series_ticker=KXBTCD returned 318 open strike markets, /markets/{ticker}/orderbook returned orderbook_fp levels, and /events?status=settled returned events without nested markets (hence the /markets?status=settled path). The exchange lists markets in its own order, not by activity, so a small maxMarketsPerSource without filters returns whatever Kalshi lists first; use seriesTickers or search to target what you need. Settled markets fetched from /markets have no event metadata: category is null and eventTitle falls back to the market title. Multivariate parlay legs are excluded. Kalshi's public rate limit is generous for this volume; the Actor still spaces requests 250 ms apart.

Both exchanges publish live data that changes by the second; scrapedAt tells you when the record was taken. Prices on illiquid markets (wide spreads, zero 24-hour volume) are quotes, not fair values - check spread, volume24hUsd and liquidityUsd before acting on them.

Prediction Market Odds Scraper FAQ

How do I compare the same question across both exchanges?

Run with search set to the topic ("fed december", "bitcoin 100k") and both sources on. Prices are 0-1 on both, so the difference between yesPrice values is the raw edge before fees and slippage.

Why is yesPrice null on some Polymarket markets?

Those markets name their outcomes (["Texas", "Oklahoma"]) rather than Yes/No. The prices are in outcomes; yesPrice is only filled when the outcomes are literally Yes and No so it always means the same thing.

Can I get resolved markets with their final prices?

Yes - set status: "closed". Resolved markets carry status: "resolved", resolvedOutcome and a winning price of 1.

How much does a full snapshot cost?

At the default 30 markets per source, a run of both exchanges is 60 markets, i.e. $0.06, plus $0.03 if orderbooks are on. At 200 markets per source it is 400 markets, i.e. $0.40, plus $0.20 with orderbooks.

What happens if an exchange is down?

The run still succeeds with the other source, and a free unavailable record explains what failed (HTTP status and body snippet). Nothing is faked.

Same author, same pay-per-event model and the same no-login approach. They combine well in one pipeline (for example calendar events plus prediction odds plus channel chatter around a release):

  • Telegram Channel Scraper - posts, views, media and links from public Telegram channels, no login.
  • Substack Scraper - posts, authors, likes, restacks and comments from public Substack publications, custom domains included.
  • Economic Calendar Scraper - Forex Factory and Investing.com macro events with UTC times, impact, actual, forecast and previous values.
  • Product Hunt Scraper - daily, weekly and monthly leaderboards, topic pages, search and product launches with votes, makers and website URLs.

This Actor only reads public, unauthenticated API endpoints that both exchanges document for market data, and it never places orders or touches account endpoints. Market prices are public facts, but each exchange's presentation and API are subject to its own terms: see Polymarket's terms of use and Kalshi's terms. You are responsible for using the data in line with those terms and applicable law, including any restrictions on redistribution and on trading from your jurisdiction. Keep request rates low (the Actor already serializes requests with a delay) and read Apify's article on the legality of web scraping if you are unsure. Nothing here is investment advice.

Support

Found a market that normalizes wrongly, a missing field, or need another exchange (Manifold, PredictIt, Metaculus)? Open an issue on the Actor's Issues tab in Apify Console or contact the author through the Actor page. Reports that include the input JSON and the market id or ticker are usually fixed within days.