BellwetherOpen Polymarket

Polymarket API Guide (2026): US and International Endpoints, Auth, Limits and Code

Illustration of a trading floor next to a Bellwether card

How we make money: we earn a commission if you sign up for Polymarket or subscribe to KalshiSpy through our links. We earn nothing from Kalshi, Robinhood, FanDuel, DraftKings, Novig or OG. Rankings are not for sale. Full disclosure

Polymarket has two separate sets of APIs, one for each of its products. Polymarket US, the CFTC-regulated exchange for US residents, has a public market-data API and an authenticated Retail API for orders. The international polymarket.com site has the Gamma, CLOB and Data APIs, plus WebSocket feeds. Anyone can read the international market data, but its order endpoints reject new orders from the United States. If you are in the US and want to trade by API, you need the Polymarket US API.

Everything below was checked against docs.polymarket.us and docs.polymarket.com on October 16–17, 2026. Both APIs change often. Both changelogs list breaking changes as recently as October 2026, so check the docs before you ship.

Which API to use

You want toUseBase URLKey needed?
Read US markets, books and pricesPolymarket US public APIhttps://gateway.polymarket.usNo
Trade on Polymarket US from the USPolymarket US Retail APIhttps://api.polymarket.usYes
Find international markets and eventsGamma APIhttps://gamma-api.polymarket.comNo
Read international books, prices and price historyCLOB APIhttps://clob.polymarket.comNo
Trade on the international site (outside the US)CLOB APIhttps://clob.polymarket.comYes
Read wallet positions, trades, holders and leaderboardsData API v2https://data-api.polymarket.com/v2No
Move funds between chains (international)Bridge APIhttps://bridge.polymarket.comNo

The two products have different markets, prices and order books. A Polymarket US market and the international market on the same event are separate instruments, with different identifiers and different prices.

The Polymarket US API

Public market data

The public API needs no key. Markets are identified by a slug, such as tec-mlb-champ-2026-09-27-cle.

EndpointReturns
GET /v1/marketsMarkets, with limit, offset, active, closed and ordering filters
GET /v1/markets/{slug}One market
GET /v1/markets/{slug}/bookFull order book and stats
GET /v1/markets/{slug}/bboBest bid and offer, last trade and open interest, in a lighter format
GET /v1/markets/{slug}/settlementSettlement price
GET /v1/eventsEvents, with the same filters
GET /v1/price-historyYes and No display prices for one market; pass the slug as symbol

There are also endpoints for series, sports leagues and teams, tags and search. Prices come back as objects such as {"value": "0.2050", "currency": "USD"}. Book levels use px for price and qty for quantity, and the book's sell side is called offers, not asks.

Each market also carries a feeCoefficient. On October 17, 2026 it was 0.0695 on every market we sampled, matching the published fee schedule.

Python: read US prices

Tested against the live API on October 17, 2026.

import requests

GATEWAY = "https://gateway.polymarket.us"

markets = requests.get(
    f"{GATEWAY}/v1/markets",
    params={"limit": 5, "active": "true", "closed": "false"},
    timeout=10,
).json()["markets"]

for m in markets:
    bbo = requests.get(f"{GATEWAY}/v1/markets/{m['slug']}/bbo", timeout=10).json()["marketData"]
    bid = bbo.get("bestBid", {}).get("value")
    ask = bbo.get("bestAsk", {}).get("value")
    print(f"{m['question']} [{m['slug']}]: bid {bid} / ask {ask}")

Sample output: World Series Champion [tec-mlb-champ-2026-09-27-cle]: bid 0.2050 / ask 0.2060.

Getting API keys

  1. Open an account in the Polymarket US app and pass identity verification. Our guide to using Polymarket in the US covers that.
  2. Go to polymarket.us/developer and sign in with the same method you used in the app: Apple, Google or email.
  3. Create a key. You get a Key ID and a Secret Key. The secret is shown once.

Polymarket US warns that switching sign-in methods can break API access (authentication docs).

Signing requests

Authenticated requests carry three headers:

HeaderValue
X-PM-Access-KeyYour Key ID
X-PM-TimestampCurrent time in milliseconds
X-PM-SignatureEd25519 signature of timestamp + METHOD + path, base64-encoded

The timestamp must be within 30 seconds of server time. This is the pattern from Polymarket US's docs:

import base64
import os
import time

import requests
from cryptography.hazmat.primitives.asymmetric import ed25519

key_id = os.environ["POLYMARKET_KEY_ID"]
secret = os.environ["POLYMARKET_SECRET_KEY"]
private_key = ed25519.Ed25519PrivateKey.from_private_bytes(base64.b64decode(secret)[:32])

def auth_headers(method, path):
    timestamp = str(int(time.time() * 1000))
    message = f"{timestamp}{method}{path}"
    signature = base64.b64encode(private_key.sign(message.encode())).decode()
    return {
        "X-PM-Access-Key": key_id,
        "X-PM-Timestamp": timestamp,
        "X-PM-Signature": signature,
        "Content-Type": "application/json",
    }

path = "/v1/portfolio/positions"
positions = requests.get(f"https://api.polymarket.us{path}", headers=auth_headers("GET", path), timeout=10)
print(positions.json())

The official SDKs

Polymarket US publishes polymarket-us for Python (3.10+) and TypeScript (Node 18+). The SDK signs requests for you.

import os
from polymarket_us import PolymarketUS

client = PolymarketUS(
    key_id=os.environ["POLYMARKET_KEY_ID"],
    secret_key=os.environ["POLYMARKET_SECRET_KEY"],
)

book = client.markets.book("chiefs-super-bowl")
order = client.orders.create({
    "marketSlug": "chiefs-super-bowl",
    "intent": "ORDER_INTENT_BUY_LONG",
    "type": "ORDER_TYPE_LIMIT",
    "price": {"value": "0.55", "currency": "USD"},
    "quantity": 100,
    "tif": "TIME_IN_FORCE_GOOD_TILL_CANCEL",
})

The slug above is the docs' example. Use a live slug from /v1/markets.

The Retail API covers orders (create, modify, cancel, preview, close a position, and batches of up to 20), portfolio positions and activity, account balances, combos and RFQs (generally available since October 1, 2026), and incentive programs.

US WebSockets

EndpointStreams
wss://api.polymarket.us/v1/ws/privateYour orders, positions, balances and RFQ updates
wss://api.polymarket.us/v1/ws/marketsOrder books, best bid and offer, and trades

Both need your API key. Polymarket US recommends them over polling. For example, subscribe to SUBSCRIPTION_TYPE_MARKET_DATA_LITE instead of calling /bbo in a loop.

US rate limits

From the rate limits page, as of October 2026:

  • General: 25 requests per second across the Retail API, counted per source IP and Cloudflare location. Several keys on one IP share the limit.
  • Combos and RFQs: POST /v1/combos and POST /v1/rfqs share 1 request per second. The RFQ read endpoints are 1 per second each. Creating and deleting quotes allows 100 per second.
  • Over the limit: you get 429 Too Many Requests. Wait at least a second and back off exponentially.
  • Latency stopgap: when the exchange is slow, it rejects any new order not processed within 5 seconds, with the message Global Rate Limit Exceeded. Despite the wording, this is not a rate limit, and you do not need to slow down. Pure cancels are never rejected this way.

Production trading systems that need higher limits can ask support@polymarket.us.

Maintenance window

Polymarket US goes down every Thursday from 6 to 8 a.m. Eastern (since September 24, 2026; it was 2 to 4 a.m. before). All open orders are canceled first, all API requests return 503 during the window, and books reopen empty (general FAQs). Build your bot to expect this.

The international APIs

The international site settles trades on the Polygon blockchain. Its collateral is pUSD, a token backed by USDC (pUSD docs). Older guides that tell you to hold "native USDC, not USDC.e" are out of date: deposits of either are wrapped into pUSD.

Gamma API: find markets

Gamma serves events, markets, tags, series, comments, sports metadata and search.

EndpointNotes
GET /marketslimit and offset paging; filters include closed, slug, condition_ids, clob_token_ids, tag_id, volume and liquidity ranges; sort with order and ascending
GET /markets/keysetCursor paging: pass next_cursor back as after_cursor; limit up to 100; offset returns 422
GET /markets/{id} and /markets/slug/{slug}One market
GET /events and /events/keysetEvents, which group related markets
GET /public-searchSearch markets, events and profiles

A Gamma market object includes question, conditionId, slug, outcomes, outcomePrices and clobTokenIds. The last three are JSON-encoded strings, so parse them before use. Each outcome has its own token ID, and the CLOB and Data APIs use those token IDs.

CLOB API: books and prices

The CLOB's public read endpoints need no key.

EndpointReturns
GET /book?token_id=Order book: bids, asks, tick_size, min_order_size, neg_risk, last_trade_price
POST /booksSeveral books in one request
GET /price?token_id=&side=Best price on one side
GET /midpoint?token_id=Midpoint of best bid and best ask
GET /spread?token_id=Spread
GET /last-trade-price?token_id=Last trade price
GET /prices-history?market=Price series for one token ID; interval is one of 1m, 1h, 6h, 1d, 1w, all, max; fidelity sets resolution in minutes; or pass startTs and endTs
GET /tick-size and GET /fee-rateMarket trading parameters

Older guides use /orderbook/{token_id} and /markets on the CLOB. Use /book and Gamma's /markets instead.

Python: read international books

Tested against the live API on October 17, 2026.

import json
import requests

GAMMA = "https://gamma-api.polymarket.com"
CLOB = "https://clob.polymarket.com"

# The five open markets with the most 24-hour volume
markets = requests.get(
    f"{GAMMA}/markets",
    params={"closed": "false", "limit": 5, "order": "volume24hr", "ascending": "false"},
    timeout=10,
).json()

for m in markets:
    token_ids = json.loads(m["clobTokenIds"])  # a JSON string, not a list
    outcomes = json.loads(m["outcomes"])

    book = requests.get(f"{CLOB}/book", params={"token_id": token_ids[0]}, timeout=10).json()
    bids = [float(level["price"]) for level in book["bids"]]
    asks = [float(level["price"]) for level in book["asks"]]
    best_bid = max(bids) if bids else None
    best_ask = min(asks) if asks else None

    print(m["question"])
    print(f"  {outcomes[0]}: bid {best_bid} / ask {best_ask}, tick {book['tick_size']}")

The code takes the highest bid and the lowest ask rather than the first element of each list, so it does not depend on sort order. A side can be empty, which is why it checks for that.

curl checks

# Five open markets
curl -G "https://gamma-api.polymarket.com/markets" \
  --data-urlencode "closed=false" --data-urlencode "limit=5"

# Midpoint for one token
curl "https://clob.polymarket.com/midpoint?token_id=<TOKEN_ID>"

# Hourly prices for the last day
curl "https://clob.polymarket.com/prices-history?market=<TOKEN_ID>&interval=1d&fidelity=60"

# The most recent trade across all markets
curl "https://data-api.polymarket.com/v2/trades?limit=1"

Data API v2: wallets and activity

The Data API reports what has happened and who holds what. It needs no key. Version 1 is retired on October 24, 2026, so new code should use v2 (migration guide).

v2 endpointReturns
GET /v2/positions?user=A wallet's positions; status can be OPEN, REDEEMABLE, REDEEMABLE_LOST, MERGEABLE or CLOSED
GET /v2/tradesTrade feed, filterable by user or condition
GET /v2/activityAccount activity
GET /v2/holders?condition=Top holders of a market
GET /v2/oi and /v2/live-volumeOpen interest and live event volume
GET /v2/leaderboardTrader rankings
GET /v2/prices-history?token_id=A token's price history
GET /v2/user-pnl, /v2/user-stats, /v2/user-volumeA user's PnL series, stats and volume

What changed from v1:

  • Every response wraps its payload in data. Paged routes add a pagination object.
  • Paging is cursor-only. Pass pagination.next_cursor back as cursor until it is null. There is no offset.
  • Fields are snake_case (proxy_wallet, condition_id).
  • Markets are selected with condition (up to 20 condition IDs), not market.
  • /closed-positions is folded into /v2/positions?status=CLOSED.
  • On the feed endpoints, resend the same filters with every page.

Authentication for trading

Placing orders on the international CLOB takes two layers (API docs):

  1. L1. Your wallet signs an EIP-712 ClobAuth message on Polygon (chain ID 137). You send that signature to POST /auth/api-key to create credentials, or to GET /auth/derive-api-key to recover existing ones. You get back an apiKey, a secret and a passphrase.
  2. L2. Each private request is signed with HMAC-SHA256 using the secret, over the timestamp, the uppercase method, the path and the exact body. The request carries five headers: POLY_ADDRESS, POLY_SIGNATURE, POLY_TIMESTAMP, POLY_API_KEY and POLY_PASSPHRASE.

Each order is also signed by the wallet. The SDKs handle all of this.

Check the location first. GET https://polymarket.com/api/geoblock returns whether the calling IP is blocked, with its country and region. The United States and the United Kingdom are close-only on both the website and the API: existing positions can be closed, new orders are rejected (geographic restrictions).

SDKs

Polymarket replaced its older CLOB clients with unified SDKs that cover Gamma, CLOB, Data API and wallet setup in one client:

LanguagePackageNotes
Pythonpolymarket-clientPublicClient and AsyncPublicClient for data; SecureClient and AsyncSecureClient for trading
TypeScript@polymarket/clientSigner adapters for Viem, Privy and Ethers v5
RustIn developmentA migration guide covers the older crate

If your code imports py-clob-client, @polymarket/clob-client-v2 or the builder relayer packages, follow the SDK migration guide. A minimal Python example:

from polymarket import PublicClient

with PublicClient() as client:
    page = client.list_markets(closed=False, page_size=10).first_page()
    for market in page.items:
        print(market)

International WebSockets

FeedURLUse
Market channelwss://ws-subscriptions-clob.polymarket.com/ws/marketPublic books, price changes, trades and market lifecycle
User channelwss://ws-subscriptions-clob.polymarket.com/ws/userYour orders and fills; needs API credentials
Sportswss://sports-api.polymarket.com/wsLive game status and scores
PolyBolt live datawss://ws-live-v2.polymarket.com/wsReference prices for crypto, equities and TWAPs; replaces the older RTDS feed and needs CLOB credentials

To subscribe to the market channel, send the token IDs after you connect:

const ws = new WebSocket("wss://ws-subscriptions-clob.polymarket.com/ws/market");

ws.onopen = () => {
  ws.send(JSON.stringify({
    type: "market",
    assets_ids: ["<TOKEN_ID>"],
    custom_feature_enabled: true, // adds best_bid_ask, new_market and market_resolved events
  }));
};

ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);
  console.log(msg.event_type, msg);
};

The server sends a book snapshot first unless you set initial_dump: false. Subscriptions do not survive a disconnect, so reconnect and resubscribe in code.

International rate limits

Polymarket enforces IP-based limits through Cloudflare. Requests over the limit are throttled, delayed or queued, rather than rejected at once. Selected limits from the rate limits page, as of October 2026:

APIEndpointLimit
GammaGeneral4,000 requests per 10 s
Gamma/markets300 per 10 s
Gamma/events500 per 10 s
CLOB/book, /price, /midpoint1,500 per 10 s each
CLOB/books, /prices, /midpoints500 per 10 s each
CLOB/prices-history1,000 per 10 s
CLOBPOST /order5,000 per 10 s burst; 120,000 per 10 min sustained
CLOBDELETE /cancel-all250 per 10 s burst; 6,000 per 10 min sustained
Data API v2General800 per 10 s
Data API v2/v2/trades300 per 10 s
BridgeGeneral50 per 10 s

Order and cancel requests also count against per-signer token buckets, separate for orders and cancels, with tiers based on 30-day trading volume. Polymarket ran these in warning mode from July 24, 2026, flagging requests that would be rejected with the header Poly-RateLimit-Warning: true (trading rate limits). Watch for that header. A batch order is admitted only if the bucket holds enough tokens for every order in it.

Fees for API traders

International taker fees are set by market category and applied at match time. You do not put them in your order. The formula is fee = shares × feeRate × p × (1 − p). Rates run from 0.04 (politics, finance, tech, mentions) to 0.07 (crypto). Geopolitics markets are fee-free. Makers pay nothing and receive rebates funded from taker fees (fees). Read a market's rate from GET /fee-rate.

On Polymarket US, the taker fee is 0.0695 × contracts × p × (1 − p) on standard markets as of October 2026, at most $1.74 per 100 contracts. Makers earn a rebate. Rebates move to volume tiers on October 19, 2026 (fee schedule).

On-chain data

International trades, balances and positions settle on Polygon, so you can also query them directly. Polymarket's data resources page points to:

  • Goldsky, which streams Polymarket on-chain data into your own database, and CryptoHouse, a ClickHouse instance you can query with SQL.
  • Dune, with starter queries for volume, value locked and open interest, plus community dashboards.
  • Allium, another SQL analytics platform.

Polymarket notes that third-party schemas can lag its contract upgrades, so check indexed addresses against its contracts page. On-chain data covers the international site only. Polymarket US trades do not settle on a public blockchain.

Handling errors

Common responses on both APIs:

  • 400 for bad parameters, such as a condition ID passed where a token ID belongs. International error bodies can include retry_after_seconds.
  • 401 for missing or invalid credentials.
  • 404 when no order book exists for a token, which happens on closed or inactive markets.
  • 422 on Gamma when you pass offset to a keyset endpoint or closed=any without an ID filter.
  • 429 when you are over a limit on Polymarket US.
  • 503 during the Polymarket US maintenance window.

A retry helper with exponential backoff and jitter:

import random
import time

import requests

def get_json(url, params=None, max_retries=5):
    for attempt in range(max_retries):
        try:
            r = requests.get(url, params=params, timeout=10)
            if r.status_code in (429, 500, 502, 503):
                time.sleep(min(2 ** attempt + random.random(), 30))
                continue
            r.raise_for_status()
            return r.json()
        except requests.ConnectionError:
            time.sleep(min(2 ** attempt + random.random(), 30))
    raise RuntimeError(f"Gave up on {url}")

Because the international API throttles rather than rejects, a slow response can be a rate-limit signal. Set timeouts, and measure latency as well as status codes.

Building a trading bot

An API client is the easy part of a bot. The work is in the signal, sizing, risk controls and reliability.

A typical loop:

  1. Pull market state from a WebSocket feed, or by polling books and prices.
  2. Work out your own probability from outside data: polls, news, prices on other venues.
  3. Size the position from your edge, your confidence, your current position and your risk budget.
  4. Place or amend orders to close the gap between the position you want and the one you have.
  5. Track fills, fees and profit and loss.

Practical points:

  • Fees and spreads set the bar. At 50¢ the US taker fee is about 3.5% of the money you put in. If your edge is smaller than the fee plus the spread, you have no edge.
  • Most markets are not fast. Outside the busiest games, books rarely move in milliseconds. Polling every few seconds is fine for most strategies.
  • Cancel logic matters. Stale orders left on the book during a sharp move fill at bad prices. Build a kill switch. The international CLOB has a heartbeat endpoint for this.
  • Plan for downtime. Polymarket US cancels every open order before its Thursday window. The international matching engine has maintenance restarts too.
  • Start tiny. Trade at a hundredth of your planned size for the first week.

Bots must also follow each exchange's rules. Polymarket US bars trading on confidential information obtained in breach of a duty, and some contracts bar people connected to the event (trading restrictions).

Building a research tool

Most useful API projects never place an order.

  • Probability over time. Poll prices every few minutes into a database and chart them against news.
  • Backtesting. Pull /prices-history and test whether a signal would have beaten the market.
  • Cross-venue comparison. Compare a Polymarket US price with the same event on the international site or on Kalshi. Different books often disagree.
  • Resolution tracking. Watch the source a market resolves on, such as a government data release, so you know when the outcome is set.

Read endpoints cover all of these without a key.

Gotchas

  1. Token ID, condition ID or slug. International markets have one condition ID and one token ID per outcome. Order books and price history need the token ID. Polymarket US uses slugs.
  2. Encoded fields. Gamma returns outcomes, outcomePrices and clobTokenIds as JSON strings.
  3. Prices are strings. Both APIs return prices as strings such as "0.5500". Parse with Decimal if you do arithmetic on money.
  4. Data API v1 shuts down October 24, 2026.
  5. Breaking changes are frequent. On October 15, 2026 Polymarket US changed its event list endpoints to return primary markets only. Watch both changelogs.
  6. US data retention. Polymarket US prunes old executions and keeps market data and orders for 45 and 90 days. Store what you need.
  7. Location. Orders to the international CLOB from the US are rejected. Do not route around it.

FAQ

Does Polymarket have an API?

Yes, two. Polymarket US has a public market-data API at gateway.polymarket.us and an authenticated Retail API at api.polymarket.us. The international site has the Gamma, CLOB and Data APIs.

Can I trade on Polymarket by API from the US?

Yes, through the Polymarket US Retail API, after you open and verify a Polymarket US account and create a key at polymarket.us/developer. The international CLOB rejects new orders from US IP addresses.

Do I need an API key to read data?

No. Polymarket US public endpoints, Gamma, the CLOB read endpoints and Data API v2 all work without a key.

What are the rate limits?

Polymarket US allows 25 requests per second in general, per IP. The international APIs use Cloudflare limits per 10-second window, such as 1,500 per 10 seconds for /book, plus per-signer limits on orders.

Which SDK should I use?

For Polymarket US, polymarket-us in Python or TypeScript. For the international site, polymarket-client in Python or @polymarket/client in TypeScript. The older py-clob-client has been replaced.

How do I get historical prices?

On the international site, GET /prices-history on the CLOB or GET /v2/prices-history on the Data API, with a token ID. On Polymarket US, GET /v1/price-history with the market slug as symbol. For bulk history on the international site, Dune, Goldsky and Allium hold the on-chain record.

What does Polymarket settle in?

Polymarket US settles in US dollars through its clearinghouse, QC Clearing LLC. The international site settles in pUSD, a USDC-backed token on Polygon.

Where is the WebSocket?

Polymarket US: wss://api.polymarket.us/v1/ws/markets and /v1/ws/private, both with a key. International: wss://ws-subscriptions-clob.polymarket.com/ws/market for public data and /ws/user for your own orders.

Weekly roundup · Free

Get the Bellwether weekly market roundup

One short email each Sunday: what moved on Kalshi and Polymarket, the regulation news that mattered, and the markets worth watching.

One email per week · Unsubscribe anytime · We don't share your email