# Prediction markets - Kalshi + Polymarket prices (public APIs) (`retrainmap/prediction-markets`) Actor

Current prices, volume and liquidity for open Kalshi and Polymarket markets, normalised to one row per market (yes/no price 0-1, event, category, close date, URL). Filter by venue, status, text, category or Kalshi series. Public read-only APIs; market data only, no trading.

- **URL**: https://apify.com/retrainmap/prediction-markets.md
- **Developed by:** [RetrainMap Team](https://apify.com/retrainmap) (community)
- **Categories:**
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 records

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?

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.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — 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

## Prediction markets — Kalshi + Polymarket prices (public APIs)

One normalised row per market from the public, read-only listings of **Kalshi** and
**Polymarket**: event, market question, category, the current yes/no price (0–1 USD per
share, i.e. the market-implied probability), bid/ask, last trade, volume, liquidity, open
interest, open/close dates, status and a link. Filter by venue, status, free text, category
or Kalshi series ticker.

**Market data only, for information.** The Actor holds no account at either venue, sends no
order, and nothing here is advice or an invitation to wager. Availability of the venues
themselves depends on where you are; reuse of the data is governed by the
[Kalshi](https://kalshi.com/terms) and [Polymarket](https://polymarket.com/tos) terms of
service — check them before redistributing.

### Data source and status

| | |
|---|---|
| Kalshi | `https://api.elections.kalshi.com/trade-api/v2/events?with_nested_markets=true` (status / series filters, cursor paging, 100 events per request). Unauthenticated read access, as documented by Kalshi. The old host `trading-api.kalshi.com` answers 401 "API has been moved" — this Actor uses the current one |
| Polymarket | `https://gamma-api.polymarket.com/events` (active/closed filters, sorted by 24-hour volume, 25 events per request with nested markets and tags) and `/public-search?q=` when a search text is given. Public Gamma API, no key |
| Refresh cadence | Live on every run — prices are read at fetch time (`fetched_at` on each row). Kalshi and Polymarket quotes move continuously; a row is a snapshot, not a feed |
| Response guards | Every field a row depends on must be present with the type measured on 2026-09-04 (Kalshi `*_dollars` price strings, `*_fp` quantities; Polymarket `outcomes` / `outcomePrices` JSON strings). Anything else fails the run loudly instead of writing guessed values |
| Rate | At most one request per `request_interval_ms` (default 500 ms); back-off on 429/5xx |
| Identification | `User-Agent: RetrainMap-prediction-markets/0.1 (https://apify.com/retrainmap; <contact>)` plus the contact address in the standard `From:` header |

Honesty note: Kalshi's API does not return the web-page slug of a market, so the Kalshi `url`
is the API resource for that market (`/trade-api/v2/markets/<ticker>`), not a kalshi.com page;
use `series` / `event_id` / `market_id` to find it on the site. Polymarket rows link to the
market page (`polymarket.com/event/<event>/<market>`).

### Input

| Field | Type | Meaning |
|---|---|---|
| `source` | `both` / `kalshi` / `polymarket` | Default `both` (Kalshi gets the first half of the row budget, Polymarket the rest) |
| `status` | `open` / `closed` / `settled` / `all` | Default `open` = trading now. Polymarket has one `closed` flag for closed and settled |
| `search` | string | Optional. Kalshi: substring match on event/market titles while paging; Polymarket: the venue's own search |
| `category` | string | Optional substring. Kalshi event category (Economics, Politics, Sports, …); Polymarket tag label/slug or series |
| `series_ticker` | string | Optional, Kalshi only (e.g. `KXFED`). When set, Polymarket is skipped |
| `max_records` | integer | Default 300 |
| `request_interval_ms` | integer | Default 500 (floor 250) |
| `contact_email` | string | Sent in the User-Agent and `From:` header |

Example — Kalshi Fed-rate markets only:

```json
{ "source": "kalshi", "series_ticker": "KXFED", "max_records": 50 }
```

### Output (dataset row)

`source`, `market_id` (Kalshi ticker / Polymarket market id), `event_id`, `series`,
`event_title`, `market_title`, `market_subtitle`, `category`, `tags` (Polymarket),
`market_type`, `outcomes`, `outcome_prices`, `yes_price`, `no_price` (0–1), `price_basis`
(`last_trade`, `midpoint` or `outcome_prices`), `yes_bid`, `yes_ask`, `no_bid`, `no_ask`,
`last_price`, `volume`, `volume_24h`, `liquidity`, `open_interest` (Kalshi), `open_time`,
`close_time`, `expiration_time`, `status` (the venue's own value), `result` (Kalshi, once
settled), `url`, `fetched_at`.

Kalshi `yes_price` is the last trade price when the market has traded, otherwise the yes
bid/ask midpoint when both sides are quoted, otherwise null; `no_price` is `1 − yes_price`.
Polymarket `yes_price`/`no_price` are the venue's `outcomePrices` for the Yes/No outcomes; for
markets whose outcomes are not Yes/No the first outcome's price is reported as `yes_price`
and the full `outcomes`/`outcome_prices` lists are on the row. Polymarket `yes_bid`/`yes_ask`
are the venue's best bid/ask for the first outcome; markets without prices (not yet deployed)
are skipped and counted in the run summary. A run summary (counts per venue, filters, request
count) is stored as `RUN_SUMMARY` in the run's key-value store.

### Pricing (pay per event)

| Event | Price |
|---|---|
| `run-start` — once per run | $0.10 |
| `record` — per row written | $0.005 |

The default run (300 rows) costs $1.60. Rows stop when your run's maximum charge is reached;
the run summary says so.

### Operator

Steelyard Ventures LLC (RetrainMap) — info@steelyardclinical.com. The Actor writes only to its
own dataset and key-value store; it stores no credentials and sends nothing but GET requests.

# Actor input Schema

## `source` (type: `string`):

Which venue(s) to read. With `both`, the row budget is split: Kalshi gets the first half, Polymarket the rest (and whatever Kalshi did not use).

## `status` (type: `string`):

`open` = trading now (Kalshi status=open and market status active; Polymarket active and not closed). `closed` / `settled` use the venues' own filters (Polymarket has a single closed flag for both). `all` = no status filter.

## `search` (type: `string`):

Optional. Kalshi: case-insensitive substring match on the event title, market title and subtitle (pages of events are scanned, up to 60 pages). Polymarket: passed to the venue's own search endpoint (/public-search).

## `category` (type: `string`):

Optional, case-insensitive substring. Kalshi: the event category (Economics, Politics, Sports, Entertainment, Elections, Climate and Weather, Science and Technology, Financials, Companies, Health, World). Polymarket: any tag label/slug or series (e.g. politics, crypto, fed-rates).

## `series_ticker` (type: `string`):

Optional, Kalshi only (e.g. KXFED, KXHIGHNY). Applied server-side; when set, Polymarket is skipped.

## `max_records` (type: `integer`):

Stop after this many market rows have been written.

## `request_interval_ms` (type: `integer`):

Politeness delay towards the venues' APIs (floor 250 ms). A default run needs about two requests.

## `contact_email` (type: `string`):

Sent in the User-Agent and in the standard From: request header so the venues can reach the operator (RFC 9110 s10.1.2).

## Actor input object example

```json
{
  "source": "both",
  "status": "open",
  "max_records": 300,
  "request_interval_ms": 500,
  "contact_email": "info@steelyardclinical.com"
}
```

# Actor output Schema

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

One row per market: event, market, category, yes/no price (0-1), bid/ask, last trade, volume, liquidity, open interest, dates, status, URL, fetch time.

## `results_csv` (type: `string`):

The same rows as a CSV file.

# 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 = {
    "source": "both",
    "status": "open",
    "max_records": 300
};

// Run the Actor and wait for it to finish
const run = await client.actor("retrainmap/prediction-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 = {
    "source": "both",
    "status": "open",
    "max_records": 300,
}

# Run the Actor and wait for it to finish
run = client.actor("retrainmap/prediction-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 '{
  "source": "both",
  "status": "open",
  "max_records": 300
}' |
apify call retrainmap/prediction-markets --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,retrainmap/prediction-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/YA1GZh6zTDHExhhz9/builds/PYLrsaPwELMicKqsx/openapi.json
