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 SDK handles EIP-712 signing and submission for you. If you prefer the REST API directly, see Authentication for constructing the required headers and the API Reference for full endpoint documentation including the raw order object fields and request/response schemas.

Order Types

TypeBehaviorUse Case
GTCGood-Til-Cancelled — rests on the book until filled or cancelledDefault for limit orders
GTDGood-Til-Date — active until a specified expiration timeAuto-expire before known events
FOKFill-Or-Kill — must fill immediately and entirely, or cancelAll-or-nothing market orders
FAKFill-And-Kill — fills what’s available immediately, cancels the restPartial-fill market orders
  • GTC and GTD are limit order types — they rest on the book at your specified price.
  • 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

Limit Orders

The simplest way to place a limit order — create, sign, and submit in one call:
import { ClobClient, Side, OrderType } from "@polymarket/clob-client";

const response = await client.createAndPostOrder(
  {
    tokenID: "TOKEN_ID",
    price: 0.5,
    size: 10,
    side: Side.BUY,
  },
  {
    tickSize: "0.01",
    negRisk: false,
  },
  OrderType.GTC,
);

console.log("Order ID:", response.orderID);
console.log("Status:", response.status);

Two-Step Sign Then Submit

For more control, you can separate signing from submission. This is useful for batch orders or custom submission logic:
// Step 1: Create and sign locally
const signedOrder = await client.createOrder(
  {
    tokenID: "TOKEN_ID",
    price: 0.5,
    size: 10,
    side: Side.BUY,
  },
  { tickSize: "0.01", negRisk: false },
);

// Step 2: Submit to the CLOB
const response = await client.postOrder(signedOrder, OrderType.GTC);

GTD Orders

GTD orders auto-expire at a specified time. Useful for quoting around known events.
// Expire in 1 hour (+ 60s security threshold buffer)
const expiration = Math.floor(Date.now() / 1000) + 60 + 3600;

const response = await client.createAndPostOrder(
  {
    tokenID: "TOKEN_ID",
    price: 0.5,
    size: 10,
    side: Side.BUY,
    expiration,
  },
  { tickSize: "0.01", negRisk: false },
  OrderType.GTD,
);
There is a security threshold of one minute on GTD expiration. To set an effective lifetime of N seconds, use now + 60 + N. For example, for a 30-second effective lifetime, set the expiration to now + 60 + 30.

Market Orders

Market orders execute immediately against resting liquidity using FOK or FAK types:
import { Side, OrderType } from "@polymarket/clob-client";

// FOK BUY: spend exactly $100 or cancel entirely
const buyOrder = await client.createMarketOrder(
  {
    tokenID: "TOKEN_ID",
    side: Side.BUY,
    amount: 100, // dollar amount
    price: 0.5, // worst-price limit (slippage protection)
  },
  { tickSize: "0.01", negRisk: false },
);
await client.postOrder(buyOrder, OrderType.FOK);

// FOK SELL: sell exactly 200 shares or cancel entirely
const sellOrder = await client.createMarketOrder(
  {
    tokenID: "TOKEN_ID",
    side: Side.SELL,
    amount: 200, // number of shares
    price: 0.45, // worst-price limit (slippage protection)
  },
  { tickSize: "0.01", negRisk: false },
);
await client.postOrder(sellOrder, OrderType.FOK);
  • FOK — fill entirely or cancel the whole order
  • FAK — fill what’s available, cancel the rest
The price field on market orders acts as a worst-price limit (slippage protection), not a target execution price.

One-Step Market Order

For convenience, createAndPostMarketOrder handles creation, signing, and submission in one call:
const response = await client.createAndPostMarketOrder(
  {
    tokenID: "TOKEN_ID",
    side: Side.BUY,
    amount: 100,
    price: 0.5,
  },
  { tickSize: "0.01", negRisk: false },
  OrderType.FOK,
);

Post-Only Orders

Post-only orders guarantee you’re always the maker. If the order would match immediately (cross the spread), it’s rejected instead of executed.
const response = await client.postOrder(signedOrder, OrderType.GTC, true);
  • Only works with GTC and GTD order types
  • Rejected if combined with FOK or FAK

Batch Orders

Place up to 15 orders in a single request:
import { OrderType, Side, PostOrdersArgs } from "@polymarket/clob-client";

const orders: PostOrdersArgs[] = [
  {
    order: await client.createOrder(
      {
        tokenID: "TOKEN_ID",
        price: 0.48,
        side: Side.BUY,
        size: 500,
      },
      { tickSize: "0.01", negRisk: false },
    ),
    orderType: OrderType.GTC,
  },
  {
    order: await client.createOrder(
      {
        tokenID: "TOKEN_ID",
        price: 0.52,
        side: Side.SELL,
        size: 500,
      },
      { tickSize: "0.01", negRisk: false },
    ),
    orderType: OrderType.GTC,
  },
];

const response = await client.postOrders(orders);

Order Options

Every order requires two market-specific options: tickSize and negRisk. For details on signature types (0 = EOA, 1 = POLY_PROXY, 2 = GNOSIS_SAFE), see Authentication.

Tick Sizes

Your order price must conform to the market’s tick size, or the order is rejected.
Tick SizePrecisionExample 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
const tickSize = await client.getTickSize("TOKEN_ID");

Negative Risk

Multi-outcome events (3+ outcomes) use the Neg Risk CTF Exchange. Pass negRisk: true for these markets.
const isNegRisk = await client.getNegRisk("TOKEN_ID");
Both values are also available on the market object: minimum_tick_size and neg_risk.

Prerequisites

Before placing an order, your funder address must have approved the Exchange contract to spend the relevant tokens:
  • BUY orders: USDC.e allowance >= spending amount
  • SELL orders: conditional token allowance >= selling amount
Order size is limited by your available balance minus amounts reserved by existing open orders: maxOrderSize=balance(openOrderSizefilledAmount)\text{maxOrderSize} = \text{balance} - \sum(\text{openOrderSize} - \text{filledAmount})
Orders are continuously monitored for validity — balances, allowances, and onchain cancellations are tracked in real time. Any maker caught intentionally abusing these checks will be blacklisted.

Advanced Parameters

These optional fields can be passed in the UserOrder object for fine-grained control:
ParameterTypeDescription
feeRateBpsnumberFee rate in basis points (default: market rate)
noncenumberCustom nonce for order uniqueness
takerstringRestrict the order to a specific taker address

Sports Markets

Sports markets have additional behaviors:
  • Outstanding limit orders are automatically cancelled once the game begins, clearing the entire order book at the official start time
  • Marketable orders have a 3-second placement delay before matching
  • Game start times can shift — monitor your orders closely, as they may not be cleared if the start time changes unexpectedly

Response

A successful order placement returns:
{
  "success": true,
  "errorMsg": "",
  "orderID": "0xabc123...",
  "takingAmount": "",
  "makingAmount": "",
  "status": "live",
  "transactionsHashes": [],
  "tradeIDs": []
}

Statuses

StatusDescription
liveOrder resting on the book
matchedOrder matched immediately with a resting order
delayedMarketable order subject to a matching delay
unmatchedMarketable but failed to delay — placement still successful

Error Messages

ErrorDescription
INVALID_ORDER_MIN_TICK_SIZEPrice doesn’t conform to the market’s tick size
INVALID_ORDER_MIN_SIZEOrder size below the minimum threshold
INVALID_ORDER_DUPLICATEDIdentical order already placed
INVALID_ORDER_NOT_ENOUGH_BALANCEInsufficient balance or allowance
INVALID_ORDER_EXPIRATIONExpiration timestamp is in the past
INVALID_POST_ONLY_ORDER_TYPEPost-only used with FOK/FAK
INVALID_POST_ONLY_ORDERPost-only order would cross the book
FOK_ORDER_NOT_FILLED_ERRORFOK order couldn’t be fully filled
INVALID_ORDER_ERRORSystem error inserting the order
EXECUTION_ERRORSystem error executing the trade
ORDER_DELAYEDOrder match delayed due to market conditions
DELAYING_ORDER_ERRORSystem error while delaying the order
MARKET_NOT_READYMarket not yet accepting orders

Heartbeat

The heartbeat endpoint maintains session liveness. If a valid heartbeat is not received within 10 seconds (with a 5-second buffer), all open orders are cancelled.
let heartbeatId = "";
setInterval(async () => {
  const resp = await client.postHeartbeat(heartbeatId);
  heartbeatId = resp.heartbeat_id;
}, 5000);
  • Include the most recent heartbeat_id in each request. Use an empty string for the first request.
  • If you send an expired ID, the server responds with 400 and the correct ID. Update and retry.

Next Steps