Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.polymarket.com/llms.txt

Use this file to discover all available pages before exploring further.

The unified Python SDK gives you a consistent surface across Polymarket discovery, market data, trading, account data, and realtime streams.
The Python SDK is currently in beta. We are keeping it in this beta phase while we address issues and harden the SDK before transitioning to a more stable release.
The SDK ships parallel async and sync clients with matching method names and arguments: AsyncPublicClient / PublicClient for public reads, and AsyncSecureClient / SecureClient for authenticated reads and trading. Prefer the async clients for servers, bots, and any code that already runs inside an event loop. Use the sync clients for scripts, notebooks, and one-off tools where an event loop would just add ceremony. Realtime stream subscriptions are async only and require the async clients. Examples below show the body of an async def main() function; wrap them with asyncio.run(main()) to run as a script, as shown in Quickstart. To switch a snippet to sync, swap AsyncPublicClient / AsyncSecureClient for PublicClient / SecureClient, drop await, replace async with with with, replace async for with for, and remove the asyncio.run(...) wrapper.

Quickstart

1

Install the Package

Install the SDK from PyPI.
uv add polymarket-client
2

Create a Public Client

Create an instance of the AsyncPublicClient inside an async with block so its network transports are released when you are done.
from polymarket import AsyncPublicClient

async with AsyncPublicClient() as client:
    ...
3

Fetch Markets

Fetch a page of markets to discover active trading opportunities.
import asyncio

from polymarket import AsyncPublicClient


async def main() -> None:
    async with AsyncPublicClient() as client:
        markets = client.list_markets(closed=False, page_size=5)
        first_page = await markets.first_page()

        for market in first_page.items:
            # market: Market
            ...


asyncio.run(main())

SDK Patterns

The SDK uses consistent patterns for pagination, Python-native model values, and structured SDK exceptions across public and authenticated workflows.

Typed Primitives

Identifiers and EVM addresses are exposed as typing.NewType aliases (MarketId, ConditionId, TokenId, EventId, EvmAddress, …) so static type checkers can keep them distinct from plain strings. Precision-sensitive price, size, and amount fields generally use decimal.Decimal; date and time fields use datetime.date or datetime.datetime.
from datetime import datetime
from decimal import Decimal

from polymarket import ConditionId, EvmAddress, MarketId, TokenId


class Market:
    id: MarketId
    condition_id: ConditionId | None
    state: MarketState
    outcomes: MarketOutcomes
    resolution: MarketResolution
    # …


class MarketState:
    start_date: datetime | None
    end_date: datetime | None
    # …


class MarketOutcome:
    label: str
    token_id: TokenId | None
    price: Decimal | None


class MarketResolution:
    resolved_by: EvmAddress | None
    # …

Market and Event Data

Market and event responses are returned as SDK models with snake_case fields and nested submodels.
class Market:
    id: MarketId
    slug: str | None
    condition_id: ConditionId | None
    question: str | None
    description: str | None
    category: str | None
    image: str | None
    icon: str | None
    state: MarketState
    outcomes: MarketOutcomes
    metrics: MarketMetrics
    prices: MarketPrices
    trading: MarketTrading
    resolution: MarketResolution
    rewards: MarketRewards
    sports: MarketSportsMetadata
    tags: tuple[MarketTag, ...]
    # …

Environment Configuration

Production is the default environment. Pass an Environment object when your integration needs to target a different deployment or custom endpoint set. The client owns network transports, so use async with (or call await client.close()) to release them when you are done.
from polymarket import AsyncPublicClient, PRODUCTION


async with AsyncPublicClient(environment=PRODUCTION) as client:
    ...

Pagination

With async clients, list methods return an AsyncPaginator across paginated endpoints. Use async for to iterate through pages.
async with AsyncPublicClient() as client:
    markets = client.list_markets(closed=False, page_size=10)

    async for page in markets:
        # page.items: tuple[Market, ...]
        ...
You can also fetch the first page directly and resume later from a cursor.
first_page = await markets.first_page()
# first_page.items: tuple[Market, ...]

async for page in markets.from_cursor(first_page.next_cursor):
    # page.items: tuple[Market, ...]
    ...
When you only care about the items and not page boundaries, iterate them directly.
async for market in markets.items():
    # market: Market
    ...

Error Handling

All SDK exceptions inherit from PolymarketError. Catch specific subclasses to handle known cases, and catch PolymarketError as the final SDK fallback.
Catching PolymarketError last ensures error subclasses added in future SDK releases do not pass through unhandled.
from polymarket import (
    AsyncPublicClient,
    PolymarketError,
    RateLimitError,
    UserInputError,
)

async with AsyncPublicClient() as client:
    try:
        markets = client.list_markets(closed=False, page_size=10)
        first_page = await markets.first_page()
        # first_page.items: tuple[Market, ...]
    except RateLimitError:
        # Retry later.
        ...
    except UserInputError:
        # Fix the request parameters.
        ...
    except PolymarketError:
        # Handle any other SDK error.
        ...

Market Data

Use market data methods to fetch market and event details, order books, current prices, historical prices, and batch quotes.
market = await client.get_market(
    url="https://polymarket.com/market/eth-flipped-in-2026",
)

market_by_slug = await client.get_market(slug="eth-flipped-in-2026")

market_by_id = await client.get_market(id="12345")
Then fetch related tags, order books, prices, and history.
market_tags = await client.get_market_tags(market.id)

event_tags = await client.get_event_tags(event.id)

Discovery

Use discovery methods to browse events, markets, teams, tags, comments, sports metadata, and search results. The examples below show a few common entry points.
events = client.list_events(page_size=10)

async for page in events:
    # page.items: tuple[Event, ...]
    ...

Realtime Streams

Subscribe through one SDK interface even when events come from different stream families. The SDK routes each subscription spec to the right stream and merges the results into one async iterator. Subscriptions are async only and require AsyncPublicClient or AsyncSecureClient.
from polymarket import AsyncPublicClient
from polymarket.streams import CryptoPricesSpec, MarketSpec


yes_token_id = market.outcomes.yes.token_id
if yes_token_id is None:
    raise RuntimeError("Market does not have a YES token id")

async with AsyncPublicClient() as client:
    stream = await client.subscribe(
        [
            MarketSpec(token_ids=[yes_token_id]),
            CryptoPricesSpec(
                topic="prices.crypto.binance",
                symbols=["btcusdt"],
            ),
        ],
    )

    async with stream:
        async for event in stream:
            # event:
            #   | MarketBookEvent
            #   | MarketPriceChangeEvent
            #   | MarketLastTradePriceEvent
            #   | MarketTickSizeChangeEvent
            #   | MarketBestBidAskEvent
            #   | NewMarketEvent
            #   | MarketResolvedEvent
            #   | CryptoPricesBinanceEvent
            print(type(event).__name__)
            break
AsyncSecureClient.subscribe accepts the same public subscription specs and adds UserSpec for user-scoped order and trade events on the authenticated wallet.
import os

from polymarket import AsyncSecureClient
from polymarket.streams import UserSpec


async with await AsyncSecureClient.create(
    private_key=os.environ["POLYMARKET_PRIVATE_KEY"],
    wallet=os.environ.get("POLYMARKET_WALLET_ADDRESS"),
) as secure_client:
    user_stream = await secure_client.subscribe(UserSpec())

    async with user_stream:
        async for event in user_stream:
            # event:
            #   | UserOrderEvent
            #   | UserTradeEvent
            print(type(event).__name__)
            break

Authenticated Client

Create a secure client when you need wallet-scoped reads or trading.
Secure clients own multiple network transports. Wrap them in async with, or call await secure_client.close() when you are done, to release the underlying connections. The snippets below show client creation and subsequent calls as a flat sequence for readability — in real code, keep the client inside an async with block or close it explicitly.

Private Key Setup

The Python SDK authenticates with a local private key. Pass wallet when you want to operate on a Polymarket wallet address that differs from the signer address; if omitted, the client uses the signer address as the account wallet.
import os

from polymarket import AsyncSecureClient


async with await AsyncSecureClient.create(
    private_key=os.environ["POLYMARKET_PRIVATE_KEY"],
    wallet=os.environ.get("POLYMARKET_WALLET_ADDRESS"),
) as secure_client:
    ...
Keep private keys and API credentials in your secret manager or local environment. Do not commit them to source control.

API Key Authentication

Configure an API key when the SDK needs to set up gasless wallet operations.
import os

from polymarket import AsyncSecureClient, RelayerApiKey


secure_client = await AsyncSecureClient.create(
    private_key=os.environ["POLYMARKET_PRIVATE_KEY"],
    wallet=os.environ.get("POLYMARKET_WALLET_ADDRESS"),
    api_key=RelayerApiKey(
        key=os.environ["POLYMARKET_RELAYER_API_KEY"],
        address=os.environ["POLYMARKET_RELAYER_API_KEY_ADDRESS"],
    ),
)
Builder API keys are supported for backwards compatibility with builders that still use them for gasless workflows. They are not used for order attribution. Use builder_code on orders for attribution.

Trading Setup

Before placing orders, make sure the authenticated wallet has the required trading approvals. If you use gasless wallet operations, configure API key authentication first.
From this point forward, snippets in Trading Setup, Trading, Position Lifecycle, and Wallet Operations submit real on-chain transactions or live orders against the configured environment when executed. Review each call before running it against a wallet that holds funds.
1

Check Gasless Readiness

Optionally check whether the deposit wallet for the authenticated EOA is already deployed. The result is informational — call setup_gasless_wallet() in the next step regardless, since it is idempotent on deployment and is the call that binds the client to the deposit wallet.
is_gasless_ready = await secure_client.is_gasless_ready()
2

Set Up Gasless Wallet

Always call setup_gasless_wallet() for gasless workflows. It deploys the deposit wallet if needed and returns a new client bound to the deposit wallet address. Close the previous client when you replace it.
gasless_client = await secure_client.setup_gasless_wallet()
await secure_client.close()
secure_client = gasless_client
3

Set Up Trading Approvals

Set up the approvals required for trading and wait for the setup transaction to complete.
handle = await secure_client.setup_trading_approvals()
outcome = await handle.wait()

# outcome.transaction_hash: TransactionHash

Trading

Use a secure client to create, sign, and submit orders. Limit orders specify the price and size you want to trade. Market orders execute against resting liquidity immediately. Order placement returns a discriminated response. Check response.ok before reading order details.

Place Orders

yes_token_id = market.outcomes.yes.token_id
if yes_token_id is None:
    raise RuntimeError("Market does not have a YES token id")

response = await secure_client.place_limit_order(
    token_id=yes_token_id,
    side="BUY",
    price="0.52",
    size="10",
)

if response.ok:
    # response.order_id: str
    ...
else:
    # response.code: OrderResponseErrorCode
    # response.message: str
    ...

Create, Then Post

Create signed orders separately when you want to review, store, or batch them before submitting.
yes_token_id = market.outcomes.yes.token_id
if yes_token_id is None:
    raise RuntimeError("Market does not have a YES token id")

order = await secure_client.create_limit_order(
    token_id=yes_token_id,
    side="BUY",
    price="0.52",
    size="10",
)

response = await secure_client.post_order(order)

if response.ok:
    # response.order_id: str
    ...
else:
    # response.code: OrderResponseErrorCode
    # response.message: str
    ...

Position Lifecycle

Use position lifecycle methods to split collateral into outcome tokens, merge complete sets back into collateral, or redeem resolved positions. These examples assume the secure client is configured with API key authentication as shown in API Key Authentication.
condition_id = market.condition_id
if condition_id is None:
    raise RuntimeError("Market does not have a condition id")

handle = await secure_client.split_position(
    condition_id=condition_id,
    amount=1,
)

outcome = await handle.wait()

# outcome.transaction_hash: TransactionHash

Wallet Operations

Use wallet operation methods for direct token movements from the authenticated wallet. Amounts are in base units. These examples assume the secure client is configured with API key authentication as shown in API Key Authentication.
import os

handle = await secure_client.transfer_erc20(
    token_address=secure_client.environment.collateral_token,
    recipient_address=os.environ["POLYMARKET_RECIPIENT_ADDRESS"],
    amount=1_000_000,
)

outcome = await handle.wait()

# outcome.transaction_hash: TransactionHash

Order Management

Manage open orders for the authenticated wallet after placement. These examples assume order_id comes from an accepted order response.
order = await secure_client.get_order(order_id=order_id)

# order: OpenOrder

Rewards and Scoring

Use rewards methods to inspect active reward programs and scoring methods to check whether orders are eligible for scoring. list_current_rewards and list_market_rewards are public reads and are also available on AsyncPublicClient / PublicClient; get_order_scoring and get_orders_scoring require a secure client because they read account-scoped order data.
rewards = secure_client.list_current_rewards()

async for page in rewards:
    # page.items: tuple[CurrentReward, ...]
    ...

Account Data

Secure clients read account-scoped data for the authenticated wallet by default. Methods that take a user= parameter (positions, portfolio value, activity) accept a different wallet address to read its data instead.
positions = secure_client.list_positions(
    market=[market.id],
    page_size=10,
)

async for page in positions:
    # page.items: tuple[Position, ...]
    ...

Authentication Sessions

Secure clients expose the API credentials created for the authenticated session. Store them securely if you want to reuse the session later without producing a new authentication signature while the credentials remain valid.
import os

from polymarket import AsyncSecureClient


secure_client = await AsyncSecureClient.create(
    private_key=os.environ["POLYMARKET_PRIVATE_KEY"],
    wallet=os.environ.get("POLYMARKET_WALLET_ADDRESS"),
)

saved_credentials = secure_client.credentials.model_dump(mode="json")