Polymarket vs Kalshi Arbitrage — Same Question, Two Prices avatar

Polymarket vs Kalshi Arbitrage — Same Question, Two Prices

Pricing

from $20.00 / 1,000 contract pair priced across venues

Go to Apify Store
Polymarket vs Kalshi Arbitrage — Same Question, Two Prices

Polymarket vs Kalshi Arbitrage — Same Question, Two Prices

Compare the same question on Polymarket and Kalshi: bid/ask, gross gap and net gap after both venues' fees. Unknown Polymarket fees leave net null with a reason. $0.02 per gross-priced pair, suggestions included; refusals free. Unofficial.

Pricing

from $20.00 / 1,000 contract pair priced across venues

Rating

0.0

(0)

Developer

h ichi

h ichi

Maintained by Community

Actor stats

0

Bookmarked

3

Total users

2

Monthly active users

4 days ago

Last modified

Share

What it does: Takes a question that trades on both Polymarket and Kalshi and prices the gap between them, before fees and after Kalshi's fee plus the market-specific Polymarket taker fee. If Polymarket's fee settings are unavailable or unsupported, it returns the gross gap with a null net gap and an explicit reason.

You enter: Pairs you checked, e.g. will-bitcoin-outperform-gold-in-2026|KXBTCVSGOLD-26 (Polymarket|Kalshi).

You get: One row per pair: best bid and ask on each site, which site to buy on and sell on, gap in cents per $1 before and after the fee, dollars resting at those prices, close-date gap. Pairs it suggests itself (on by default) are marked unverified, indicative only.

Price: $0.02 per pair priced, the pairs it suggests included. +$0.002 per site row if you also want each side (two per pair, on by default). Pairs that can't be priced are never charged.

Unofficial — not affiliated with Polymarket or Kalshi. Reads public pages only.

Confirmed pairs — the only tradable number

You list the pairs you have checked, as "<polymarket-slug-or-conditionId>|<KALSHI-TICKER>". Only these get an edgePct.

fieldmeaning
matchVerifiedtrue — you confirmed this pairing
directione.g. "buy polymarket / sell kalshi" — which leg you lift
edgePct(bid on the dearer venue - ask on the cheaper) x 100, cents per $1 contract
edgeNetPctthe same, less both venues' fees at the executed prices; null when Polymarket's fee is unknown
kalshiFeeCents / polymarketFeeCentsper contract; see Fees below
netPricedwhether the net gap can be calculated from supported fee settings
polymarketFeeStatus / polymarketFeeReasonenabled, disabled, unknown or unsupported, with the source validation result
directionBasisnet-edge when fees are known; gross-edge-fees-unknown when the direction is only a gross comparison
polymarketDepthUsd / kalshiDepthUsddollars resting at the touch, bid and ask
closeGapDayshow far apart the two contracts settle
directionsBlockedthe round trip that was not taken, and why

Suggested pairs — never a trade, always a question

With autoSuggest on, the matcher proposes pairings it found. These carry matchVerified: false, edgePct: null, and their number under the deliberately different name indicativeEdgePct — alongside matchScore, matchBasis (what actually matched), closeGapDays, and both resolution texts verbatim (polymarketResolution, kalshiResolution) so you can confirm or reject in one glance. confirmAs gives you the exact string to paste into pairs.

Reading them matters. In a live run on 2026-08-10 the matcher proposed OpenAI IPO before 2027? against Kalshi's Who will IPO before 2027? OpenAI — same company, same year, an 8-point apparent edge. The two resolution texts do not agree: Polymarket needs the IPO completed by 31 Dec 2026, Kalshi needs it confirmed before 1 Jan 2027. Announcing is easier than completing, so Kalshi trades higher for a reason. That gap is a definition, not an arbitrage — and it is visible only because both texts travel with the suggestion.

What the matcher refuses to do

Every gate below is a hard drop, not a score penalty, because a fact that is simply wrong cannot be traded off against wording similarity. Each one's losses are counted in the summary.

gatewhy
close times more than maxCloseGapDays apartcontracts that settle weeks apart are not the same contract
token overlap below minMatchScore
numeric thresholds present on both sides but disagreeing"BTC above $150,000" and "BTC above $120,000" are 0.75 similar as text and are opposite bets
dates present on both sides but disagreeing
opposite direction words (above / below)the one pairing that would flip the sign of the edge
a qualifier on one side only (second, exactly, not, consecutive)"second-hottest year" vs "hottest year" is one word and the opposite bet
the shared words are more common than the differing onessee below

That last gate came out of a live run. At minMatchScore 0.6 the matcher paired "Remote IPO before 2027?" with "Who will IPO before 2027? Kraken" — two different companies whose entire overlap is the template ipo / before / 2027. A candidate is now dropped unless the rarest word the two questions share is at least as rare as the rarest word they differ on.

What it counts

A filtered set that hides its own losses reads as complete, so the spread_summary record reports every drop: droppedCloseGap, droppedPolarity, droppedAsymmetricQualifier, droppedNumericThresholdDisagreement, droppedDateDisagreement, droppedNoSharedSubjectWord, droppedBelowMatchScore, duplicateKalshiSuggestionsDropped, duplicatePolymarketSuggestionsDropped, unpriceableLegs, confirmedPairsUnresolvable. Verified and unverified edge statistics are kept in separate fields and never averaged together. Net statistics and best-net records exclude null net gaps. confirmedPairsNetPriced / suggestionsNetPriced count known net gaps; confirmedPairsFeeUnknown / suggestionsFeeUnknown count priced gross gaps with unavailable or unsupported fees.

Depth, and what is refused

minLiquidityUsd applies to the size resting at the touch on both legs — the Polymarket side from the CLOB order book, the Kalshi side from yes_bid_size / yes_ask_size, each valued at its own price. Gamma's book-wide liquidity is reported separately as bookWideLiquidityUsd and is not used for this, because it is not the size behind the quote.

A pair is refused, not priced, when:

  • either venue has no two-sided quote (one side of the book is empty);
  • the depth a direction needs is below minLiquidityUsd, in both directions;
  • a leg is not accepting orders, or is not a binary Yes/No market;
  • the pair string does not resolve to one market on each venue.

Every refusal names the input to change and is returned with the record rather than dropped.

Fees

  • Kalshi: ceil(coefficient x P x (1 - P)) dollars per contract, rounded up to the next cent — 2c on a 50c contract. The coefficient (published: 0.07) is an input, because a stale fee constant would quietly overstate every edge.
  • Polymarket: reads feesEnabled and feeSchedule from each Gamma market. For a positive finite rate no greater than 1, exponent: 1 and takerOnly: true, the one-share taker fee is rate × P × (1 − P) USD, evaluated at the Polymarket ask when buying or bid when selling. Amounts below $0.00001 are zero; other amounts round half-up to five USD decimals before conversion to cents. For example, rate 0.05 at a 42c price costs $0.01218, or 1.218c per share. Only an explicit feesEnabled: false establishes a fee-free market. No category defaults or maker/taker rebate discounts are assumed. See the official fee formula and precision and market fee settings.
  • Missing, malformed or unsupported settings produce polymarketFeeCents: null, netPriced: false and a null edgeNetPct (or indicativeEdgeNetPct for suggestions). The valid quotes, gross gap and individual leg rows remain available. The displayed direction then maximizes the gross gap and is marked directionBasis: "gross-edge-fees-unknown"; it is not a net-profit recommendation. When fees are known, direction selection subtracts both executed legs' fees. Apply a minimum-net filter only to rows with netPriced: true and a non-null net field; unknown does not mean zero.
  • polymarketFeesEnabled, polymarketFeeRate, polymarketFeeExponent and polymarketFeeTakerOnly expose the validated source settings. Individual Polymarket leg rows expose the same metadata as feesEnabled, feeRate, feeExponent, feeTakerOnly, feeStatus and feeReason, plus bidFeeCents / askFeeCents. Calculations are per one share; an actual multi-share order can round differently, and execution prices can move.
  • Gas / relayer cost on the Polymarket leg is not modelled. A net edge smaller than your own transaction cost is not a trade.

What one row looks like

Measured on 2026-08-09 (real run, fee-free at that time) — one confirmed pair, with the current fee-status fields shown and a few fields left out for length. Current runs re-read each market's fee settings.

{
"type": "confirmed_pair",
"pair": "will-bitcoin-outperform-gold-in-2026|KXBTCVSGOLD-26",
"matchVerified": true,
"polymarketQuestion": "Will Bitcoin outperform Gold in 2026?",
"kalshiQuestion": "Will Bitcoin outperform gold in 2026?",
"polymarketYesBid": 0.17,
"polymarketYesAsk": 0.19,
"kalshiYesBid": 0.178,
"kalshiYesAsk": 0.215,
"polymarketDepthUsd": { "bid": 1.7, "ask": 19.0 },
"kalshiDepthUsd": { "bid": 0.93, "ask": 10.24 },
"closeGapDays": 0.0,
"priced": true,
"edgePct": -1.2,
"edgeNetPct": -3.2,
"direction": "buy polymarket / sell kalshi",
"kalshiFeeCents": 2,
"polymarketFeeCents": 0,
"polymarketFeeStatus": "disabled",
"polymarketFeeReason": "Gamma explicitly reports feesEnabled=false",
"polymarketFeesEnabled": false,
"netPriced": true,
"directionBasis": "net-edge",
"directionsBlocked": []
}

Each row also carries feeModel and depthBasis in plain words, so the numbers above can be checked without reading this page.

Price

$0.02 per contract pair priced — every pair that gets a gross price gap is charged, including suggestions and rows whose net gap is null because the Polymarket fee is unknown, so ten priced pairs cost $0.20. A refused pair is never charged: a pair without a two-sided quote on both venues, or with a leg too thin to trade, produced no answer, and you do not pay for a non-answer.

Each venue's side of a pair is $0.002 and on by default (includeIndividualLegs), two per priced pair — confirmed and suggested alike. A refused pair carries no leg rows and no leg charge, because a refusal is free whole. Turn it off if you only want the pair rows. You pay per pair; there is no monthly fee.

Input

{
"pairs": ["will-bitcoin-outperform-gold-in-2026|KXBTCVSGOLD-26"],
"autoSuggest": true,
"categories": ["Financials"],
"maxEventsPerVenue": 300,
"maxCloseGapDays": 3,
"minMatchScore": "0.6",
"minLiquidityUsd": "0",
"kalshiFeeCoefficient": "0.07"
}

categories filters the Kalshi side only, and it is the lever that matters: Kalshi carries over 8,000 open events, so scanning without a category is scanning a slice of an arbitrary ordering. Pick the categories your pairs live in (Politics, Economics, Elections, Financials, Companies, Crypto, World, Sports, Climate and Weather).

maxEventsPerVenue defaults to 200 and tops out at 300; the example above asks for the maximum. 300 is where the scan stops fitting in the 256MB the Actor runs under — both venues' pools stay in memory for the whole match — so it is a measured ceiling rather than a chosen one.

Reads both sites' public data only, no login, requests spaced out. Nothing is kept between runs — every run re-reads both live order books.

If something goes wrong

  • Wrong number or a failed run? Open a ticket on the Issues tab. I read every one and reply within 2 business days (Japan time).
  • You never get a fake "empty" result. If the site can't be read, the run fails and says so.
  • A pair that can't be priced = no charge. A pair that does get a price costs $0.02 even when the gap comes out negative — that number is the result you asked for.
  • Checked every week. An automatic test runs this tool weekly; if the site changes, I fix it.
  • Public pages only. No login, no personal data, and it goes easy on the site.

More tools by the same author

All tools (Japan marketplaces, real estate, jobs, racing, prediction markets): https://apify.com/jpmarketdata

Disclaimer

Unofficial, independent tool — not affiliated with, endorsed by, or sponsored by Kalshi or Polymarket. Product names and logos belong to their owners and only say where the data comes from. Data is read from public pages, for market research; check before you act on it.