# Polymarket API in Python: read markets, stream books and place orders

Use Polymarket's API in Python with polymarket-client. Read markets and order books with no key, stream prices, place limit orders and avoid the old clients.

Source: https://rizon.markets/learn/polymarket-api-python (updated 2026-11-05)

> **Short answer:** Install `polymarket-client` and use `PublicClient`. Market data needs no key. Trading needs `SecureClient`, a wallet and funds. Older guides use `py-clob-client`, but Polymarket archived it on May 25, 2026, and [its README](https://github.com/Polymarket/py-clob-client) says it no longer works.

We ran the read and stream code below on Nov 5, 2026, with version 0.16.0. We didn't run the order code, because it needs a funded wallet. It follows [Polymarket's docs](https://docs.polymarket.com/trading/place-orders).

## The APIs

The SDK wraps these, and you can also call them directly.

| API | Base URL | Used for | Key needed |
|---|---|---|---|
| Gamma | `https://gamma-api.polymarket.com` | Markets and events | No |
| CLOB | `https://clob.polymarket.com` | Books, prices and orders | For private requests |
| Data | `https://data-api.polymarket.com` | Positions and trades | |
| Market WebSocket | `wss://ws-subscriptions-clob.polymarket.com/ws/market` | Live books | No |

## Read a market and its order book

You'll need Python 3.11 or newer, then `pip install polymarket-client`.

```python
from polymarket import PublicClient

with PublicClient() as client:
    page = client.list_markets(closed=False, order="volume24hr", ascending=False, page_size=5).first_page()
    market = page.items[0]
    print(market.slug, market.question)

    # the token for this market's Yes side
    yes = market.outcomes.yes
    asset_id = yes.position_id if market.version == "v2" else yes.token_id

    book = client.get_order_book(asset_id=asset_id)
    print("best bid", max(level.price for level in book.bids))
    print("best ask", min(level.price for level in book.asks))
    print("midpoint", client.get_midpoint(asset_id=asset_id))
    print("spread", client.get_spread(asset_id=asset_id))
    print("smallest order", book.min_order_size)
```

Use `max` and `min` for the best prices. When we ran this, `book.bids[0]` was the lowest bid on the book, at 0.001, and `book.asks[0]` was the highest ask.

To look up one market, use its slug or its page address: `client.get_market(slug="...")` or `client.get_market(url="...")`.

## Get price history

```python
points = client.list_price_history(asset_id=asset_id, interval="1d", bucket_seconds=3600).first_page().items
print(points[-1].timestamp, points[-1].price)
```

Prices come back as `Decimal`, so convert them before doing floating-point math.

## Stream the book

Streams are async only. The first event is the whole book, and the later ones are changes.

```python
import asyncio

from polymarket import AsyncPublicClient
from polymarket.streams import MarketSpec

async def main(asset_id: str) -> None:
    async with AsyncPublicClient() as client:
        async with await client.subscribe([MarketSpec(asset_ids=[asset_id])]) as stream:
            async for event in stream:
                print(event.topic, event.type)

asyncio.run(main("YOUR_ASSET_ID"))
```

## Place and cancel a limit order

Trading needs your wallet's private key and address, and pUSD in the account. Keep the key in an environment variable, and never commit it.

```python
import os

from polymarket import SecureClient

with SecureClient.create(
    private_key=os.environ["POLYMARKET_PRIVATE_KEY"],
    wallet=os.environ["POLYMARKET_WALLET_ADDRESS"],
) as client:
    response = client.place_limit_order(asset_id=asset_id, side="BUY", price="0.52", size="10", post_only=True)
    if not response.ok:
        raise RuntimeError(response.message)
    print(response.order_id)
    client.cancel_order(order_id=response.order_id)
```

`post_only=True` makes the order rest or get rejected, so it can't take liquidity. Makers pay no fee on Polymarket, while takers pay [a fee that depends on the category and price](https://rizon.markets/learn/polymarket-fee-calculator).

## What goes wrong

| Problem | Why |
|---|---|
| `order_version_mismatch` on market orders | [The old client](https://github.com/Polymarket/py-clob-client/issues/337). Move to `polymarket-client` |
| Order rejected for its price | It isn't on the market's tick size, which can change while a market trades |
| Order rejected for its size | It's below the book's `min_order_size` |
| Post-only order rejected | It would have matched at once, which is what post-only prevents |
| Expiring order gone early | Good-till-date orders expire a minute before their timestamp, which must be at least 3 minutes ahead |
| Orders refused by location | [Polymarket lists](https://docs.polymarket.com/polymarket-learn/FAQ/geoblocking) the US, UK, Australia and others as close-only, on the API too |

## Test your bot first

We found no testnet or sandbox in Polymarket's docs, so a bug there costs real money. You can run your quoting logic on [Rizon's API](https://rizon.markets/docs) first, which takes one call to get a key and uses practice money:

```python
import requests

API = "https://rizon.markets/api/v1"
key = requests.post(f"{API}/accounts").json()["key"]
headers = {"Authorization": f"Bearer {key}"}

print(requests.get(f"{API}/markets").json()["markets"][0]["id"])
print(requests.post(f"{API}/orders", headers=headers, json={"market": "BTC-15s", "side": "up", "size": 10}).json())
```

It's a different contract. Rizon's book quotes a strike and pays 2×, so you're testing your plumbing and your quoting, not Polymarket's matching. [Where to test a trading bot without real money](https://rizon.markets/learn/test-a-trading-bot-without-real-money) compares the options. If you prefer to work with an AI assistant, see [how to build a trading bot with Claude Code](https://rizon.markets/learn/how-to-build-a-trading-bot-with-claude-code).

## FAQ

### Does the Polymarket API need an API key?

Not for market data. Trading and account data need a wallet signature. `SecureClient` handles the signing and creates the API credentials for you.

### What happened to py-clob-client?

Polymarket archived it on May 25, 2026. `py-clob-client-v2` is archived too. [The migration guide](https://docs.polymarket.com/getting-started/migrate-from-previous-sdks) moves you to `polymarket-client`.

### What are the API's rate limits?

[Polymarket publishes them](https://docs.polymarket.com/api-reference/rate-limits) by endpoint, per IP. For example, `/book` allows 1,500 requests per 10 seconds, and placing orders allows 5,000 per 10 seconds in a burst. Requests over a limit are delayed rather than rejected.

### Is there a Polymarket testnet?

We found none in its docs. [Polymarket has no demo account](https://rizon.markets/learn/polymarket-demo-account) either.

### Can I use the Polymarket API from the US?

The global platform lists the US as close-only on both the site and the API. Polymarket US is a separate venue, with [its own docs](https://docs.polymarket.us).
