Skip to main content
All orders on Polymarket are expressed as limit orders. Market orders are supported by submitting a limit order with a marketable price — your order executes immediately at the best available price on the book. The underlying order primitive is structured, hashed, and signed using the EIP-712 standard, then executed onchain via the Exchange contract. Preparing orders manually is involved, so we recommend using the open-source TypeScript or Python SDK clients, which handle signing and submission for you.
If you prefer to use the REST API directly, you’ll need to manage order signing yourself. See Authentication for details on constructing the required headers.

Order Types

TypeBehaviorUse Case
GTC (Good-Til-Cancelled)Rests on the book until filled or cancelledDefault for passive limit orders
GTD (Good-Til-Date)Active until a specified expiration time (UTC seconds timestamp), unless filled or cancelled firstAuto-expire orders before known events
FOK (Fill-Or-Kill)Must be filled immediately and entirely, or the whole order is cancelledAll-or-nothing execution
FAK (Fill-And-Kill)Fills as many shares as available immediately, then cancels any unfilled remainderPartial immediate execution
  • FOK and FAK are market order types — they execute against resting liquidity immediately.
    • BUY: specify the dollar amount you want to spend
    • SELL: specify the number of shares you want to sell
  • GTC and GTD are limit order types — they rest on the book at your specified price.
GTD expiration: There is a security threshold of one minute. If you need the order to expire in 90 seconds, the correct expiration value is now + 1 minute + 30 seconds.

Post-Only Orders

Post-only orders are limit orders that will only rest on the book and not match immediately on entry.
  • If a post-only order would cross the spread (i.e., it is marketable), it will be rejected rather than executed.
  • Post-only cannot be combined with market order types (FOK or FAK). If postOnly = true is sent with a market order type, the order will be rejected.
  • Post-only can only be used with GTC and GTD order types.

Tick Sizes

Markets have different minimum price increments (tick sizes). Your order price must conform to the market’s tick size, or the order will be rejected.
Tick SizePrice PrecisionExample Prices
0.11 decimal0.1, 0.2, 0.5
0.012 decimals0.01, 0.50, 0.99
0.0013 decimals0.001, 0.500, 0.999
0.00014 decimals0.0001, 0.5000, 0.9999
Retrieve the tick size for a market using the SDK:
const tickSize = await client.getTickSize(tokenID);
// Returns: "0.1" | "0.01" | "0.001" | "0.0001"
You can also check the minimum_tick_size field on a market object returned by the Markets API.

Negative Risk

Multi-outcome events (e.g., “Who will win the election?” with 3+ candidates) use a different exchange contract called the Neg Risk CTF Exchange. When placing orders on these markets, you must pass negRisk: true in the order options.
const response = await client.createAndPostOrder(
  {
    tokenID: "TOKEN_ID",
    price: 0.5,
    size: 10,
    side: Side.BUY,
  },
  {
    tickSize: "0.01",
    negRisk: true, // Required for multi-outcome markets
  },
);
You can check whether a market uses negative risk via the SDK or the market object’s neg_risk field:
const isNegRisk = await client.getNegRisk(tokenID);

Allowances

Before placing an order, your funder address must have approved the Exchange contract to spend the relevant tokens:
  • Buying: the funder must have set a USDC.e allowance greater than or equal to the spending amount.
  • Selling: the funder must have set a conditional token allowance greater than or equal to the selling amount.
This allows the Exchange contract to execute settlement according to your signed order instructions.

Validity Checks

Orders are continually monitored to make sure they remain valid. This includes tracking:
  • Underlying balances
  • Allowances
  • Onchain order cancellations
Any maker caught intentionally abusing these checks will be blacklisted.
There are also limits on order placement per market. You can only place orders that sum to less than or equal to your available balance for each market. For example, if you have 500 USDC.e in your funding wallet, you can place one order to buy 1000 YES at $0.50 — but any additional buy orders in that market will be rejected since your entire balance is reserved for the first order. The max size you can place for an order is: maxOrderSize=underlyingAssetBalance(orderSizeorderFillAmount)\text{maxOrderSize} = \text{underlyingAssetBalance} - \sum(\text{orderSize} - \text{orderFillAmount})

Querying Orders

All query endpoints require L2 authentication. Builder-authenticated clients can also query orders attributed to their builder account using the same methods.

Get a Single Order

Retrieve details for a specific order by its ID:
const order = await client.getOrder("0xb816482a...");
console.log(order);

Get Open Orders

Retrieve your open orders, optionally filtered by market or asset:
// All open orders
const orders = await client.getOpenOrders();

// Filtered by market
const marketOrders = await client.getOpenOrders({
  market: "0xbd31dc8a...",
});

// Filtered by asset
const assetOrders = await client.getOpenOrders({
  asset_id: "52114319501245...",
});

OpenOrder Object

Each order returned contains these fields:
FieldTypeDescription
idstringOrder ID
statusstringCurrent order status
marketstringMarket ID (condition ID)
asset_idstringToken ID
sidestringBUY or SELL
original_sizestringOriginal order size at placement
size_matchedstringAmount that has been filled
pricestringLimit price
outcomestringHuman-readable outcome (e.g., “Yes”, “No”)
order_typestringOrder type (GTC, GTD, FOK, FAK)
maker_addressstringFunder address
ownerstringAPI key of the order owner
expirationstringUnix timestamp when the order expires (0 if no expiration)
associate_tradesstring[]Trade IDs this order has been partially included in
created_atstringUnix timestamp when the order was created

Trade History

When an order is matched, it creates a trade. Trades go through the following statuses:
StatusTerminal?Description
MATCHEDNoMatched and sent to the executor service for onchain submission
MINEDNoObserved as mined on the chain, no finality threshold yet
CONFIRMEDYesAchieved strong probabilistic finality — trade successful
RETRYINGNoTransaction failed (revert or reorg) — being retried by the operator
FAILEDYesTrade failed permanently and is not being retried

Trade Object

Each trade contains these fields:
FieldTypeDescription
idstringTrade ID
taker_order_idstringTaker order ID (hash)
marketstringMarket ID (condition ID)
asset_idstringToken ID
sidestringBUY or SELL
sizestringTrade size
fee_rate_bpsstringFee rate in basis points
pricestringTrade price
statusstringTrade status (see table above)
match_timestringUnix timestamp when the trade was matched
last_updatestringUnix timestamp of last status update
outcomestringHuman-readable outcome (e.g., “Yes”, “No”)
ownerstringAPI key ID of the trade owner
maker_addressstringFunder address
trader_sidestringWhether you were TAKER or MAKER in this trade
transaction_hashstringOnchain transaction hash (available after mining)
maker_ordersarrayArray of maker orders matched against this trade (see below)

MakerOrder Fields

Each entry in the maker_orders array contains:
FieldTypeDescription
order_idstringMaker order ID (hash)
ownerstringMaker’s API key ID
maker_addressstringMaker’s funder address
matched_amountstringAmount matched in this trade
pricestringMaker order price
fee_rate_bpsstringMaker fee rate in bps
asset_idstringToken ID
outcomestringOutcome name
sidestringBUY or SELL
Retrieve your trades with the SDK:
// All trades
const trades = await client.getTrades();

// Filtered by market
const marketTrades = await client.getTrades({
  market: "0xbd31dc8a...",
});

// With pagination
const paginatedTrades = await client.getTradesPaginated({
  market: "0xbd31dc8a...",
});

Heartbeat

The heartbeat endpoint maintains session liveness for order safety. If a valid heartbeat is not received within 10 seconds (with up to a 5-second buffer), all of your open orders will be cancelled.
// Send heartbeats in a loop
let heartbeatId = "";
setInterval(async () => {
  const resp = await client.postHeartbeat(heartbeatId);
  heartbeatId = resp.heartbeat_id;
}, 5000);
  • On each request, include the most recent heartbeat_id you received. For your first request, use an empty string.
  • If you send an invalid or expired heartbeat_id, the server responds with a 400 Bad Request and provides the correct heartbeat_id in the response. Update your client and retry.

Order Scoring

Check if your resting orders are eligible for maker rebates scoring:
// Single order
const scoring = await client.isOrderScoring({ orderId: "0x..." });
console.log(scoring); // { scoring: true }

// Multiple orders
const batchScoring = await client.areOrdersScoring({
  orderIds: ["0x...", "0x..."],
});

Onchain Order Info

When a trade is settled onchain, the Exchange contract emits an OrderFilled event with the following fields:
FieldDescription
orderHashUnique hash for the filled order
makerThe user who generated the order and source of funds
takerThe user filling the order, or the Exchange contract if multiple limit orders are filled
makerAssetIdID of the asset given out. If 0, the order is a BUY (giving USDC.e for outcome tokens)
takerAssetIdID of the asset received. If 0, the order is a SELL (receiving USDC.e for outcome tokens)
makerAmountFilledAmount of the asset given out
takerAmountFilledAmount of the asset received
feeFees paid by the order maker

Error Messages

When placing an order, the response may include an errorMsg if the order could not be placed. If success is false, there was a server-side error:
ErrorDescription
INVALID_ORDER_MIN_TICK_SIZEPrice doesn’t conform to the market’s tick size
INVALID_ORDER_MIN_SIZEOrder size is below the minimum threshold
INVALID_ORDER_DUPLICATEDIdentical order has already been placed
INVALID_ORDER_NOT_ENOUGH_BALANCEFunder doesn’t have sufficient balance or allowance
INVALID_ORDER_EXPIRATIONExpiration timestamp is in the past
INVALID_ORDER_ERRORSystem error while inserting order
INVALID_POST_ONLY_ORDER_TYPEPost-only flag used with a market order type (FOK/FAK)
INVALID_POST_ONLY_ORDERPost-only order would cross the book
EXECUTION_ERRORSystem error while executing trade
ORDER_DELAYEDOrder placement delayed due to market conditions
DELAYING_ORDER_ERRORSystem error while delaying order
FOK_ORDER_NOT_FILLED_ERRORFOK order couldn’t be fully filled
MARKET_NOT_READYMarket is not yet accepting orders

Insert Statuses

When an order is successfully placed, the response includes a status field:
StatusDescription
matchedOrder placed and matched with a resting order
liveOrder placed and resting on the book
delayedOrder is marketable but subject to a matching delay
unmatchedOrder is marketable but failed to delay — placement still successful

Security

Polymarket’s Exchange contract has been audited by Chainsecurity (View Audit). The operator’s privileges are limited to order matching and ensuring correct ordering. Operators cannot set prices or execute unauthorized trades. Users can cancel orders onchain independently if trust issues arise.

Next Steps