create_order() and post_order() are the py-clob-client-v2 methods that place trades on Polymarket. Order placement is a two-step process: first sign the order locally with your wallet, then submit it to the CLOB API. This page covers limit orders with OrderArgs, market orders with MarketOrderArgs, order types (GTC/GTD/FOK/FAK), the new options argument, batch orders, and common errors.

⚠️ CLOB V2 (live April 28, 2026). Imports come from py_clob_client_v2. Key changes for ordering: the BUY/SELL constants became the Side enum (Side.BUY); create_order() takes a second options argument (PartialCreateOrderOptions(tick_size=..., neg_risk=...)); OrderArgs/MarketOrderArgs no longer carry fee_rate_bps/nonce/taker (fees are protocol-set at match time); and there’s a new one-step create_and_post_market_order(). The V1 py-clob-client package no longer works on production — see Migrating to CLOB V2.

For the complete V2 method reference, see the py-clob-client-v2 Reference. For Kalshi’s equivalent order placement, see the Prediction Market API Reference.


The Two-Step Order Flow

Every order on Polymarket follows this pattern:

  1. Sign locallycreate_order() or create_market_order() signs the order with your private key, producing a SignedOrder
  2. Post to APIpost_order() submits the signed order to the CLOB for matching
# Step 1: Sign (options carries tick_size / neg_risk)
signed = client.create_order(order_args, options)

# Step 2: Post
response = client.post_order(signed, OrderType.GTC)

This design means your private key never leaves your machine — the API only receives the cryptographic signature. For straightforward trades, create_and_post_order() (limit) and create_and_post_market_order() (market) combine both steps into one call.


Limit Orders with OrderArgs

Signatures

client.create_order(order_args: OrderArgs, options: PartialCreateOrderOptions = None) -> SignedOrder
client.post_order(signed_order, order_type: OrderType = OrderType.GTC, post_only: bool = False, defer_exec: bool = False) -> dict

OrderArgs Fields

FieldTypeRequiredDescription
token_idstrYesThe outcome token ID (YES or NO token)
pricefloatYesPrice per share (0.01 to 0.99)
sizefloatYesNumber of shares to buy or sell
sideSideYesSide.BUY or Side.SELL (the V2 Side enum, imported from py_clob_client_v2)
expirationintNoUnix timestamp; 0 (default) = no expiration (GTC). Set a future ts and post as GTD
builder_codestrNobytes32 builder code for fee attribution
metadatastrNoOptional bytes32 metadata

V2: OrderArgs no longer takes fee_rate_bps, nonce, or taker. tick_size/neg_risk moved to the options argument of create_order().

post_order() Parameters

ParameterTypeDescription
signed_ordersigned orderThe signed order from create_order()
order_typeOrderTypeGTC, GTD, FOK, or FAK (see Order Types)
post_onlyboolIf True, rejects if the order would immediately match
defer_execboolIf True, defers matching-engine execution (advanced)

Example — Place a Limit Buy

from py_clob_client_v2 import ClobClient, OrderArgs, OrderType, PartialCreateOrderOptions, Side

client = ClobClient(
    host="https://clob.polymarket.com",
    chain_id=137,
    key="<your-private-key>",
)
client.set_api_creds(client.create_or_derive_api_key())

order = OrderArgs(
    token_id="<token-id>",
    price=0.45,
    size=20.0,
    side=Side.BUY,
)

signed = client.create_order(order, PartialCreateOrderOptions(tick_size="0.01"))
response = client.post_order(signed, OrderType.GTC)
print(f"Order ID: {response['orderID']}")
print(f"Status:   {response.get('status', 'submitted')}")

Example — Place a Limit Sell

from py_clob_client_v2 import OrderArgs, OrderType, PartialCreateOrderOptions, Side

order = OrderArgs(
    token_id="<token-id>",
    price=0.65,
    size=10.0,
    side=Side.SELL,
)

signed = client.create_order(order, PartialCreateOrderOptions(tick_size="0.01"))
response = client.post_order(signed, OrderType.GTC)

Example — Order with Expiration (GTD)

import time
from py_clob_client_v2 import OrderArgs, OrderType, PartialCreateOrderOptions, Side

order = OrderArgs(
    token_id="<token-id>",
    price=0.40,
    size=50.0,
    side=Side.BUY,
    expiration=int(time.time()) + 3600,  # Expires in 1 hour
)

signed = client.create_order(order, PartialCreateOrderOptions(tick_size="0.01"))
response = client.post_order(signed, OrderType.GTD)  # GTD = good-til-date

Market Orders with MarketOrderArgs

Market orders execute immediately against resting liquidity at the best available prices.

Signatures

client.create_market_order(order_args: MarketOrderArgs, options: PartialCreateOrderOptions = None) -> SignedOrder
client.create_and_post_market_order(order_args: MarketOrderArgs, options: PartialCreateOrderOptions = None, order_type: OrderType = OrderType.FOK) -> dict

MarketOrderArgs Fields

FieldTypeRequiredDescription
token_idstrYesThe outcome token ID
amountfloatYesFor BUY: USDC to spend. For SELL: number of shares to sell
sideSideYesSide.BUY or Side.SELL
order_typeOrderTypeNoOrderType.FOK (default) or OrderType.FAK
user_usdc_balancefloatNoYour collateral balance; lets the SDK fee-adjust market buys
builder_codestrNobytes32 builder code for fee attribution
metadatastrNoOptional bytes32 metadata

V2: MarketOrderArgs dropped fee_rate_bps (and nonce/taker). Fees are protocol-set at match time; the new user_usdc_balance field lets the SDK size fee-adjusted market buys.

Example — Buy $25 at Market Price (one-step)

from py_clob_client_v2 import MarketOrderArgs, OrderType, PartialCreateOrderOptions, Side

response = client.create_and_post_market_order(
    order_args=MarketOrderArgs(
        token_id="<token-id>",
        amount=25.0,
        side=Side.BUY,
        order_type=OrderType.FOK,
    ),
    options=PartialCreateOrderOptions(tick_size="0.01"),
    order_type=OrderType.FOK,
)
print(f"Order ID: {response['orderID']}")

Example — Sell 50 Shares at Market Price (two-step)

from py_clob_client_v2 import MarketOrderArgs, OrderType, PartialCreateOrderOptions, Side

sell_order = MarketOrderArgs(
    token_id="<token-id>",
    amount=50.0,
    side=Side.SELL,
    order_type=OrderType.FOK,
)

signed = client.create_market_order(sell_order, PartialCreateOrderOptions(tick_size="0.01"))
response = client.post_order(signed, OrderType.FOK)

Key difference: OrderArgs uses price + size (price per share × number of shares). MarketOrderArgs uses amount (total USDC to spend for buys, or shares to sell for sells).


Order Types: GTC, GTD, FOK, FAK

TypeConstantBehaviorUse Case
GTCOrderType.GTCRests on the order book until filled or canceledLimit orders — you want a specific price
GTDOrderType.GTDRests until a chosen expiration timestampLimit orders with a deadline
FOKOrderType.FOKMust fill entirely and immediately, or is rejectedMarket orders — you want instant execution
FAKOrderType.FAKFills what it can immediately, cancels the restPartial fills OK — you want whatever is available now
from py_clob_client_v2 import OrderType

# Limit order that rests on the book
client.post_order(signed, OrderType.GTC)

# Limit order that rests until its expiration timestamp
client.post_order(signed, OrderType.GTD)

# Market order that fills immediately or fails
client.post_order(signed, OrderType.FOK)

# Aggressive order — fill what you can, cancel rest
client.post_order(signed, OrderType.FAK)

Post-Only Orders (Market Makers)

Post-only orders are rejected if they would immediately match against resting orders. This guarantees you’re always the maker (providing liquidity), never the taker.

from py_clob_client_v2 import OrderArgs, OrderType, PartialCreateOrderOptions, Side

order = OrderArgs(
    token_id="<token-id>",
    price=0.49,
    size=100.0,
    side=Side.BUY,
)

signed = client.create_order(order, PartialCreateOrderOptions(tick_size="0.01"))
response = client.post_order(signed, OrderType.GTC, post_only=True)
# If the best ask is <= 0.49, this order is rejected
# Otherwise, it rests on the book at 0.49

Batch Orders

Place up to 15 orders in a single API call. Essential for market makers updating quotes on both sides.

Signature

client.post_orders(args: list, post_only: bool = False, defer_exec: bool = False) -> dict

In V2 the order type travels with each signed order, so post_orders() takes post_only/defer_exec flags rather than a single order_type for the whole batch.

Example — Ladder of Buy Orders

from py_clob_client_v2 import OrderArgs, PartialCreateOrderOptions, Side

opts = PartialCreateOrderOptions(tick_size="0.01")
orders = []
for price in [0.40, 0.42, 0.44, 0.46, 0.48]:
    order = OrderArgs(
        token_id="<token-id>",
        price=price,
        size=50.0,
        side=Side.BUY,
    )
    orders.append(client.create_order(order, opts))

response = client.post_orders(orders)
print(f"Batch submitted: {len(orders)} orders")

Example — Two-Sided Quotes

from py_clob_client_v2 import OrderArgs, PartialCreateOrderOptions, Side

opts = PartialCreateOrderOptions(tick_size="0.01")
signed_orders = []

# Buy side
for price in [0.48, 0.47, 0.46]:
    order = OrderArgs(token_id="<token-id>", price=price, size=100.0, side=Side.BUY)
    signed_orders.append(client.create_order(order, opts))

# Sell side
for price in [0.52, 0.53, 0.54]:
    order = OrderArgs(token_id="<token-id>", price=price, size=100.0, side=Side.SELL)
    signed_orders.append(client.create_order(order, opts))

response = client.post_orders(signed_orders)
print(f"Posted {len(signed_orders)} orders (3 bids + 3 asks)")

Tick Size Validation

Every Polymarket market has a tick_size — the minimum price increment. Orders that don’t align to the tick size are rejected. In CLOB V2 you pass it via the options argument: PartialCreateOrderOptions(tick_size="0.01"). Read a market’s tick size with client.get_tick_size(token_id) or from the tick_size field on the get_order_book() response.

Tick SizeValid PricesExample
0.10.1, 0.2, …, 0.9Coarse markets
0.010.01, 0.02, …, 0.99Most markets
0.0010.001, 0.002, …, 0.999High-volume markets
0.00010.0001, …, 0.9999Finest granularity
def align_price(price, tick_size=0.01):
    """Round a price to the nearest valid tick."""
    return round(round(price / tick_size) * tick_size, 4)

# Example
raw_price = 0.4537
aligned = align_price(raw_price, tick_size=0.01)
print(f"{raw_price}{aligned}")  # 0.4537 → 0.45

aligned_fine = align_price(raw_price, tick_size=0.001)
print(f"{raw_price}{aligned_fine}")  # 0.4537 → 0.454

To get the tick size for a market, query the Gamma API:

import requests

slug = "bitcoin-above-100k"
resp = requests.get(f"https://gamma-api.polymarket.com/markets?slug={slug}")
market = resp.json()[0]
tick_size = float(market.get("minimum_tick_size", "0.01"))

Neg Risk Markets

Some Polymarket markets are flagged as neg_risk (negative risk) — multi-outcome markets that share a collateral pool. You must set this flag correctly or the order routes to the wrong Exchange contract and is rejected. In CLOB V2, pass it via the options argument; read a market’s flag with client.get_neg_risk(token_id) or the neg_risk field on the get_order_book() response.

from py_clob_client_v2 import OrderArgs, OrderType, PartialCreateOrderOptions, Side

# Read the flag (or use the neg_risk field from get_order_book)
is_neg_risk = client.get_neg_risk("<token-id>")

# Pass it through options when creating the order
client.create_and_post_order(
    order_args=OrderArgs(token_id="<token-id>", price=0.50, size=10.0, side=Side.BUY),
    options=PartialCreateOrderOptions(tick_size="0.01", neg_risk=is_neg_risk),
    order_type=OrderType.GTC,
)

When using the TypeScript SDK, neg_risk is passed in the options:

const order = await client.createAndPostOrder(
  { tokenID: "TOKEN_ID", price: 0.50, size: 10, side: Side.BUY },
  { tickSize: "0.01", negRisk: true }  // Pass neg_risk flag
);

Complete Workflow: Buy, Monitor, Sell

A full trading cycle combining order placement with position and balance checks:

import requests
from py_clob_client_v2 import (
    ClobClient, OrderArgs, OrderType,
    PartialCreateOrderOptions, Side,
    BalanceAllowanceParams, AssetType,
)

client = ClobClient(
    host="https://clob.polymarket.com",
    chain_id=137,
    key="<your-private-key>",
)
client.set_api_creds(client.create_or_derive_api_key())

token_id = "<token-id>"
WALLET = "<your-wallet-address>"   # funder address that holds your positions
opts = PartialCreateOrderOptions(tick_size="0.01")

# 1. Check pUSD balance (V2: get_balance_allowance, not get_balance)
balance = client.get_balance_allowance(
    BalanceAllowanceParams(asset_type=AssetType.COLLATERAL)
)
pusd = int(balance["balance"]) / 1e6
print(f"Available: ${pusd:.2f} pUSD")

# 2. Check order book for pricing (V2 returns a dict)
book = client.get_order_book(token_id)
best_ask = float(book["asks"][0]["price"]) if book["asks"] else None
print(f"Best ask: ${best_ask}")

# 3. Place a limit buy below the best ask
size = 0.0
if best_ask and pusd > 0:
    buy_price = round(best_ask - 0.02, 2)  # 2 cents below ask
    buy_size = min(100, pusd / buy_price)

    client.create_and_post_order(
        order_args=OrderArgs(token_id=token_id, price=buy_price, size=buy_size, side=Side.BUY),
        options=opts,
        order_type=OrderType.GTC,
    )
    print(f"Buy order placed: {buy_size:.0f} shares @ ${buy_price}")

# 4. Check positions after fill — positions come from the Data API in V2
positions = requests.get(
    "https://data-api.polymarket.com/positions",
    params={"user": WALLET},
).json()
for pos in positions:
    if pos["asset"] == token_id:
        size = float(pos["size"])
        print(f"Position: {size:.2f} shares")

# 5. Place a sell order at target price
if size > 0:
    client.create_and_post_order(
        order_args=OrderArgs(token_id=token_id, price=0.70, size=size, side=Side.SELL),
        options=opts,
        order_type=OrderType.GTC,
    )
    print(f"Sell order placed: {size:.0f} shares @ $0.70")

Common Errors

ErrorCauseFix
maker address not allowed, please use the deposit wallet flowNew-style accounts must trade via a deposit wallet (POLY_1271)Use signature_type=SignatureTypeV2.POLY_1271 with your deposit-wallet funder. This is the most-reported V2 error — see the known-bugs tracker
INSUFFICIENT_BALANCENot enough pUSD for the orderCheck balance with get_balance_allowance() first (pUSD)
INVALID_TICK_SIZEPrice doesn’t match market’s tick sizePass the right tick via options=PartialCreateOrderOptions(tick_size=...); read it with get_tick_size()
Order rejected (sub-cent amount)Float-precision drift in V2 amount roundingKnown V2 issue on FOK/limit orders — round size/price yourself before signing
MARKET_NOT_TRADABLEMarket is closed or resolvedCheck market status via Gamma API before ordering
INVALID_SIGNATURE / 401Auth signature/credential mismatchRegenerate credentials: client.set_api_creds(client.create_or_derive_api_key())
FOK order rejectedNot enough liquidity for full fillCheck order book depth or use FAK for partial fills
Post-only rejectedOrder would immediately matchYour price crosses the spread — adjust it

See Also


{{ partial “marketplace-cta.html” . }}


This reference is maintained by AgentBets.ai. Found an error or SDK change we missed? Let us know on Twitter.

Not financial advice. Built for builders.