Polymarket Bot Tutorial · Chapter 10 of 32
Quick answer (updated 2026-10-25)
FOK (Fill or Kill) fills the whole order immediately or cancels all of it. FAK (Fill and Kill) fills whatever it can immediately and cancels the rest, but needs at least one match. GTC (Good Till Cancelled) rests on the book until it fills or you cancel it. GTD (Good Till Date) rests until an expiry time you set. FOK and FAK never rest on the book, so they are Polymarket's market-order types; GTC and GTD are limit orders and can be post-only. Every Polymarket order carries a price limit, so none of them can fill worse than the price you allow.
Polymarket's order book (the CLOB) accepts exactly four order types, sent in the orderType field: GTC, GTD, FOK and FAK. This chapter explains what each one does with an unfilled remainder, shows the same order filling four different ways on one order book, and gives working code for the official Python and TypeScript SDKs. Polymarkets.co.il is an independent guide, not Polymarket. Every rule below was checked on 25 October 2026 against Polymarket's developer documentation.
What this chapter answers
- FOK vs FAK: the one difference that matters
- What a FOK order and a FAK order are on Polymarket
- What GTC and GTD mean, and the GTD expiry rule
- All Polymarket order types and options in one table
- How to place each type in Python and TypeScript
- Which type a bot should use, and what the FOK and FAK errors mean
FOK vs FAK: the difference
Both types try to fill immediately against orders already on the book, and neither ever rests there. They differ only in what happens when the book cannot fill the full size at your price limit:
- FOK, Fill or Kill: all or nothing. If the full size is not available right now, the whole order is rejected and nothing trades.
- FAK, Fill and Kill: take what is there. The available part fills and the unfilled remainder is cancelled. If nothing at all can match, it is rejected too.
FAK behaves like what other venues call IOC (Immediate or Cancel). Polymarket's own SDKs use FAK as the default for market orders.
| FOK (Fill or Kill) | FAK (Fill and Kill) | |
|---|---|---|
| Partial fill | Never | Yes, the rest is cancelled |
| Rests on the book | No | No |
| When the book is too thin | Whole order rejected | Fills the available part |
| When nothing matches | Rejected | Rejected |
| Error text (docs, 25 Oct 2026) | order couldn't be fully filled. FOK orders are fully filled or killed. | no orders found to match with FAK order. FAK orders are partially filled or killed if no match is found. |
| Pays the taker fee | Yes, on fee-charging markets | Yes, on fee-charging markets |
| Default in the SDK | No | Yes |
What is a FOK order?
A Fill or Kill order must fill its entire size the moment it reaches the matching engine, at your price limit or better. If it cannot, the exchange kills it: no shares change hands and no order stays open. You either get exactly the position you asked for or no position at all.
Use FOK when a partial fill would be worse than no fill: an entry that only makes sense at full size, one leg of a pair you plan to hedge with a second order, or an exit where you need a clean, known position afterwards. The cost is that FOK gets rejected more often on thin books, so your code needs a plan for the rejection: re-check the price and retry smaller, switch to FAK, or skip.
What is a FAK order on Polymarket?
A Fill and Kill order takes every share it can get immediately at your price limit or better, then cancels whatever is left. You can end up with the full size, part of it, or (if nothing matched) a rejection. It never leaves a resting order behind.
Use FAK when some fill is better than none: buying into a fast market with a price ceiling, cutting inventory quickly, or a stop-loss exit. Because the result can be partial, always read how much actually filled before your bot takes its next step. The order response carries makingAmount, takingAmount and the tradeIDs of the fills; the authenticated user WebSocket reports each fill as a trade event.
GTC and GTD: orders that rest on the book
GTC (Good Till Cancelled) is the standard limit order. Whatever part does not match immediately rests on the book at your price until another trader fills it or you cancel it. There is no time limit. If your GTC price already crosses the book, the crossing part fills at once as a taker and the remainder rests.
GTD (Good Till Date) is a GTC with an expiry, given as a Unix timestamp in seconds. Two rules from Polymarket's docs trip people up:
- A GTD order expires one minute before its stated expiration (a security threshold). For an effective life of N seconds, set
expiration = now + 60 + N. - The expiration must be at least 3 minutes in the future, or the order is rejected with
invalid expiration. The shortest effective GTD life is therefore about two minutes.
Only GTC and GTD can be sent as post-only (postOnly: true). A post-only order is rejected with invalid post-only order: order crosses book instead of trading as a taker, which guarantees you only ever add liquidity. That matters because makers pay no fee and earn rebates; see market making on Polymarket.
Polymarket order types at a glance
As of 25 October 2026 the CLOB has four order types and a handful of options. There are no stop, trailing or iceberg order types for prediction markets; those are built in your own code.
| Type or option | Rests on book | Partial fill | Unfilled part | Typical use |
|---|---|---|---|---|
GTC limit | Yes | Yes | Rests until filled or cancelled | Quoting, patient entries and take-profit exits |
GTD limit | Yes, until expiry | Yes | Expires 60 s before the set time | Quotes that must die before a known event |
FOK market | No | No | Whole order rejected | All-or-nothing entries |
FAK market | No | Yes | Cancelled | Take available liquidity now, stop-loss exits |
postOnly (GTC, GTD only) | Yes | Yes | Rejected if it would cross | Guaranteed maker orders |
Batch POST /orders | 1 to 15 signed orders per request; each one is accepted or rejected on its own | |||
Order heartbeat POST /heartbeats | Optional dead man's switch: once enabled, if no heartbeat arrives for about 10 seconds the exchange queues a best-effort cancel of all open orders for that API key; send one every 5 seconds | |||
Two market settings apply to every type. The price must sit on the market's tick size (0.01 on most markets; 0.1, 0.005, 0.0025, 0.001 and 0.0001 also exist), and the size must meet the market's minimum order size (min_order_size in the order book, often 5). Read both from the market rather than hardcoding them, and watch for tick_size_change on the market WebSocket.
One order, four outcomes: a worked example
Say the asks for an outcome are 25 shares at 0.52 and 30 shares at 0.54, and you want to buy 40 shares paying no more than 0.53:
| Order type | What happens | You end up with |
|---|---|---|
| FOK, limit 0.53 | Only 25 shares are offered at 0.53 or lower, so the full 40 cannot fill | Nothing; the order is rejected |
| FAK, limit 0.53 | Takes the 25 shares at 0.52, cancels the other 15 | 25 shares at 0.52, no open order |
| GTC at 0.53 | Takes the 25 shares at 0.52 as a taker, rests a bid for 15 at 0.53 | 25 shares plus an open bid for 15 |
| GTD at 0.53, one hour | Same as GTC, but the 15-share bid expires after about an hour | 25 shares plus a bid that dies on schedule |
Notice the fill price: the 25 shares fill at 0.52, not at your 0.53 limit. Polymarket's docs state that price improvement always goes to the taker. Your limit is a worst case, not the price you pay.
How to place FOK, FAK, GTC and GTD orders (Python and TypeScript)
The current official clients are the unified SDKs: polymarket-client 0.14.0 on PyPI (import polymarket) and @polymarket/client 0.14.0 on npm, both released 21 October 2026. In them, GTC and GTD go through the limit-order method (omit expiration for GTC), and FOK and FAK go through the market-order method. A market BUY is sized in dollars (amount), a market SELL in shares (shares), and max_price / min_price set the worst price you accept. The wallet is your Polymarket account wallet (shown in the profile menu on polymarket.com); see authentication and wallets.
Python (polymarket-client 0.14.0)
# pip install polymarket-client
import asyncio, os, time
from decimal import Decimal
from polymarket import AsyncSecureClient
async def main():
async with await AsyncSecureClient.create(
private_key=os.environ["POLYMARKET_PRIVATE_KEY"],
wallet=os.environ["POLYMARKET_WALLET_ADDRESS"],
) as client:
market = await client.get_market(slug="your-market-slug")
yes = market.outcomes.yes
asset_id = yes.position_id if market.version == "v2" else yes.token_id
# FOK: spend 10 USD now, all or nothing, never above 0.53
fok = await client.place_market_order(
asset_id=asset_id, side="BUY", amount="10",
max_price=Decimal("0.53"), order_type="FOK",
)
# FAK: sell up to 20 shares now, never below 0.50, cancel the rest
fak = await client.place_market_order(
asset_id=asset_id, side="SELL", shares="20",
min_price=Decimal("0.50"), order_type="FAK",
)
# GTC: rest a bid for 10 shares at 0.48 until filled or cancelled
gtc = await client.place_limit_order(
asset_id=asset_id, side="BUY", price="0.48", size="10",
)
# GTD: same bid with an effective life of one hour (expiry fires 60 s early)
gtd = await client.place_limit_order(
asset_id=asset_id, side="BUY", price="0.48", size="10",
expiration=int(time.time()) + 60 + 3600,
)
for r in (fok, fak, gtc, gtd):
if r.ok:
print(r.order_id, r.status, r.trade_ids)
else:
print("rejected:", r.code, r.message)
asyncio.run(main())
TypeScript (@polymarket/client 0.14.0)
// npm install @polymarket/client@latest
import { createSecureClient, OrderSide, OrderType } from "@polymarket/client";
import { privateKey } from "@polymarket/client/viem";
const client = await createSecureClient({
wallet: process.env.POLYMARKET_WALLET_ADDRESS,
signer: privateKey(process.env.POLYMARKET_PRIVATE_KEY),
});
const market = await client.fetchMarket({ slug: "your-market-slug" });
const { yes } = market.outcomes;
const assetId = market.version === "v2" ? yes.positionId : yes.tokenId;
const fok = await client.placeMarketOrder({
assetId, side: OrderSide.BUY, amount: "10", maxPrice: 0.53, orderType: OrderType.FOK,
});
const fak = await client.placeMarketOrder({
assetId, side: OrderSide.SELL, shares: "20", minPrice: 0.5, orderType: OrderType.FAK,
});
const gtc = await client.placeLimitOrder({
assetId, side: OrderSide.BUY, price: "0.48", size: "10",
});
const gtd = await client.placeLimitOrder({
assetId, side: OrderSide.BUY, price: "0.48", size: "10",
expiration: Math.floor(Date.now() / 1000) + 60 + 3600,
});
for (const r of [fok, fak, gtc, gtd]) {
console.log(r.ok ? `${r.orderId} ${r.status}` : `rejected: ${r.code} ${r.message}`);
}
To quote as a guaranteed maker, add post_only=True (Python) or postOnly: true (TypeScript) to a GTC or GTD call. Still on the deprecated py-clob-client-v2 or @polymarket/clob-client-v2? The same four values exist there as OrderType.GTC, GTD, FOK and FAK; the old V1 py-clob-client stopped working on 28 April 2026.
Raw REST: where the order type goes
Over the plain API, orderType sits next to the signed order in the POST /order body; it is not part of the EIP-712 data you sign. Market orders (FOK and FAK) must carry "expiration": "0"; GTD carries its Unix-seconds expiry. Signing and headers are covered in the CLOB API guide.
{
"deferExec": false,
"order": { "...": "signed order fields", "expiration": "0" },
"orderType": "FOK",
"owner": "<your CLOB API key>"
}
What the exchange sends back
An accepted order returns a status. Read it before assuming you have a position:
| Status | Meaning | Seen with |
|---|---|---|
matched | Filled (fully, or partly for FAK and crossing GTC) against resting orders | All four types |
live | Resting on the book | GTC, GTD |
delayed | Accepted but held for a matching delay; no fills yet | Markets with a taker or sports delay |
unmatched | Marketable order placed on the book after its delay without a match | Delay markets |
Some markets hold marketable orders briefly before matching: a short taker delay on crypto markets (150 ms since 4 September 2026, per Polymarket's changelog) and a longer delay on configured live sports markets. While an order is delayed it cannot be cancelled, and a delayed FOK or FAK reply has no fills yet, so follow it on the user WebSocket. Since 24 July 2026, successful FOK and FAK replies return tradeIDs rather than transaction hashes; the SDKs' wait_for_order_fill_settlement() / waitForOrderFillSettlement() waits for the on-chain hashes.
Which order type should a bot use?
- All-or-nothing entry at a price cap: FOK. Handle the rejection (retry smaller, or skip).
- Get in or out now and accept a partial: FAK with
max_priceormin_price. Read the filled amount before the next step. - Quote, wait for a better price, avoid the taker fee: GTC, sent post-only so it can never cross.
- Quote that must not survive a known event (a game start, a data release): GTD with the expiry rule above.
- Stop-loss: no native stop order exists, so your bot watches the price and sends a FAK sell with a
min_pricefloor when the trigger hits. A GTC sell cannot wait as a stop: priced below the current bid it fills at once, and priced above it waits for a rise, not a fall. - Resting orders from a process that might crash: enable the order heartbeat so stale orders get cancelled; see bot risk controls.
Fees decide a lot here. Takers pay fee = shares x feeRate x price x (1 - price) on fee-charging markets (feeRate 0.07 on crypto, 0.05 on sports, 0.04 on politics, 0 on geopolitics, per the fee table on 25 October 2026); makers pay nothing and share in daily rebates. FOK and FAK always take, so they always pay the fee where one applies. The full table is in the CLOB API guide.
FOK and FAK error messages
order couldn't be fully filled. FOK orders are fully filled or killed.The book did not have your full size at your price. Nothing traded.no orders found to match with FAK order. FAK orders are partially filled or killed if no match is found.Not a single share was available at your limit. Nothing traded.FOK orders are filled or killed(HTTP 500): the same outcome reported by the matching engine.invalid expiration: a GTD expiry in the past or less than 3 minutes ahead.order {id} is invalid. Size ({size}) lower than the minimum: {min}: below the market's minimum order size.
Every other CLOB error, with fixes, is on the Polymarket API errors page.
docs.polymarket.com); package versions on PyPI and npm.

