> youcanbuildthings.com
tutorials books topics about
reference from: Polymarket Profits 2

Polymarket Trading Fees: The Real Formula and Table

by J Cook · 7 min read·

Summary:

  1. Polymarket charges fees per category, with crypto at 7.2% and several categories at 5%.
  2. The fee formula uses price × (1−price), which peaks at $0.50 and falls toward the extremes.
  3. Makers pay zero and earn daily USDC rebates on specific categories.
  4. Hardcoding flat fees will lose money on every trade in the wrong category.

Most Polymarket bot tutorials tell you fees are “0 to 2%.” That is wrong, and it is wrong in a way that makes every edge calculation useless. The real number on a crypto market trade at $0.30 is closer to 5% of trade value. Bots that assume 2% bleed on every trade.

I ran my first arbitrage scanner with hardcoded 2% fees and watched it lose $180 in two days on spreads that looked profitable on paper. The gaps were real. The scanner just did not subtract the right fee, so every green-looking trade was actually a red one. This article is the fee math most tutorials skip.

What are Polymarket’s trading fees?

Polymarket charges a taker fee that varies by market category, ranging from 0% on Geopolitics to 7.2% on Crypto. From the official fees docs, the formula is:

fee = shares × feeRate × price × (1 − price)

Makers pay zero. Only takers pay. The numbers below are pulled directly from docs.polymarket.com/trading/fees:

CategoryTaker Fee RateMaker Rebate
Crypto0.072 (7.2%)20%
Sports0.03 (3.0%)25%
Finance0.04 (4.0%)25%
Politics0.04 (4.0%)25%
Economics0.05 (5.0%)25%
Culture0.05 (5.0%)25%
Weather0.05 (5.0%)25%
Other / General0.05 (5.0%)25%
Mentions0.04 (4.0%)25%
Tech0.04 (4.0%)25%
Geopolitics0

Geopolitics markets are fee-free. Every other category charges something. Your bot must know which category a market belongs to before sizing any trade.

Why does the fee depend on the share price?

Taker fee parabola for 100 shares on Polymarket by category: crypto 7.2 percent peaks at $1.80 at p=0.50, economics at $1.25, politics at $1.00, sports at $0.75, geopolitics flat at zero. All curves show the price times one-minus-price parabola shape dropping toward zero at the extremes

Because the fee formula has a price × (1 − price) term, which is a parabola that peaks at $0.50 and drops toward zero at both extremes. It is the same shape as the variance of a coin flip at that probability.

Two trades carry the same dollar fee if their prices sum to 1.00. A trade at $0.30 and a trade at $0.70 cost the same because 0.30 × 0.70 = 0.70 × 0.30. A trade at $0.01 or $0.99 is nearly fee-free. A trade at $0.50 hits the maximum.

Here is the official fee table for a 100-share Crypto trade, pulled directly from docs.polymarket.com/trading/fees:

PriceTrade ValueTaker Fee (USDC)
$0.10$10$0.65
$0.20$20$1.15
$0.30$30$1.51
$0.40$40$1.73
$0.50$50$1.80
$0.60$60$1.73
$0.70$70$1.51
$0.80$80$1.15
$0.90$90$0.65

Notice the symmetry around $0.50. Also notice how fast the fee grows as a percent of trade value when the price is low. At $0.10, $0.65 fee on a $10 trade is 6.5% of trade value. At $0.50, $1.80 on $50 is 3.6%. At $0.30, $1.51 on $30 is 5.0%. Raw percentage of trade value is what kills bot edge, not the dollar fee.

What broke when I used flat fees?

My first scanner hardcoded 2% as the fee. On a 3.2% arbitrage gap between two crypto markets at mid prices, the scanner computed 1.2% net edge and fired the trade. The real fee was closer to 5% effective, so net edge was actually negative 1.8%. Every trade lost money. Two days of it was $180.

Fix: read the real fee rate per market and compute the fee from the formula, not from a guess.

def calculate_fee(shares, price, fee_rate_bps):
    """Compute the real Polymarket taker fee in USDC.

    Uses the official formula from docs.polymarket.com/trading/fees:
    fee = shares × feeRate × price × (1 − price)
    """
    fee_rate = fee_rate_bps / 10000
    return shares * fee_rate * price * (1 - price)


# Worked example: 500 shares at $0.30 on a crypto market
fee = calculate_fee(
    shares=500,
    price=0.30,
    fee_rate_bps=720,  # Crypto category: 0.072 = 720 bps
)
print(f"Fee: ${fee:.2f}")
# Fee: $7.56

trade_value = 500 * 0.30
print(f"Trade value: ${trade_value:.2f}")
print(f"Fee as % of trade value: {fee / trade_value * 100:.2f}%")
# Trade value: $150.00
# Fee as % of trade value: 5.04%

That 5.04% is what most tutorials call “0 to 2%.” Your edge calculations must subtract the real number, not the marketing one.

How do you fetch the live fee rate from the API?

Two calls, two endpoints. The CLOB exposes the fee rate for a specific token, the Gamma API exposes whether fees are enabled on the market. You need both, and the field names are not what you think.

import requests

CLOB_BASE = "https://clob.polymarket.com"
GAMMA_BASE = "https://gamma-api.polymarket.com"


def get_fee_rate_bps(token_id, default=720):
    """Fetch live fee rate in basis points for a CLOB token.

    The CLOB /fee-rate endpoint returns {"base_fee": <int>} where
    base_fee is an integer in basis points. Don't let the field
    name confuse you: it is a rate, not a flat fee.
    Returns int bps. Defaults to 720 (crypto worst case) on error.
    """
    try:
        resp = requests.get(
            f"{CLOB_BASE}/fee-rate",
            params={"token_id": token_id},
            timeout=5,
        )
        resp.raise_for_status()
        return int(resp.json().get("base_fee", default))
    except Exception:
        return default


def is_fees_enabled(market_id):
    """Check whether a Polymarket market actually charges fees.

    Some markets have feesEnabled=false and charge zero regardless
    of category. Bots that skip this check will reject good trades.
    """
    try:
        resp = requests.get(
            f"{GAMMA_BASE}/markets/{market_id}",
            timeout=5,
        )
        resp.raise_for_status()
        return bool(resp.json().get("feesEnabled", False))
    except Exception:
        return False

Check feesEnabled before computing any fee. Some markets are fee-free regardless of category. A bot that computes 720 bps on a fee-free crypto market will reject perfectly good trades.

Fee unit reference (keep this near your code)

Three names look interchangeable and are not:

NameWhere it livesWhat it is
feeRateFee docs, formulaDecimal (0.072 = 7.2%)
feeRateBpsOrder signing payloadInteger basis points (720 = 7.2%)
base_feeCLOB /fee-rate responseInteger basis points (same scale as feeRateBps)

Divide base_fee or feeRateBps by 10,000 to get the decimal feeRate that goes into the formula. Miss the conversion and your edge calculation is off by 100×.

Sanity check: reproduce the official $1.80 row

Polymarket’s fee table says 100 shares at p=$0.50 on a crypto market costs $1.80. Your code must reproduce that exact number. If it doesn’t, your base_fee parsing is wrong.

def test_reconciles_with_official_table():
    fee = calculate_fee(shares=100, price=0.50, fee_rate_bps=720)
    assert abs(fee - 1.80) < 0.0001, f"Got {fee}, expected 1.80"
    print(f"OK: 100 shares @ $0.50 crypto = ${fee:.2f}")

test_reconciles_with_official_table()
# OK: 100 shares @ $0.50 crypto = $1.80

Run it once on every code change. If the assert fires, you’ve broken the formula or the unit conversion.

How are fees collected — USDC or shares?

Fees are calculated in USDC but the asset they’re collected in depends on trade direction. Per the official fees docs:

  • Buy orders: fees are collected in outcome shares (you receive fewer shares than shares × price / price would give)
  • Sell orders: fees are collected in USDC (you receive less USDC than shares × price would give)

This matters for backtesting. If your simulator just subtracts a dollar fee from PnL, it’s fine for estimation. If you’re reconciling against actual on-chain balances, you need to model share deltas on buys and USDC deltas on sells separately.

What are maker rebates on Polymarket?

Maker rebates are daily USDC payments to liquidity providers who post limit orders that add depth to the book. Makers pay zero fees AND earn a share of the daily rebate pool on qualifying categories. From the docs, Crypto pays 20% and most other categories pay 25%.

The practical impact: a maker trade on a 5-minute crypto market is not just fee-free. It is yield-positive. You earn USDC on every fill on top of whatever the trade itself does. On the same 500-share trade at $0.30 above, a maker pays zero instead of $7.56. Over 100 trades a day, that is $756 in saved fees plus the rebate on top.

This is the reason every serious arbitrage bot on Polymarket uses limit orders, not market orders. The tradeoff is fill risk: your limit might never fill if the price moves away. That is fine for arbitrage where missing a trade is free and losing money on a filled taker trade is not.

def min_spread_for_breakeven(price, fee_rate_bps, execution="taker"):
    """Minimum spread (two legs) needed to break even after fees.

    Returns the per-share spread you need on a round trip. Pair this
    with slippage and you have your minimum profitable spread.
    """
    if execution == "maker":
        return 0  # Makers pay zero
    fee_rate = fee_rate_bps / 10000
    # Fee on buy leg + fee on sell leg
    return 2 * fee_rate * price * (1 - price)


# Compare taker vs maker on a crypto market at mid prices
print(f"Taker min spread @ $0.50: ${min_spread_for_breakeven(0.50, 720, 'taker'):.4f}")
print(f"Maker min spread @ $0.50: ${min_spread_for_breakeven(0.50, 720, 'maker'):.4f}")
# Taker min spread @ $0.50: $0.0360
# Maker min spread @ $0.50: $0.0000

3.6 cents minimum spread just to break even with taker execution on a crypto market at mid prices. Zero cents with maker execution. That is the structural reason limit orders dominate market orders on the highest-volume categories.

Which categories have the best fee math?

Geopolitics is the cleanest (zero fees). Sports is next best at 3% taker. Crypto is the worst taker category at 7.2%, but it also has the highest-volume 5-minute markets where maker rebates matter most. The right strategy is not “avoid Crypto.” It is “trade Crypto as a maker, never as a taker.”

Here is a rough ranking of categories by how easy it is to run a profitable taker bot:

RankCategoryReason
1Geopolitics0% fee. No fee drag at all.
2Sports3.0% taker fee. Fastest to clear.
3Finance / Politics / Tech / Mentions4.0% taker. Middle ground.
4Economics / Culture / Weather / Other5.0% taker. Thin edges get eaten.
5Crypto7.2% taker. Only works as a maker.

If you are running a pure taker bot (market orders), start with Geopolitics and Sports. Move to Crypto only when your execution quality is good enough to post limit orders that fill.

These fee functions plug directly into the paper-trading trainer from Chapter 3 of Polymarket Profits 2. Every strategy in the book imports them and runs them through the trainer first, so you validate your fee math on live market data with fake USDC before a single real dollar is on the line.

What should you actually do?

  • Always fetch feeRateBps before computing edge. Hardcoding flat rates will lose you money the day Polymarket updates fees.
  • Always check feesEnabled on the market metadata. Fee-free markets exist and your bot should not reject them.
  • Use the real formula shares × feeRate × price × (1−price) in every edge calculation. The quadratic shape matters more than the headline rate.
  • Post limit orders on Crypto, not market orders. The 7.2% taker fee eats every edge on mid-price markets. Maker execution is the only way the math works.
  • Bank the maker rebate as upside, not as baseline. Daily rebates are paid from the fee pool, so they scale with platform volume. Count them as a bonus, not a planned return line.

bottom_line

  • The “0 to 2%” fee number is wrong for most categories. The real effective fee on a Crypto trade at mid prices is 5% or worse.
  • The price × (1−price) term is the most important part of the formula. It makes prices near the extremes nearly fee-free and punishes trades at $0.50.
  • Trade Crypto as a maker or do not trade Crypto at all. Taker execution on that category is a guaranteed loss on typical arbitrage spreads.

Frequently Asked Questions

What are the actual trading fees on Polymarket?+

Polymarket charges taker fees from 0% (Geopolitics) to 7.2% (Crypto) with the formula `fee = shares × feeRate × price × (1−price)`. Makers pay zero and earn a rebate on certain categories. Source: docs.polymarket.com/trading/fees.

Why do Polymarket fees change based on the share price?+

The fee formula includes a `price × (1−price)` term, so fees peak at `p=$0.50` and drop toward zero at the extremes. A trade at $0.30 and a trade at $0.70 incur the exact same dollar fee because the math is symmetric around $0.50.

Are Polymarket maker orders really free?+

Yes. Taker orders that execute against existing liquidity pay the full category fee. Maker orders that add liquidity pay zero and earn a share of the daily rebate pool — 20% on crypto markets and 25% on most others per the fees docs.