place_limit_order() and place_market_order() are the current polymarket-client methods that place trades on Polymarket. On AsyncSecureClient / SecureClient you can also sign without submitting (create_limit_order / create_market_order → post_order) or batch up to 15 signed orders with post_orders. This page covers GTC/GTD limits, FAK/FOK markets, post-only quotes, builder attribution, and response handling.

For agent builders: automate limit ladders, post_only maker quotes, and paced batch posts — those keep price control and stay within documented batch size. What burns keys and accounts is shipping the owner private key into an agent runtime, ignoring rate limits, and firing FOK into thin books that cannot fill.

Official sources: Place Orders, Python SDK, and SDK Migration. The older py-clob-client-v2 names create_order / create_and_post_order are superseded — treat them as historical only. The sibling CLOB V2 migration guide still documents the V1→V2 cutover and is stale relative to the unified SDK.

Update (September 2026): Uninstall py-clob-client-v2 (and related builder packages); install polymarket-client (0.9.0 on PyPI as of 2026-09-04, Python >=3.11). Method renames:

Old py-clob-client-v2Current polymarket-client
create_and_post_orderplace_limit_order
create_ordercreate_limit_order
create_and_post_market_orderplace_market_order
create_market_ordercreate_market_order (still exists)
post_orderpost_order
ClobClient + set_api_credsAsyncSecureClient.create / SecureClient.create
OrderArgs / Side enumkeyword args; side="BUY" / "SELL" strings in official Python examples
PartialCreateOrderOptions(tick_size, neg_risk)resolved automatically by SDK

Install and create a secure client

pip uninstall -y py-clob-client-v2 py-builder-relayer-client py-builder-signing-sdk
pip install polymarket-client
uv remove py-clob-client-v2 py-builder-relayer-client py-builder-signing-sdk
uv add polymarket-client
import os

from polymarket import AsyncSecureClient

async with await AsyncSecureClient.create(
    private_key=os.environ["POLYMARKET_PRIVATE_KEY"],
) as client:
    ...

Omit wallet to use the default Deposit Wallet flow. Pass wallet=... when reconnecting an existing account wallet. The sync twin is SecureClient.create(...).

Before placing, load a market and read outcome token IDs:

market = await client.get_market(slug="MARKET_SLUG")

if market.outcomes.yes.token_id is None or market.outcomes.no.token_id is None:
    raise RuntimeError("Market token IDs not found")

yes_token_id = market.outcomes.yes.token_id
no_token_id = market.outcomes.no.token_id

One-step limit: place_limit_order

Omit expiration for GTC. Pass a Unix timestamp (seconds) for GTD. Set post_only=True so the order is rejected instead of taking if it would match immediately.

import time

# GTC — rests until filled or canceled
response = await client.place_limit_order(
    token_id=yes_token_id,
    side="BUY",
    price="0.52",
    size="10",
)

# GTD — effective lifetime ~1 hour (see GTD rules below)
expiration = int(time.time()) + 60 + 60 * 60
response = await client.place_limit_order(
    token_id=yes_token_id,
    side="BUY",
    price="0.52",
    size="10",
    expiration=expiration,
)

# Post-only maker quote
response = await client.place_limit_order(
    token_id=yes_token_id,
    side="BUY",
    price="0.52",
    size="10",
    post_only=True,
)

Check acceptance:

if response.ok:
    print(response.order_id, response.status)
    print(response.trade_ids, response.transactions_hashes)
else:
    print(response.code, response.message)

Accepted limit statuses:

StatusMeaning
liveResting on the book
matchedMatched immediately with resting liquidity
delayedMarketable but subject to a matching delay

Two-step: create_limit_order → post_order

Sign locally when you need to inspect, store, or batch the order later. Your private key stays on the machine that signs; the CLOB receives the signature.

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

response = await client.post_order(signed_order)

Use create_market_order the same way when preparing an FAK/FOK market order to post later.


Market: place_market_order

Market orders derive a marketable price from current book depth. FAK is the default (fill available liquidity, cancel remainder). FOK requires a full immediate fill or nothing.

Estimate first, then place with a price cap:

estimated_buy_price = await client.estimate_market_price(
    token_id=yes_token_id,
    side="BUY",
    amount="10",
    order_type="FAK",
)

response = await client.place_market_order(
    token_id=yes_token_id,
    side="BUY",
    amount="10",
    max_price=estimated_buy_price,
    order_type="FAK",
)

Sell by shares (not USD):

estimated_sell_price = await client.estimate_market_price(
    token_id=yes_token_id,
    side="SELL",
    shares="10",
    order_type="FAK",
)

response = await client.place_market_order(
    token_id=yes_token_id,
    side="SELL",
    shares="10",
    min_price=estimated_sell_price,
    order_type="FAK",
)

amount on a BUY is pre-fee USD notional. Set max_spend when the all-in cost (order + applicable fees) must stay inside a fixed budget:

response = await client.place_market_order(
    token_id=yes_token_id,
    side="BUY",
    amount="10",
    max_spend="10",
    max_price=estimated_buy_price,
    order_type="FAK",
)

FOK example:

response = await client.place_market_order(
    token_id=yes_token_id,
    side="BUY",
    amount="10",
    order_type="FOK",
)

Market responses use matched or delayed. A delayed acceptance has empty trade_ids / transactions_hashes until matching finishes — treat it as pending. Check market.trading.seconds_delay before you rely on immediate fills.

Optional settlement wait (default 30 seconds via timeout_s):

if response.ok:
    hashes = await client.wait_for_order_fill_settlement(response)

Order types: GTC / GTD / FOK / FAK

TypeBehaviorTypical use
GTCRemains active until filled or canceledLimit with no deadline
GTDRemains until your expiration timestampLimit that must die before an event
FOKEntire size fills immediately, or nothingMarket — all-or-nothing
FAKFills available size immediately, cancels remainderMarket — partial OK (SDK default)

GTD rules (from Place Orders): orders expire 1 minute before the stated expiration as a security threshold. To get an effective lifetime of N seconds, use now + 60 + N. Expiration must be at least 3 minutes in the future — sooner timestamps are rejected — so the minimum effective lifetime is about two minutes.


Batch: post_orders (1–15)

Create each signed order, then submit together. Useful for quote ladders.

first_order = await client.create_limit_order(
    token_id=yes_token_id,
    side="BUY",
    price="0.51",
    size="10",
)

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

responses = await client.post_orders([first_order, second_order])

for response in responses:
    if response.ok:
        print(response.order_id, response.status)
    else:
        print(response.code, response.message)

post_orders accepts 1–15 signed orders per call. Each response entry can succeed or fail independently — check ok on every entry.


Tick size and neg_risk

The unified SDK resolves tick size, negative-risk routing, fees, and signing details when you place. You do not pass PartialCreateOrderOptions anymore.

Still useful to read for sizing and debugging:

tick_size = market.trading.minimum_tick_size
neg_risk = market.state.neg_risk

Price must follow the market’s minimum increment; size must meet minimum order size. The CLOB rejects non-conforming prices.


Builder attribution

Pass your 32-byte hex builder code so matched trades credit your Builder profile. It is part of the signed order — set it before signing / one-step place.

import os

response = await client.place_limit_order(
    token_id=yes_token_id,
    side="BUY",
    price="0.52",
    size="10",
    builder_code=os.environ["POLYMARKET_BUILDER_CODE"],
)

Copy the code from polymarket.com → Settings → Builders. Verify later with list_builder_trades(builder_code=...).


Complete workflow sketch

import asyncio
import os

from polymarket import AsyncSecureClient


async def main() -> None:
    async with await AsyncSecureClient.create(
        private_key=os.environ["POLYMARKET_PRIVATE_KEY"],
    ) as client:
        market = await client.get_market(slug="MARKET_SLUG")
        yes_token_id = market.outcomes.yes.token_id
        if yes_token_id is None:
            raise RuntimeError("YES token ID missing")

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

        if not response.ok:
            raise RuntimeError(f"{response.code}: {response.message}")

        print(response.order_id, response.status)

        if response.trade_ids:
            hashes = await client.wait_for_order_fill_settlement(response)
            print(hashes)

        condition_id = market.condition_id
        assert condition_id is not None
        page = await client.list_positions(market=[condition_id]).first_page()
        for position in page.items:
            if position.token_id == yes_token_id:
                print(position.size)


asyncio.run(main())

For pre-trade collateral checks, see get_balance_allowance. For depth before market orders, see get_order_book.


Common errors

SymptomCauseWhat to do
response.ok is FalseCLOB rejected the orderRead response.code / response.message
not enough balance / allowanceCollateral or allowance too lowFund / approve; SDK also refreshes allowance during place
GTD rejectedExpiration less than 3 minutes aheadUse now + 60 + N with N large enough
Post-only rejectedOrder would take immediatelyWiden from the touch or drop post_only
FOK rejected / empty fillBook cannot fill full sizeCheck depth; prefer FAK on thin books
delayed statusMatching delay (seconds_delay)Treat as pending; do not assume fill yet
RequestRejectedError status 425Matching engine restart windowRetry place_limit_order with exponential backoff
RateLimitErrorToo many requestsBack off; see rate limits
TimeoutError from settlement waitFills still settling after ~30sTrades still executed; poll positions / wait longer

Agent compatibility

Safer automation

  • Limit ladders and post_orders batches of 1–15
  • post_only=True maker quotes that refuse to take
  • GTC/GTD with explicit size/price strings aligned to tick size
  • estimate_market_price + max_price / min_price / max_spend before market buys
  • Scoped session keys for Deposit Wallet trading (time-limited signer) — keep owner keys off the bot host when you can

Unsafe / account-burning patterns

  • Shipping the owner private key in an agent container, chat tool, or CI log
  • Ignoring rate limits and hammering /order or /orders
  • Blind FOK on thin books
  • Treating delayed as a confirmed fill



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.