# Kalshi Sports Markets Scraper (`utopicvision/kalshi-sports-markets-scraper`) Actor

Kalshi Premier League and sports prediction markets as clean structured rows, with parsed teams, competitions, bet types, kickoff times and settled outcomes.

- **URL**: https://apify.com/utopicvision/kalshi-sports-markets-scraper.md
- **Developed by:** [George Semaan](https://apify.com/utopicvision) (community)
- **Categories:**
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 market rows

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

## Kalshi Sports Markets Scraper: Premier League, Soccer, NFL, NBA Odds & Prediction Markets

**$2.00 per 1,000 rows. No API key. No proxies. A default run finishes in seconds.**

Pulls Kalshi's sports prediction markets and returns them as flat,
spreadsheet-ready rows with the fixture, the subject team, the competition, the
bet type and the kickoff time already parsed into their own columns.

Defaults to the English Premier League. Point it at any of Kalshi's 3,600+ sports
series and it works the same way: soccer, NFL, NBA, tennis, golf, esports,
cricket, MMA.

***

### What you get

One flat row per market. No nested JSON, no reading rules text to work out which
club a ticker belongs to.

| ticker | home\_team | away\_team | team | market\_kind | proposition | game\_date | yes\_bid | yes\_ask | volume |
|---|---|---|---|---|---|---|---|---|---|
| KXEPLGAME-26AUG29LFCNFO-LFC | Liverpool | Nottingham | Liverpool | game\_winner | Liverpool | 2026-08-29 | 0.68 | 0.69 | 980960.72 |
| KXEPLSPREAD-26AUG29LFCNFO-LFC3 | Liverpool | Nottingham Forest | Liverpool | spread | Liverpool wins by more than 2.5 goals | 2026-08-29 | 0.22 | 0.23 | 15540.31 |
| KXEPLTOTAL-26AUG29LFCNFO-1 | Liverpool | Nottingham Forest | | total | Over 0.5 goals scored | 2026-08-29 | 0.95 | 0.96 | 11047.72 |

Season-long markets have no fixture, so they come back keyed on the club:

| ticker | team | market\_kind | season | proposition | yes\_bid | yes\_ask |
|---|---|---|---|---|---|---|
| KXEPLRELEGATION-27-ARS | Arsenal | relegation | 2026-27 | Arsenal | 0.03 | 0.06 |

***

### Coverage

Measured on a live default run:

- **208 open markets** across the core EPL series, in **11 requests** and about
  five seconds.
- **100%** of rows resolve a fixture, a subject team or a competition.
- Bet types on that run: game winner 78, totals 60, spreads 40, relegation 20,
  both teams to score 10.

The default covers the five core Premier League series. The full Sports catalogue
is **3,613 series**, reachable by naming them in `seriesFilter`: every EPL market
type (corners, correct score, goalscorer, assists, half-time lines, top four,
champion), plus 1,403 soccer series overall, 537 American football, 527
basketball, 214 baseball, 135 tennis, 114 golf, 107 esports, 70 hockey, 64
motorsport, 56 cricket, 38 MMA.

***

### Join on `team_id`, not on the club name

Kalshi does not spell a club the same way in every series. In a single live run
the same three clubs appeared as:

| One series says | Another says |
|---|---|
| Nottingham | Nottingham Forest |
| Newcastle | Newcastle United |
| Coventry | Coventry City |

Every row carries **`team_id`**, Kalshi's own identifier for the club, which is
stable across series and seasons. Join on that and the variants stop mattering.

There is deliberately **no** derived `team_canonical` column. Rows are written a
series at a time so that an interrupted run keeps its work, which means any
"canonical" name learned mid-run is only whatever has been seen so far. Testing
showed the same club emitted under two different canonical names inside one run.
A join key that is not stable is worse than no join key, so this actor publishes
the identifier Kalshi already guarantees instead of inventing one.

***

### Input

Every field is optional.

| Field | Default | What it does |
|---|---|---|
| `mode` | `live` | `live` for open markets and prices, `settled` for outcomes, `both`. |
| `seriesFilter` | the five core EPL series | Which series to fetch. Clear it entirely to fetch every sports series with open markets, which is slow: the catalogue is over 3,600 series. |
| `includeOrderbook` | `false` | Add resting bid depth, a derived yes-ask and the spread. Batched, so it costs very few extra requests. |
| `includeRecentTrades` | `false` | Add recent executed trades. Costs one request per market. |
| `historyDays` | `7` | Settlement lookback window. |
| `incremental` | `false` | Remember the newest settlement seen and fetch only newer ones next run. |
| `maxResults` | `5000` | Safety cap on rows per run. |
| `requestsPerSecond` | `2` | Politeness limit. |

#### Filters

Ask for the slice you want instead of downloading everything and filtering
locally. Filters run **before rows are written**, so a narrower request returns
fewer rows and costs less.

| Field | What it does |
|---|---|
| `teams` | Keep only markets involving these clubs, e.g. `["Arsenal", "Liverpool"]`. Matches the home side, the away side **and** the subject team, so a club's away fixtures are included. Case and punctuation insensitive. |
| `competitions` | Keep only these competitions, e.g. `["EPL"]`. Read from Kalshi's event metadata, not parsed from text. |
| `sports` | Keep only these sports, e.g. `["Soccer"]`. |
| `marketKinds` | Keep only these bet types, e.g. `["game_winner", "spread"]`. |
| `seasonMarketsOnly` | `season` for relegation, champion and top-four style markets; `fixture` for single matches. |
| `minStrike` / `maxStrike` | Strike window in the market's own unit: goals for a total, goal margin for a spread. A market is kept when **any part of its range** overlaps the window, so open-ended lines like "over 2.5 goals" are not silently dropped. |
| `dateFrom` / `dateTo` | Kickoff-date window, `YYYY-MM-DD`. |
| `minVolume` | Drop markets that have traded less than this. The most effective single lever for cutting a run down to markets anyone is actually trading. |
| `minOpenInterest` | Drop markets with fewer open contracts than this. |
| `minProbability` / `maxProbability` | Implied-probability window, `0` to `1`. `0.4` to `0.6` finds near coin-flips; `maxProbability: 0.05` finds longshots. |
| `results` | Settled modes only: keep only `yes`, `no` or `scalar` outcomes. |

```jsonc
// Every Arsenal market anyone is actually trading
{ "mode": "live", "teams": ["Arsenal"], "minVolume": 100 }

// This weekend's match winners only
{ "mode": "live", "marketKinds": ["game_winner"], "dateFrom": "2026-08-29", "dateTo": "2026-08-31" }

// Season-long markets: relegation, champion, top four
{ "mode": "live", "seasonMarketsOnly": "season" }
```

***

### Output fields

**Identity**: `ticker`, `event_ticker`, `series_ticker`, `market_title`,
`event_title`, `series_title`, `category`, `status`, `market_type`.

**Fixture**: `home_team`, `away_team`, `home_abbr`, `away_abbr`,
`team_order_source`. The order is Kalshi's own listing order; nothing in the
payload states which side is at home, and `team_order_source` records that.

**Subject**: `team`, `team_side`, `team_source`, `team_id`, `proposition`.
`proposition` is Kalshi's own wording for the claim, passed through untouched.

**Classification**: `sport`, `competition`, `competition_scope`, `market_kind`,
`market_kind_source`, `is_season_market`, `frequency`, `tags`.

**Timing**: `kickoff_time`, `game_date`, `season`, `open_time`, `close_time`,
`expiration_time`.

**Strike**: `strike_type`, `floor_strike`, `cap_strike`, `floor_inclusive`,
`cap_inclusive`, `bracket_label`.

**Prices**: `yes_bid`, `yes_ask`, `no_bid`, `no_ask`, `last_price`,
`previous_price`, `implied_probability_yes`, `yes_bid_size`, `yes_ask_size`.

**Size**: `volume`, `volume_24h`, `open_interest`, `liquidity`, `notional_value`.

**Settlement**: `result`, `settlement_value`, `settlement_ts`.

**Raw**: `rules_primary`, `rules_secondary`, passed through untouched.

Every row carries `schema_version`. Provenance columns (`team_source`,
`market_kind_source`, `team_order_source`) let you filter to values read directly
from the payload rather than derived.

***

### Notes on accuracy

- Prices are decimal dollars from 0 to 1, not integer cents. Volumes and sizes
  can be fractional.
- `market_kind` comes from Kalshi's own `competition_scope` where available. A
  bet type this actor has not seen before is emitted as a slug of its own name
  rather than as a blank, so new Kalshi market types are filterable immediately.
- `game_date` comes from `occurrence_datetime`, the scheduled kickoff, not from
  the market's close time.
- `result` can be `yes`, `no` or `scalar`. `scalar` means a voided or partial
  settlement.
- Settled markets can later be `disputed` or `amended`. `updated_time` is
  included so you can upsert rather than append.

***

### Compliance

This is an **unofficial** tool and is not affiliated with, endorsed by, or
connected to Kalshi. It reads Kalshi's public, unauthenticated market data
endpoints, the same ones documented with copy-paste examples at
`docs.kalshi.com`, at a deliberately conservative 2 requests per second. No
account, API key or login is used.

***

### Keywords

kalshi scraper, kalshi api, kalshi sports, premier league odds api, EPL odds
scraper, english premier league data, soccer prediction markets, football betting
odds api, prediction market data, sports betting odds scraper, kalshi nfl, kalshi
nba, implied probability api, match winner odds, over under goals data, both
teams to score data, relegation odds, sports market data feed

# Actor input Schema

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

Open markets with live prices, settled markets with their final outcome and the observed temperature, or both.

## `seriesFilter` (type: `array`):

Which Kalshi series to fetch, e.g. KXEPLGAME, KXEPLSPREAD, KXEPLTOTAL. Defaults to the core English Premier League markets so a default run is fast and cheap. Clear this field entirely to fetch every sports series with open markets, which takes a long time: the Sports catalogue has over 3,600 series. Series are discovered at runtime, so new ones appear without an update.

## `teams` (type: `array`):

Keep only markets involving these clubs, e.g. Arsenal, Liverpool. Matches the home side, the away side and the market’s subject team, so a club’s away fixtures are included. Matching ignores case and punctuation.

## `competitions` (type: `array`):

Keep only these competitions, e.g. EPL, UCL. Read from Kalshi’s own event metadata rather than parsed from text.

## `sports` (type: `array`):

Keep only these sports, e.g. Soccer, Basketball, Tennis. Taken from the series tags.

## `marketKinds` (type: `array`):

Keep only these kinds of market, e.g. game\_winner, spread, total, both\_teams\_to\_score, relegation, correct\_score, goalscorer. Unknown Kalshi bet types appear as a slug of their own name rather than as a blank.

## `seasonMarketsOnly` (type: `string`):

Season-long markets (relegation, champion, top four) and single fixtures are different products. Restrict to one or the other.

## `minStrike` (type: `number`):

Lowest strike to keep, in the market's own unit: goals for a totals market, goal margin for a spread. A market counts if any part of its range falls inside the window, so open-ended markets like 'over 2.5 goals' are kept.

## `maxStrike` (type: `number`):

Highest strike to keep, in the market's own unit.

## `dateFrom` (type: `string`):

Keep only markets played on or after this day, as YYYY-MM-DD. This is the kickoff date, not the market close date.

## `dateTo` (type: `string`):

Keep only markets played on or before this day, as YYYY-MM-DD.

## `minVolume` (type: `number`):

Drop markets that have traded less than this. The single most effective way to cut a run down to markets anyone is actually trading.

## `minOpenInterest` (type: `number`):

Drop markets with fewer open contracts than this.

## `minProbability` (type: `number`):

Keep markets priced at or above this implied probability, from 0 to 1. Use 0.4 with a maximum of 0.6 for near coin-flips.

## `maxProbability` (type: `number`):

Keep markets priced at or below this implied probability, from 0 to 1. Use 0.05 to find longshots.

## `results` (type: `array`):

For settled modes only: keep only markets that settled this way.

## `includeOrderbook` (type: `boolean`):

Adds resting bid levels on both sides, plus a derived yes-ask and spread. Batched, so it costs very few extra requests.

## `includeRecentTrades` (type: `boolean`):

Adds recent executed trades per market. This costs one request per market and makes large runs much slower.

## `maxTradesPerMarket` (type: `integer`):

How many recent trades to attach when trades are enabled.

## `historyDays` (type: `integer`):

How far back to pull settled markets. Only applies to the settled modes.

## `includeHistoricalArchive` (type: `boolean`):

Kalshi keeps only about two months of settled markets on the live endpoint. Turn this on to also walk the archive, which reaches back years. Returns a lot of rows.

## `incremental` (type: `boolean`):

Remember the newest settlement seen and fetch only newer ones next time. Ideal for a scheduled run.

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

Safety cap on how many rows one run produces.

## `requestsPerSecond` (type: `integer`):

Politeness limit. The default is deliberately conservative; raising it risks the exchange throttling the run.

## Actor input object example

```json
{
  "mode": "live",
  "seriesFilter": [
    "KXEPLGAME",
    "KXEPLSPREAD",
    "KXEPLTOTAL",
    "KXEPLBTTS",
    "KXEPLRELEGATION"
  ],
  "teams": [
    "Liverpool",
    "Arsenal"
  ],
  "competitions": [],
  "sports": [],
  "marketKinds": [],
  "results": [],
  "includeOrderbook": false,
  "includeRecentTrades": false,
  "maxTradesPerMarket": 20,
  "historyDays": 7,
  "includeHistoricalArchive": false,
  "incremental": false,
  "maxResults": 5000,
  "requestsPerSecond": 2
}
```

# Actor output Schema

## `markets` (type: `string`):

Every market matched by the run, with the fixture, subject team, competition, bet type, kickoff time, prices and settled outcome parsed into their own columns.

# 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 = {
    "seriesFilter": [
        "KXEPLGAME",
        "KXEPLSPREAD",
        "KXEPLTOTAL",
        "KXEPLBTTS",
        "KXEPLRELEGATION"
    ],
    "teams": [
        "Liverpool",
        "Arsenal"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("utopicvision/kalshi-sports-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 = {
    "seriesFilter": [
        "KXEPLGAME",
        "KXEPLSPREAD",
        "KXEPLTOTAL",
        "KXEPLBTTS",
        "KXEPLRELEGATION",
    ],
    "teams": [
        "Liverpool",
        "Arsenal",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("utopicvision/kalshi-sports-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 '{
  "seriesFilter": [
    "KXEPLGAME",
    "KXEPLSPREAD",
    "KXEPLTOTAL",
    "KXEPLBTTS",
    "KXEPLRELEGATION"
  ],
  "teams": [
    "Liverpool",
    "Arsenal"
  ]
}' |
apify call utopicvision/kalshi-sports-markets-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,utopicvision/kalshi-sports-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/EQ4LA0mpt6eofBsbG/builds/z5Axq34b8LrJc4mZO/openapi.json
