Skip to main content

Pandas API client to Polymarket

Project description

polymarket-pandas

Pandas-native Python client for the full Polymarket API surface — REST, WebSocket, Relayer, and Bridge — with automatic type coercion and DataFrame output.


Installation

pip install polymarket-pandas
# or
uv add polymarket-pandas

For on-chain CTF operations (merge, split, redeem positions):

pip install "polymarket-pandas[ctf]"

For the interactive Streamlit explorer dashboard:

pip install "polymarket-pandas[explorer]"

For the MCP server (Claude Code, Claude Desktop, etc.):

pip install "polymarket-pandas[mcp]"

MCP Server

Query Polymarket data from any MCP client with 74 tools covering the full API surface.

# Run the server (stdio transport)
polymarket-mcp

# SSE transport
polymarket-mcp --sse

Claude Code / Claude Desktop Setup

Add to your MCP settings (~/.claude/settings.json or Claude Desktop config):

{
  "mcpServers": {
    "polymarket": {
      "command": "polymarket-mcp"
    }
  }
}

Environment Variables

Variable Purpose
POLYMARKET_ADDRESS Wallet address for position/trade queries
POLYMARKET_PRIVATE_KEY Private key for order signing
POLYMARKET_API_KEY L2 API key for private endpoints
POLYMARKET_API_SECRET L2 API secret
POLYMARKET_API_PASSPHRASE L2 API passphrase
POLYMARKET_MCP_MAX_ROWS Default max rows in table output (default 200)

Available Tools (74)

Discovery: search_markets, get_markets, get_market_by_slug/id, get_events, get_event_by_slug/id, get_tags, get_tag_by_slug/id, get_related_tags, get_market_tags, get_event_tags, get_series, get_series_by_id, get_teams, get_comments, get_comment_by_id, get_comments_by_user, get_profile, get_sports_metadata, get_sports_market_types

Pricing: get_orderbook, get_midpoint_price, get_spread, get_last_trade_price, get_tick_size, get_neg_risk, get_fee_rate, get_market_price, get_price_history, get_builder_trades, get_rebates, get_server_time

Data: get_positions, get_closed_positions, get_market_positions, get_top_holders, get_positions_value, get_trades, get_user_activity, get_leaderboard, get_builder_leaderboard, get_builder_volume, get_accounting_snapshot, get_open_interest, get_live_volume, get_traded_markets_count

Rewards: get_rewards_markets_current, get_rewards_markets_multi, get_rewards_market, get_rewards_earnings, get_rewards_earnings_total, get_rewards_percentages, get_rewards_user_markets

Private: get_balance_allowance, get_user_trades, get_active_orders, get_order, get_order_scoring, get_api_keys, send_heartbeat

Write: build_order, place_order, cancel_order, cancel_orders, cancel_all_orders, cancel_orders_from_market, create_api_key, derive_api_key, delete_api_key

Bridge: get_bridge_supported_assets, get_bridge_transaction_status, get_bridge_quote


Interactive Explorer

Explore all public endpoints visually with the built-in Streamlit dashboard:

# Install with explorer support
pip install "polymarket-pandas[explorer]"

# Launch the dashboard
polymarket-explore

The explorer includes 10 pages covering Markets, Events, Series, Orderbook (depth chart), Prices (history + spot), Positions, Trades, Leaderboard, Rewards, and Bridge. Each page shows the raw DataFrame, an interactive Plotly chart, and the equivalent Python code to reproduce.


Quick Start

from polymarket_pandas import PolymarketPandas

client = PolymarketPandas()

# Get active markets
markets = client.get_markets(closed=False, limit=100)
print(markets[["slug", "volume24hr", "endDate"]].head())

# Get the order book for a token
book = client.get_orderbook("15871154585880608648...")
print(book)

Configuration

All credentials are read from environment variables (or a .env file via python-dotenv). You can also pass them directly as constructor arguments.

Env var Constructor kwarg Purpose
POLYMARKET_ADDRESS address Your proxy wallet address
POLYMARKET_PRIVATE_KEY private_key Private key for EIP-712 (L1) signing
POLYMARKET_FUNDER private_funder_key Funder private key
POLYMARKET_API_KEY _api_key CLOB API key
POLYMARKET_API_SECRET _api_secret CLOB API secret (used for L2 HMAC)
POLYMARKET_API_PASSPHRASE _api_passphrase CLOB API passphrase
POLYMARKET_BUILDER_API_KEY _builder_api_key Builder API key
POLYMARKET_BUILDER_API_SECRET _builder_api_secret Builder API secret
POLYMARKET_BUILDER_API_PASSPHRASE _builder_api_passphrase Builder API passphrase
POLYMARKET_RELAYER_API_KEY _relayer_api_key Relayer API key
POLYMARKET_RELAYER_API_KEY_ADDRESS _relayer_api_key_address Address owning the relayer key
POLYMARKET_RPC_URL rpc_url Polygon RPC URL (default: https://polygon-rpc.com)
HTTP_PROXY proxy_url HTTP proxy URL
# Explicit credentials
client = PolymarketPandas(
    address="0xYourAddress",
    private_key="0xYourKey",
    _api_key="your-api-key",
    _api_secret="your-secret",
    _api_passphrase="your-passphrase",
)

API Key setup

# Step 1 — derive or create CLOB API credentials from your wallet key
creds = client.derive_api_key()   # uses private_key from env
# creds = client.create_api_key()  # creates a new key

# Step 2 — set credentials on the client
client._api_key = creds["apiKey"]
client._api_secret = creds["secret"]
client._api_passphrase = creds["passphrase"]

DataFrames and type coercion

Every method that returns a list of objects returns a preprocessed pd.DataFrame:

  • Numeric columns → float64
  • ISO-8601 string timestamps → datetime64[ns, UTC]
  • Unix-ms integer timestamps → datetime64[ns, UTC]
  • Boolean-ish string columns → bool
  • JSON-encoded string columns (clobTokenIds, outcomes, outcomePrices) → Python objects

Raw dict returns are used only where a single object is expected (e.g. get_market_by_id).

Typed returns

All dict-returning endpoints use TypedDicts for IDE autocomplete and type safety:

from polymarket_pandas import (
    SamplingMarketsCursorPage, OrdersCursorPage,
    SignedOrder, SendOrderResponse, TransactionReceipt,
)

# Every cursor-paginated endpoint has a specific type with typed data
page = client.get_sampling_markets()   # SamplingMarketsCursorPage
page["data"]         # DataFrame[SamplingMarketSchema] — IDE knows columns
page["next_cursor"]  # str

orders = client.get_active_orders()    # OrdersCursorPage
orders["data"]       # DataFrame[ActiveOrderSchema]

order: SignedOrder = client.build_order(token_id, 0.55, 10, "BUY")
order["makerAmount"]  # str — IDE knows all 13 fields

All DataFrame-returning endpoints use pandera DataFrameModels (22 schemas) for column documentation:

from polymarket_pandas import MarketSchema, PositionSchema, OrderbookSchema

# Annotation-only — no runtime validation overhead
markets = client.get_markets()  # DataFrame[MarketSchema]
# Users who want validation: MarketSchema.validate(markets)

All schemas use strict=False (extra columns allowed) so API changes don't break validation. Field names verified against the official Polymarket OpenAPI specs. All 11 cursor-paginated endpoints have specific CursorPage types (e.g. OrdersCursorPage, UserTradesCursorPage) that inherit from a CursorPage base and specify data: DataFrame[Schema].


REST API Reference

Gamma API — Markets

get_markets(**kwargs) → pd.DataFrame

List markets with rich filtering.

df = client.get_markets(
    limit=500,                  # rows per page (default 500)
    offset=0,
    closed=False,               # exclude resolved markets
    tag_id=1337,
    liquidity_num_min=1000,
    end_date_min="2025-01-01",
    expand_clob_token_ids=True, # explode multi-outcome markets (default True)
    expand_events=True,         # inline event columns (default True)
    expand_series=True,         # inline series columns (default True)
)
Full parameter list
Parameter Type Description
limit int Rows per page (default 500)
offset int Pagination offset
order list[str] Fields to sort by
ascending bool Sort direction
id list[int] Filter by gamma market IDs
slug list[str] Filter by slugs
clob_token_ids list[str] Filter by CLOB token IDs
condition_ids list[str] Filter by condition IDs
market_maker_address list[str] Filter by market maker
liquidity_num_min/max float Liquidity range filter
volume_num_min/max float Volume range filter
start_date_min/max str|Timestamp Start date range
end_date_min/max str|Timestamp End date range
tag_id int Filter by tag
related_tags bool Include related tags
cyom bool CYOM markets only
uma_resolution_status str UMA resolution filter
game_id str Sports game ID
sports_market_types list[str] Sports market types
rewards_min_size float Minimum reward size
question_ids list[str] Filter by question IDs
closed bool Include/exclude closed markets
expand_clob_token_ids bool Explode multi-outcome rows (default True)
expand_events bool Inline event fields (default True)
expand_series bool Inline series fields (default True)

get_markets_all(**kwargs) → pd.DataFrame

Auto-paginate through all matching markets.

all_markets = client.get_markets_all(closed=False)

get_market_by_id(id, include_tag=None) → dict

market = client.get_market_by_id(12345)

get_market_by_slug(slug, include_tag=None) → dict

market = client.get_market_by_slug("will-trump-win-2024")

get_market_tags(id) → pd.DataFrame

tags = client.get_market_tags(12345)

Gamma API — Sampling / Simplified Markets (CLOB)

These three endpoints are served by the CLOB server and use cursor-based pagination. Each method returns a dict with keys data (DataFrame), next_cursor, count, limit. The corresponding _all() variants auto-paginate.

get_sampling_markets(next_cursor=None) → dict

Markets currently eligible for liquidity-provider rewards. Full market objects including tokens, rewards, minimum_tick_size, etc.

page = client.get_sampling_markets()
df   = page["data"]
# fetch next page
page2 = client.get_sampling_markets(next_cursor=page["next_cursor"])

get_simplified_markets(next_cursor=None) → dict

Lightweight snapshot of all CLOB markets: condition_id, tokens (price/outcome), rewards, active, closed, archived, accepting_orders.

page = client.get_simplified_markets()
# auto-page all
all_simplified = client.get_simplified_markets_all()

get_sampling_simplified_markets(next_cursor=None) → dict

Intersection of sampling and simplified — lightest payload for reward-eligible markets.

all_sampling = client.get_sampling_simplified_markets_all()

Gamma API — Events

get_events(**kwargs) → pd.DataFrame

events = client.get_events(
    closed=False,
    tag_id=1337,
    limit=300,
    expand_markets=True,            # inline market columns (default True)
    expand_clob_token_ids=True,     # explode token rows (default True)
)

get_events_all(**kwargs) → pd.DataFrame

Auto-paginate all matching events.

get_event_by_id(id, include_chat=None, include_template=None) → dict

get_event_by_slug(slug, ...) → dict

get_event_tags(id) → pd.DataFrame


Gamma API — Tags

get_tags(**kwargs) → pd.DataFrame

tags = client.get_tags(limit=300)

get_tags_all(**kwargs) → pd.DataFrame

get_tag_by_id(id, include_template=None) → dict

get_tag_by_slug(slug, include_template=None) → dict

get_related_tags_by_tag_id(id, omit_empty=None, status=None) → pd.DataFrame

get_related_tags_by_tag_slug(slug, omit_empty=None, status=None) → pd.DataFrame


Gamma API — Series

get_series(**kwargs) → pd.DataFrame

series = client.get_series(
    closed=False,
    expand_events=True,
    expand_event_tags=False,
)

get_series_all(**kwargs) → pd.DataFrame

Auto-paginate all matching series. Works with expand_events=True.

get_series_by_id(id, include_chat=None) → dict


Gamma API — Sports

get_sports_metadata(**kwargs) → pd.DataFrame

meta = client.get_sports_metadata(sport="NFL")

get_sports_market_types() → dict

get_teams(**kwargs) → pd.DataFrame

teams = client.get_teams(league=["NFL", "NBA"])

get_teams_all(**kwargs) → pd.DataFrame


Gamma API — Comments

get_comments(**kwargs) → pd.DataFrame

comments = client.get_comments(
    parent_entity_type="Event",
    parent_entity_id=12345,
    limit=100,
)

get_comments_all(**kwargs) → pd.DataFrame

get_comments_by_user_address(user_address, **kwargs) → pd.DataFrame

get_comments_by_user_address_all(user_address, **kwargs) → pd.DataFrame

get_comment_by_id(id, get_positions=None) → dict


Gamma API — Search

search_markets_events_profiles(q, **kwargs) → dict

results = client.search_markets_events_profiles("bitcoin", limit_per_type=10)
# results keys: "markets", "events", "profiles"

Gamma API — Profiles

get_profile(address) → dict

profile = client.get_profile("0xYourAddress")

Data API — Positions & Trades

get_positions(user, **kwargs) → pd.DataFrame

positions = client.get_positions(
    user="0xYourAddress",
    sizeThreshold=1,
    redeemable=False,
    limit=100,
    sortBy="TOKENS",
    sortDirection="DESC",
)

get_closed_positions(user, **kwargs) → pd.DataFrame

closed = client.get_closed_positions(
    user="0xYourAddress",
    sortBy="REALIZEDPNL",
    limit=50,
)

get_market_positions(market, **kwargs) → pd.DataFrame

All positions for a market, across traders.

pos = client.get_market_positions(
    market="0xConditionId...",
    status="OPEN",
    sortBy="TOTAL_PNL",
    limit=50,
)

get_positions_all(user, **kwargs) → pd.DataFrame

get_closed_positions_all(user, **kwargs) → pd.DataFrame

get_market_positions_all(market, **kwargs) → pd.DataFrame

get_top_holders(market, limit=100, minBalance=1) → pd.DataFrame

holders = client.get_top_holders(market=["0xConditionId..."])

get_positions_value(user, market=None) → pd.DataFrame

get_leaderboard(**kwargs) → pd.DataFrame

get_leaderboard_all(**kwargs) → pd.DataFrame

lb = client.get_leaderboard(
    category="CRYPTO",
    timePeriod="WEEK",
    orderBy="PNL",
    limit=25,
)

get_trades(**kwargs) → pd.DataFrame

get_trades_all(**kwargs) → pd.DataFrame

trades = client.get_trades(
    user="0xYourAddress",
    market=["0xConditionId..."],
    limit=100,
)

get_user_activity(user, **kwargs) → pd.DataFrame

get_user_activity_all(user, **kwargs) → pd.DataFrame

activity = client.get_user_activity(
    user="0xYourAddress",
    type=["TRADE", "REDEEM"],
    limit=100,
)

Data API — Misc

get_accounting_snapshot(user) → dict[str, pd.DataFrame]

Downloads and parses the ZIP accounting snapshot.

snapshot = client.get_accounting_snapshot("0xYourAddress")
positions_df = snapshot["positions"]
equity_df    = snapshot["equity"]

get_live_volume(id) → dict

vol = client.get_live_volume(id=12345)

get_open_interest(market=None) → dict

oi = client.get_open_interest(market=["0xConditionId..."])

get_traded_markets_count(user) → dict

count = client.get_traded_markets_count("0xYourAddress")

Data API — Builders

get_builder_leaderboard(timePeriod="DAY", limit=25, offset=0) → pd.DataFrame

get_builder_leaderboard_all(**kwargs) → pd.DataFrame

lb = client.get_builder_leaderboard(timePeriod="WEEK")

get_builder_volume(timePeriod="DAY") → pd.DataFrame

vol = client.get_builder_volume(timePeriod="ALL")

CLOB API — Market Data (public)

get_server_time() → int

get_tick_size(token_id) → float

get_neg_risk(token_id) → bool

get_fee_rate(token_id=None) → int

Returns the base fee in basis points.

get_orderbook(token_id) → pd.DataFrame

book = client.get_orderbook("15871154585880...")
# columns: price, size, side (bids/asks), asset_id, hash, market, ...

get_orderbooks(data) → pd.DataFrame

POST endpoint — data is a DataFrame with token_id and side columns.

get_market_price(token_id, side) → float

price = client.get_market_price("15871...", side="BUY")

get_market_prices(token_sides) → pd.DataFrame

prices = client.get_market_prices([
    {"token_id": "15871...", "side": "BUY"},
    {"token_id": "15871...", "side": "SELL"},
])

get_midpoint_price(token_id) → float

get_midpoints(token_ids) → pd.DataFrame

mids = client.get_midpoints(["15871...", "99182..."])

get_spread(token_id) → float

get_last_trade_price(token_id) → LastTradePrice

get_last_trade_prices(data) → pd.DataFrame

get_price_history(market, **kwargs) → pd.DataFrame

history = client.get_price_history(
    market="15871...",
    interval="1w",   # "1m", "1w", "1d", "6h", "1h", "max"
    fidelity=60,     # resolution in minutes
)

CLOB API — Private (L2 auth required)

All private endpoints use HMAC-SHA256 (_api_key, _api_secret, _api_passphrase).

get_balance_allowance(asset_type, token_id=None) → BalanceAllowance

# asset_type 0 = USDC collateral, 1 = conditional token
balance = client.get_balance_allowance(asset_type=0)
# balance["balance"], balance["allowances"]

get_user_trades(**kwargs) → CursorPage

Cursor-paginated. Returns {"data": DataFrame, "next_cursor": str, "count": int, "limit": int}.

page = client.get_user_trades(market="0xConditionId...")
trades = page["data"]
# Auto-paginate all:
all_trades = client.get_user_trades_all(market="0xConditionId...")

get_order(order_id) → dict

get_active_orders(**kwargs) → CursorPage

Cursor-paginated. Returns {"data": DataFrame, "next_cursor": str, "count": int, "limit": int}.

page = client.get_active_orders(market="0xConditionId...")
orders = page["data"]
# Auto-paginate all:
all_orders = client.get_active_orders_all(market="0xConditionId...")

get_order_scoring(order_id) → bool

place_order(order, owner, orderType) → SendOrderResponse

result = client.place_order(
    order=signed_order_dict,
    owner="your-api-key",
    orderType="GTC",   # "GTC", "GTD", "FOK", "FAK"
)

place_orders(orders) → pd.DataFrame

Batch place up to 15 orders. orders is a DataFrame with order fields plus owner and orderType columns.

cancel_order(order_id) → CancelOrdersResponse

cancel_orders(order_ids) → CancelOrdersResponse

result = client.cancel_orders(["order-id-1", "order-id-2"])
# result["canceled"], result["not_canceled"]

cancel_all_orders() → CancelOrdersResponse

cancel_orders_from_market(market=None, asset_id=None) → CancelOrdersResponse

send_heartbeat() → dict

Must be called at least every 10 seconds while orders are open to prevent automatic cancellation.


Order Building & Submission

Build, sign (EIP-712), and place orders. Market parameters (neg_risk, tick_size, fee_rate_bps) are auto-fetched from the CLOB API and cached per token_id — you only need to provide token_id, price, size, and side.

Caching: get_tick_size uses a 300-second TTL (tick sizes can change mid-market); get_neg_risk and get_fee_rate are cached permanently.

build_order(token_id, price, size, side, **kwargs) → SignedOrder

Build and EIP-712-sign a CLOB order. Returns a typed dict ready for place_order().

order = client.build_order(
    token_id="15871154585880...",
    price=0.55,
    size=10.0,
    side="BUY",
    # All optional — auto-fetched if omitted:
    # neg_risk=False,
    # tick_size="0.01",
    # fee_rate_bps=1000,
    # expiration=0,       # 0 = no expiry (GTC)
    # nonce=0,
)

# Expiration accepts int (Unix seconds), pd.Timestamp, or ISO-8601 string:
gtd_order = client.build_order(
    token_id="15871...", price=0.55, size=10, side="BUY",
    expiration=pd.Timestamp("2025-12-31T23:59:59Z"),  # auto-converted to int
)

submit_order(token_id, price, size, side, order_type="GTC", **kwargs) → SendOrderResponse

Build, sign, and place a single order in one call.

result = client.submit_order(
    token_id="15871154585880...",
    price=0.55,
    size=10.0,
    side="BUY",
    order_type="GTC",  # "GTC", "GTD", "FOK", "FAK"
)
# result: {"orderID": "0x...", "status": "matched", "takingAmount": "10", ...}

submit_orders(orders: pd.DataFrame) → pd.DataFrame

Build, sign, and batch-submit orders from a DataFrame. Orders are sent in groups of 15 (the CLOB batch limit) via the /orders endpoint.

import pandas as pd

orders_df = pd.DataFrame({
    "token_id": [up_token_id, down_token_id],
    "price": [0.55, 0.45],
    "size": [10, 10],
    "side": ["BUY", "BUY"],
})
responses = client.submit_orders(orders_df)
DataFrame columns
Column Required Default Description
token_id Yes CLOB token ID
price Yes Limit price (0–1)
size Yes Number of shares
side Yes "BUY" or "SELL"
order_type No "GTC" "GTC", "GTD", "FOK", "FAK"
neg_risk No auto Fetched from CLOB API
tick_size No auto Fetched from CLOB API
fee_rate_bps No auto Fetched from CLOB API
expiration No 0 Unix timestamp, pd.Timestamp, or ISO string (0 = no expiry)
nonce No 0 Order nonce

CLOB API — Builder Trades (builder auth required)

Requires _builder_api_key, _builder_api_secret, _builder_api_passphrase.

get_builder_trades(**kwargs) → dict

Returns a cursor-paginated dict (data DataFrame + next_cursor).

page = client.get_builder_trades(
    builder="0199bfa0-f4c1-7a98-9c2b-b29cc6d39e10",
    market="0xConditionId...",
    after="1700000000",
)
df = page["data"]
# columns: id, tradeType, takerOrderHash, builder, market, assetId,
#          side, size, sizeUsdc, price, status, outcome, fee, matchTime, ...

CLOB API — Rebates (public)

get_rebates(date, maker_address) → pd.DataFrame

rebates = client.get_rebates(
    date="2026-02-27",
    maker_address="0xYourMakerAddress",
)
# columns: date, condition_id, asset_address, maker_address, rebated_fees_usdc

CLOB API — API Key Management (L1 auth)

Requires private_key (EIP-712 signing).

create_api_key(nonce=0) → ApiCredentials

derive_api_key(nonce=0) → ApiCredentials

get_api_keys() → pd.DataFrame

delete_api_key() → dict


Relayer API

Base URL: https://relayer-v2.polymarket.com

check_safe_deployed(address) → bool

deployed = client.check_safe_deployed("0xProxyAddress")

get_relayer_transaction(id) → list[dict]

txs = client.get_relayer_transaction("0190b317-a1d3-7bec-9b91-eeb6dcd3a620")

Each transaction has: transactionID, transactionHash, from, to, proxyAddress, data, nonce, value, signature, state (STATE_NEW | STATE_EXECUTED | STATE_MINED | STATE_CONFIRMED | STATE_INVALID | STATE_FAILED), type (SAFE | PROXY), owner, metadata, createdAt, updatedAt.

get_relayer_nonce(address, type) → str

nonce = client.get_relayer_nonce("0xSignerAddress", type="PROXY")

get_relayer_transactions() → pd.DataFrame

Requires relayer credentials. Returns recent transactions for the authenticated account.

get_relay_payload(address, type) → RelayPayload

Returns {"address": "<relayer_address>", "nonce": "<nonce>"} — needed to construct a transaction before signing.

payload = client.get_relay_payload("0xSignerAddress", type="PROXY")

submit_transaction(...) → SubmitTransactionResponse

result = client.submit_transaction(
    from_="0xSignerAddress",
    to="0xContractAddress",
    proxy_wallet="0xProxyWallet",
    data="0xEncodedCalldata",
    nonce="31",
    signature="0xSignatureHex",
    type="PROXY",
    signature_params={
        "gasPrice": "100000000000",
        "operation": "0",
        "safeTxnGas": "0",
        "baseGas": "0",
        "gasToken": "0x0000000000000000000000000000000000000000",
        "refundReceiver": "0x0000000000000000000000000000000000000000",
    },
)
# result: {"transactionID": str, "transactionHash": str, "state": str}

Relayer API Keys

Requires _relayer_api_key and _relayer_api_key_address.

get_relayer_api_keys() → pd.DataFrame

keys = client.get_relayer_api_keys()
# columns: apiKey, address, createdAt, updatedAt

Bridge API

Base URL: https://bridge.polymarket.com

get_bridge_supported_assets() → list[dict]

assets = client.get_bridge_supported_assets()
# Each item: {chainId, chainName, token: {name, symbol, address, decimals}, minCheckoutUsd}

get_bridge_quote(...) → dict

Get a price estimate before bridging.

quote = client.get_bridge_quote(
    from_amount_base_unit="1000000",        # 1 USDC (6 decimals)
    from_chain_id="1",                      # Ethereum
    from_token_address="0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
    recipient_address="0xYourPolymarketWallet",
    to_chain_id="137",                      # Polygon
    to_token_address="0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174",
)
# quote keys: quoteId, estCheckoutTimeMs, estInputUsd, estOutputUsd,
#             estToTokenBaseUnit, estFeeBreakdown (appFeeUsd, gasUsd, ...)

create_deposit_address(address) → BridgeAddress

Create multi-chain deposit addresses. Send funds to the returned address to have USDC.e credited to your Polymarket wallet.

result = client.create_deposit_address("0xYourPolymarketWallet")
# result: {"address": {"evm": "0x...", "svm": "...", "btc": "bc1q..."}, "note": "..."}

create_withdrawal_address(address, to_chain_id, to_token_address, recipient_addr) → BridgeAddress

Bridge funds out of Polymarket to another chain.

result = client.create_withdrawal_address(
    address="0xYourPolymarketWallet",
    to_chain_id="1",                    # Ethereum mainnet
    to_token_address="0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
    recipient_addr="0xRecipientOnEthereum",
)

get_bridge_transaction_status(address) → pd.DataFrame

Poll for transaction status using an address returned by deposit/withdraw.

status = client.get_bridge_transaction_status("0xDepositAddress...")
# columns: fromChainId, fromTokenAddress, fromAmountBaseUnit,
#          toChainId, toTokenAddress, status, txHash, createdTimeMs
# status values: DEPOSIT_DETECTED | PROCESSING | ORIGIN_TX_CONFIRMED |
#                SUBMITTED | COMPLETED | FAILED

CTF — On-Chain Operations

On-chain merge, split, and redeem via Polymarket's Conditional Token Framework contracts on Polygon. Requires the [ctf] extra: pip install "polymarket-pandas[ctf]".

All methods require private_key (for signing transactions) and a funded EOA wallet (MATIC for gas). Amounts are in USDC.e base units (6 decimals): 1_000_000 = 1.00 USDC.

split_position(condition_id, amount, neg_risk=False, wait=True, timeout=120) → TransactionReceipt

Split USDC.e collateral into Yes + No outcome tokens.

result = client.split_position(
    condition_id="0x4aee6d11...",
    amount=1_000_000,       # 1.00 USDC
    neg_risk=False,         # True for neg-risk (multi-outcome) markets
)
# result: {"txHash": "0x...", "status": 1, "blockNumber": 12345, "gasUsed": 150000}

merge_positions(condition_id, amount, neg_risk=False, wait=True, timeout=120) → TransactionReceipt

Merge equal amounts of Yes + No tokens back into USDC.e.

result = client.merge_positions(
    condition_id="0x4aee6d11...",
    amount=1_000_000,
)

redeem_positions(condition_id, index_sets=None, wait=True, timeout=120) → TransactionReceipt

Redeem winning outcome tokens for USDC.e after market resolution.

result = client.redeem_positions(condition_id="0x4aee6d11...")

approve_collateral(spender=None, amount=None, wait=True, timeout=120) → TransactionReceipt

Approve a CTF contract to spend USDC.e. Required before split_position or merge_positions. Defaults to unlimited approval for the ConditionalTokens contract.

from polymarket_pandas.mixins._ctf import CONDITIONAL_TOKENS, NEG_RISK_ADAPTER

# For standard binary markets
client.approve_collateral(spender=CONDITIONAL_TOKENS)

# For neg-risk markets
client.approve_collateral(spender=NEG_RISK_ADAPTER)

WebSocket API

from polymarket_pandas import PolymarketWebSocket, PolymarketPandas

ws = PolymarketWebSocket()
# Or share config with an existing HTTP client:
# ws = PolymarketWebSocket.from_client(client)

Market Channel

Real-time order book, price, and trade updates for one or more tokens.

def on_book(df):
    print("Book update:", df[["price", "size", "side"]])

def on_price_change(df):
    print("Price change:", df[["price", "size"]])

def on_best_bid_ask(df):
    print("BBA:", df[["best_bid", "best_ask"]])

session = ws.market_channel(
    asset_ids=["15871154585880..."],
    on_book=on_book,
    on_price_change=on_price_change,
    on_best_bid_ask=on_best_bid_ask,
    # on_last_trade_price=...,
    # on_tick_size_change=...,
    # on_new_market=...,
    # on_market_resolved=...,
    # on_message=lambda event_type, payload: ...,  # catch-all
    ping_interval=10,
)
session.run_forever()

Subscribe / unsubscribe additional tokens after connection:

session.subscribe(["99182..."])
session.unsubscribe(["15871..."])

User Channel

Private order and trade events. Requires API key credentials.

ws = PolymarketWebSocket(
    api_key="your-key",
    api_secret="your-secret",
    api_passphrase="your-passphrase",
)

def on_trade(df):
    print("Trade:", df)

def on_order(df):
    print("Order update:", df)

session = ws.user_channel(
    markets=["0xConditionId..."],
    on_trade=on_trade,
    on_order=on_order,
)
session.run_forever()

Sports Channel

Live sports resolution events.

def on_sport_result(df):
    print("Result:", df)

session = ws.sports_channel(on_sport_result=on_sport_result)
session.run_forever()

RTDS Channel (Real-Time Data Streams)

Crypto prices (Binance and Chainlink) and market comments.

def on_crypto_prices(df):
    print("Binance price:", df[["price"]])

def on_crypto_prices_chainlink(df):
    print("Chainlink price:", df[["price"]])

session = ws.rtds_channel(
    subscriptions=[{"type": "crypto_prices", "condition_id": "0x..."}],
    on_crypto_prices=on_crypto_prices,
    on_crypto_prices_chainlink=on_crypto_prices_chainlink,
    ping_interval=5,
)
session.run_forever()

Async Client

AsyncPolymarketPandas wraps the sync client, running each method in a thread pool for non-blocking behavior in asyncio contexts. All 100+ public methods are available as async def.

from polymarket_pandas import AsyncPolymarketPandas

async with AsyncPolymarketPandas() as client:
    markets = await client.get_markets(closed=False, limit=100)
    book = await client.get_orderbook(token_id)
    tick = await client.get_tick_size(token_id)

    # Order building and submission work the same way
    result = await client.submit_order(
        token_id=token_id, price=0.55, size=10, side="BUY"
    )

Accepts the same constructor arguments as PolymarketPandas, plus max_workers (default 10) for the thread pool size.


Async WebSocket

AsyncPolymarketWebSocket uses the websockets library for native async I/O with async for iteration and automatic reconnection.

from polymarket_pandas import AsyncPolymarketWebSocket

ws = AsyncPolymarketWebSocket.from_client(client)  # works with sync or async client

session = ws.market_channel(asset_ids=["15871..."])
async with session:
    async for event_type, payload in session:
        if event_type == "book":
            print(payload[["price", "size", "side"]])
        elif event_type == "price_change":
            print(payload)

Features

  • async for iteration over parsed messages
  • Auto-reconnection with exponential backoff (configurable via reconnect=True)
  • Async subscribe/unsubscribe on live connections
  • Async context manager (async with session:)
  • Channels: market_channel, user_channel, sports_channel, rtds_channel
# Dynamic subscription management
async with session:
    await session.subscribe(["99182..."])
    await session.unsubscribe(["15871..."])

Pagination Helpers

Offset-based (_autopage)

Every offset-paginated endpoint has a corresponding _all() method that auto-increments offset and concatenates pages. Works correctly with expand_* flags.

# Discovery (Gamma API)
all_markets  = client.get_markets_all(closed=False, expand_events=True)
all_events   = client.get_events_all(closed=False, tag_id=1337)
all_series   = client.get_series_all(expand_events=True)
all_tags     = client.get_tags_all()
all_teams    = client.get_teams_all(league=["NFL"])
all_comments = client.get_comments_all(parent_entity_type="event")

# Data API
all_positions = client.get_positions_all(user="0x...")
all_closed    = client.get_closed_positions_all(user="0x...")
all_mkt_pos   = client.get_market_positions_all(market="0xTokenId...")
all_trades    = client.get_trades_all(market=["0xTokenId..."])
all_activity  = client.get_user_activity_all(user="0x...")
all_lb        = client.get_leaderboard_all(timePeriod="WEEK")
all_builder   = client.get_builder_leaderboard_all()

# Limit pages
first_5 = client.get_markets_all(max_pages=5)

Cursor-based (_autopage_cursor)

# Fetch ALL sampling/simplified markets
all_sampling    = client.get_sampling_markets_all()
all_simplified  = client.get_simplified_markets_all()
all_samp_simple = client.get_sampling_simplified_markets_all()

# User trades and active orders (CLOB private, cursor-paginated)
all_trades = client.get_user_trades_all(market="0xConditionId...")
all_orders = client.get_active_orders_all(market="0xConditionId...")

# Rewards
all_rewards = client.get_rewards_markets_current_all()
all_earnings = client.get_rewards_earnings_all(date="2026-03-30")

# Limit pages
first_3 = client.get_simplified_markets_all(max_pages=3)

Utility Methods

preprocess_dataframe(data) → pd.DataFrame

Apply the full type-coercion pipeline to any raw DataFrame.

response_to_dataframe(data) → pd.DataFrame

pd.DataFrame(data) + preprocess_dataframe.

orderbook_to_dataframe(data) → pd.DataFrame

Parse a raw order book dict (with bids / asks arrays) into a flat DataFrame.


Examples

See the examples/ directory for runnable scripts:

Script Description
btc_5min.py Find BTC 5-min market, fetch orderbook and prices
btc_5min_trade_merge.py Full trading flow: submit_orders (DataFrame), cancel, buy both sides, merge positions
rtds_ws.py Real-Time Data Streams — live crypto prices via WebSocket
user_ws.py Private user channel — live order and trade events

Environment Variables Reference

# Wallet / signing
POLYMARKET_ADDRESS=0xYourProxyWallet
POLYMARKET_PRIVATE_KEY=0xYourPrivateKey
POLYMARKET_FUNDER=0xFunderKey

# CLOB API (L2)
POLYMARKET_API_KEY=your-clob-api-key
POLYMARKET_API_SECRET=your-clob-api-secret
POLYMARKET_API_PASSPHRASE=your-clob-passphrase

# Builder API
POLYMARKET_BUILDER_API_KEY=your-builder-key
POLYMARKET_BUILDER_API_SECRET=your-builder-secret
POLYMARKET_BUILDER_API_PASSPHRASE=your-builder-passphrase

# Relayer API
POLYMARKET_RELAYER_API_KEY=your-relayer-key
POLYMARKET_RELAYER_API_KEY_ADDRESS=0xAddressThatOwnsRelayerKey

# Network
POLYMARKET_RPC_URL=https://polygon-bor-rpc.publicnode.com  # Polygon RPC for CTF ops
HTTP_PROXY=http://proxy:8080                                # HTTP proxy (optional)

License

Apache-2.0

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

polymarket_pandas-0.6.17.tar.gz (118.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

polymarket_pandas-0.6.17-py3-none-any.whl (102.6 kB view details)

Uploaded Python 3

File details

Details for the file polymarket_pandas-0.6.17.tar.gz.

File metadata

  • Download URL: polymarket_pandas-0.6.17.tar.gz
  • Upload date:
  • Size: 118.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for polymarket_pandas-0.6.17.tar.gz
Algorithm Hash digest
SHA256 1f9f75f4cc680b1dcc2b1187d8017ddce9917164ff6791a0cb4357fd8f82e0bf
MD5 b8db8fbb089e154e3f4fc936b967dc47
BLAKE2b-256 3a65dcbaa9eea104d6f55164b93c4f29969a88ff5c4a92743fc62fb590f542bc

See more details on using hashes here.

Provenance

The following attestation bundles were made for polymarket_pandas-0.6.17.tar.gz:

Publisher: release.yml on sigma-quantiphi/polymarket-pandas

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file polymarket_pandas-0.6.17-py3-none-any.whl.

File metadata

File hashes

Hashes for polymarket_pandas-0.6.17-py3-none-any.whl
Algorithm Hash digest
SHA256 726a56386032f42da29d59b4b167deb392f22d0b3d34d625c5ebc33fd9dadeb9
MD5 913ea5a061c50030fadf16ed89ebdba5
BLAKE2b-256 d3c69f0c0cdbabf4db93033be2146e745d0914e6b6f720dda32f2c96642acafe

See more details on using hashes here.

Provenance

The following attestation bundles were made for polymarket_pandas-0.6.17-py3-none-any.whl:

Publisher: release.yml on sigma-quantiphi/polymarket-pandas

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page