Kalshi Sports Betting Odds – NFL, NBA, MLB & NHL Lines
Pricing
from $1.00 / 1,000 games
Kalshi Sports Betting Odds – NFL, NBA, MLB & NHL Lines
One row per game from Kalshi, the CFTC-regulated exchange: both teams' prices, vig-free probabilities, overround, volume, spreads, totals and price history. NFL, NBA, MLB, NHL, EPL, UCL and more.
Pricing
from $1.00 / 1,000 games
Rating
0.0
(0)
Developer
Rowfeed
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
13 hours ago
Last modified
Categories
Share
Kalshi Sports Odds Scraper – Game Lines, Spreads & Totals
Get live Kalshi sports odds as one clean JSON row per game: moneyline prices for both teams, joined spread and total lines, and vig-removed fair probabilities. Built for odds bots, sports dashboards and AI agents that need to ask "what are the Kalshi odds for this game right now" without a login, an API key or a headless browser. Kalshi is the only CFTC-regulated, licensed exchange for sports event contracts in the US, so its prices come from real money changing hands, not a bookmaker's model. Plain HTTPS calls with retries and a silent-failure check, so a scheduled run keeps working when the exchange hiccups.
What you get
- One row per game – both teams' YES price, bid, ask, mid, implied probability, volume and open interest, plus a 3-way
drawleg whenever Kalshi lists a Draw/Tie market (soccer, including the "Reg Time: Tie" variant;nullfor two-way sports). - Spreads and totals joined in – every point-spread and total-points/goals market for the game attached as
spreads/totalslists, matched to the moneyline by Kalshi's own event code. - Vig removed for you –
overround(the book's built-in edge) andfair_probability_a/_b/_draw(each side's mid price re-normalised to sum to 1) computed for every game still trading, plusfavorite. - Finished games show the result – with
status: "settled"each side carries Kalshi's ownresultand payout instead of a stale price (see "Settled and closed games" below). - Covers 17 leagues – NFL, NBA, MLB, NHL, EPL, NCAAF, NCAAB, UCL, La Liga, Serie A, Bundesliga, Ligue 1, MLS, WNBA, ATP and WTA tennis, and UFC fights (one row per fight, the two fighters in
team_a/team_b), or poll one game directly by its event ticker.
Sample row
One open NFL game, maxGames: 1. Trimmed here: spreads/totals to one entry each (a real run returns every joined market), and team_b to its prices (real rows give it the same fields as team_a).
{"event_ticker": "KXNFLGAME-26SEP24ATLGB","league": "NFL","series_ticker": "KXNFLGAME","title": "Atlanta vs Green Bay","sub_title": "ATL vs GB (Sep 24)","expires_at": "2026-09-25T06:15:00Z","close_time": "2026-09-27T00:15:00Z","status": "active","team_a": {"name": "Green Bay","ticker": "KXNFLGAME-26SEP24ATLGB-GB","last_price": 0.71,"yes_bid": 0.70,"yes_ask": 0.71,"mid": 0.705,"implied_probability": 0.705,"volume": 864084.8,"volume_24h": 448478.57,"open_interest": 687846.03},"team_b": {"name": "Atlanta","ticker": "KXNFLGAME-26SEP24ATLGB-ATL","last_price": 0.30,"yes_bid": 0.29,"yes_ask": 0.30,"mid": 0.295,"implied_probability": 0.295},"draw": null,"overround": 0.01,"fair_probability_a": 0.705,"fair_probability_b": 0.295,"favorite": "Green Bay","spreads": [{ "ticker": "KXNFLSPREAD-26SEP24ATLGB-GB28", "yes_sub_title": "GB Packers wins by over 27.5 points", "floor_strike": 27.5, "yes_bid": 0.04, "yes_ask": 0.05 }],"totals": [{ "ticker": "KXNFLTOTAL-26SEP24ATLGB-26", "yes_sub_title": "Over 25.5 points scored", "floor_strike": 25.5, "yes_bid": 0.93, "yes_ask": 0.94 }],"url": "https://kalshi.com/markets/kxnflgame","scraped_at": "2026-09-23T16:39:50+00:00"}
Every row also carries home_team/away_team (always null - Kalshi doesn't label a home side, and this Actor never guesses), start_time (always null for the same honesty reason - use expires_at/close_time instead), and raw_event with Kalshi's own event fields.
Filters
| Input | Default | What it does |
|---|---|---|
leagues | ["NFL","NBA","MLB","NHL","EPL","NCAAF","UCL"] | Leagues to scrape. Also: NCAAB, UCL, LALIGA, SERIEA, BUNDESLIGA, LIGUE1, MLS, WNBA, ATP, WTA, UFC. |
seriesTickers | [] | Extra game series by ticker (advanced), e.g. ["KXWNBAGAME"], for leagues not in the list above. |
eventTickers | [] | Poll specific games directly, e.g. ["KXNFLGAME-26SEP24ATLGB"]. An unknown ticker becomes an uncharged error row and never fails the run: it is your input, not an outage. |
team | "" | Case-insensitive substring match against the title and both team names, e.g. "Green Bay" or "GB". |
status | open | open, closed, settled or all. What settled and closed rows contain is described below. |
includeSpreadsTotals | true | Join spread/total markets onto each row. |
includeHistory | false | Add 7 days of hourly candlesticks for both moneyline markets. |
maxGames | 100 | Cap on rows (1-1000), soonest-expiring first; settled and closed games most recent first. |
Leagues or games with nothing matching the status filter simply contribute zero rows - not an error. A default run (no filters) returns whichever of the 7 default leagues currently have games in that status.
Tennis and UFC have moneyline markets only, so spreads and totals are empty lists there. A newly listed fight or match that nobody has traded yet can show placeholder quotes (for example bid 0.05 / ask 0.83 on both fighters of a Contender Series bout, with zero volume); check team_a.volume and team_b.volume before treating those prices as a market view.
Settled and closed games
status: "settled" returns finished games. When Kalshi settles a market it empties the order book, so every leg reads yes_bid 0 and yes_ask 1 and there is no price left. On those rows (status is finalized) the fields mid, implied_probability, fair_probability_a/_b/_draw, overround and favorite are null, never a made-up 50/50. Instead, each leg and each joined spread or total market carries Kalshi's own outcome:
result:"yes"for the side that won,"no"for the others, and"scalar"when Kalshi paid out a fraction (an NFL tie pays half on both teams).settlement_value: what one YES contract paid out, in dollars:1.0,0.0, or the fraction (0.5for a tie).
last_price is kept as Kalshi reports it. On a settled leg it is the final trade (usually 0.99 or 0.01), not a pre-game price. Here is the sample game above after it finished, trimmed to the fields that change:
{"event_ticker": "KXNFLGAME-26SEP24ATLGB","status": "finalized","team_a": { "name": "Green Bay", "last_price": 0.01, "yes_bid": 0.0, "yes_ask": 1.0, "mid": null, "implied_probability": null, "result": "no", "settlement_value": 0.0 },"team_b": { "name": "Atlanta", "last_price": 0.99, "yes_bid": 0.0, "yes_ask": 1.0, "mid": null, "implied_probability": null, "result": "yes", "settlement_value": 1.0 },"overround": null,"fair_probability_a": null,"fair_probability_b": null,"favorite": null}
Settled rows come most recent first, so maxGames keeps the latest results. Kalshi only serves market data for recently settled games. On 25 September 2026 the cut-off was about 18 July for every league, so how far back you get depends on each league's calendar. MLB, tennis and UFC went back to mid-July. NFL and the big European soccer leagues only went back to their first games in August, NHL to its first preseason games in mid-September, and NBA returned nothing because its season ended in June. Older games have moved to Kalshi's archive and come back without markets, so the Actor skips them and counts them as skipped_incomplete in the run's STATS record. Use status: "settled" for recent results, not for season-long history.
status: "closed" returns games where trading has ended but Kalshi has not settled them yet. That window usually lasts minutes, so the list is often empty. Kalshi shows those markets with an empty book too, so the price fields are null, and result appears only once Kalshi has decided the outcome. Open games never carry result or settlement_value.
Pricing
Pay per event, no subscription: $1 per 1,000 games, $2 per 1,000 history fetches and $0.001 per run start (kept tiny so you can poll one game at a time). Fetching spreads and totals is free - they ride along on the game row. Set a maximum charge on the run and the Actor stops cleanly when it is reached, charging only for rows actually saved.
Details
- Source: Kalshi's public trade API v2 (
api.elections.kalshi.com/trade-api/v2). No authentication, no proxies, no browser, no personal data. Not affiliated with Kalshi. - Prices are probabilities: Kalshi is a real-money exchange, so every YES price is a dollar amount between $0 and $1 that a trader is willing to pay for a $1 payout if that outcome happens - which makes it, by construction, the market's implied probability of that outcome.
- Overround and fair probability:
overroundsums every leg's ask price and subtracts 1, so it's the total edge baked into the current asks (a healthy two-way market runs a few cents over 0).fair_probability_*divides each leg's mid price by the sum of all legs' mids, so the vig cancels out and the fair probabilities always sum to 1. A settled or closed game has no quote, so both arenullthere. - Reliability: 429 and 5xx responses are retried with exponential backoff (5 tries), a 200 without the expected data counts as a failure, and one bad ticker never stops the run - it becomes an error row (
event_ticker,error,errorMessage) and the rest continues. A run fails only when it produced no rows and the source failed (network, rate limit, blocked); an unknown ticker or a league with nothing open is a successful run with no charged rows. - Run stats: the
STATSrecord in the run's key-value store holds games, history fetches, error rows, skipped incomplete events, request and error counts per category. - Output: one dataset row per game with the columns above, sorted soonest-expiring first (settled and closed games most recent first). Export as JSON, CSV or Excel, fetch through the Apify API, or schedule runs and pipe them into Google Sheets, Make, Zapier, n8n or your own code. Eligible for agentic use via Apify's MCP server.