How to detect Polymarket phantom fills (orders that look filled but are not), implement idempotent retries, distinguish status=matched from rested-on-book, and survive transient failures.
How to detect Polymarket phantom fills (orders that look filled but are not), implement idempotent retries, distinguish status=matched from rested-on-book, and survive transient failures.
By Harley Young, lead writer at Polymarkets.co.il. Last reviewed: May 2026.
What this chapter covers
This is chapter 12 of our 32-part series on building a Polymarket trading bot. We cover the topic in depth across the sections below. Body content for each section is being written and rolled out chapter-by-chapter; FAQ answers and references are already complete and reflect production experience from running our own trader.
What is a phantom fill
status=matched vs status=delayed vs status=posted
Polling pattern: poll status before celebrating
FOK as anti-phantom-fill
Idempotent retries with client_order_id
Real production incident: how we fixed ours
Code: detect-then-act post-order pattern
What is a phantom fill
This section is in active development. Want to be notified when it goes live? Contact us or watch the authors page.
status=matched vs status=delayed vs status=posted
This section is in active development. Want to be notified when it goes live? Contact us or watch the authors page.
Polling pattern: poll status before celebrating
This section is in active development. Want to be notified when it goes live? Contact us or watch the authors page.
FOK as anti-phantom-fill
This section is in active development. Want to be notified when it goes live? Contact us or watch the authors page.
Idempotent retries with client_order_id
This section is in active development. Want to be notified when it goes live? Contact us or watch the authors page.
Real production incident: how we fixed ours
This section is in active development. Want to be notified when it goes live? Contact us or watch the authors page.
Code: detect-then-act post-order pattern
This section is in active development. Want to be notified when it goes live? Contact us or watch the authors page.
Frequently asked questions
What is a phantom fill on Polymarket?
A phantom fill is when your bot believes an order filled but the exchange records it as not yet filled (or partially filled). It happens when client code treats an HTTP 200 response as confirmation, when the response only means the order was accepted into the matching engine. We learned this the hard way - commits 06deaef, 8bb7761, and e68a087 in our trader history fix exactly this.
How do I avoid phantom fills?
Three rules: (1) Use FOK orders for buys - the order is either fully filled or fully gone, never ambiguous. (2) Treat any non-matched status as not filled - poll the order status until status=matched OR amount_filled > 0. (3) Use a client_order_id (clientOrderId in V2) for idempotence so retries do not double-fill.
What does status=delayed mean?
The order is in the matching engine but has not been fully matched yet. It might match within seconds or it might rest. Always poll - if status stays delayed for more than 5-10 seconds and amount_filled is 0, treat it as unfilled and consider canceling.
How do I retry safely without double-filling?
Generate a unique client_order_id per logical trade attempt and pass it on every retry. The exchange dedupes by client_order_id so a retried order with the same id is rejected as duplicate rather than placed again. Implementations: Python OrderArgs.client_order_id, Node CreateOrderOptions.clientOrderId.
Can I trust a 200 OK response from the order endpoint?
No - a 200 OK only means "your request was accepted by the matching engine," not "your order filled." You must poll the order status by orderId after submission and only treat status=matched (or amount_filled > 0) as a real fill.
What if my bot crashes between sending an order and seeing the response?
On restart, query open orders and recent fills via the SDK. Reconcile against your local diary/state - if you sent an order at time T but no record exists, query orders since T and match by client_order_id. If still missing, the order never reached the matching engine and you can safely re-send.
Perguntas Frequentes
What is a phantom fill on Polymarket?
A phantom fill is when your bot believes an order filled but the exchange records it as not yet filled (or partially filled). It happens when client code treats an HTTP 200 response as confirmation, when the response only means the order was accepted into the matching engine. We learned this the hard way - commits 06deaef, 8bb7761, and e68a087 in our trader history fix exactly this.
How do I avoid phantom fills?
Three rules: (1) Use FOK orders for buys - the order is either fully filled or fully gone, never ambiguous. (2) Treat any non-matched status as not filled - poll the order status until status=matched OR amount_filled > 0. (3) Use a client_order_id (clientOrderId in V2) for idempotence so retries do not double-fill.
What does status=delayed mean?
The order is in the matching engine but has not been fully matched yet. It might match within seconds or it might rest. Always poll - if status stays delayed for more than 5-10 seconds and amount_filled is 0, treat it as unfilled and consider canceling.
How do I retry safely without double-filling?
Generate a unique client_order_id per logical trade attempt and pass it on every retry. The exchange dedupes by client_order_id so a retried order with the same id is rejected as duplicate rather than placed again. Implementations: Python OrderArgs.client_order_id, Node CreateOrderOptions.clientOrderId.
Can I trust a 200 OK response from the order endpoint?
No - a 200 OK only means "your request was accepted by the matching engine," not "your order filled." You must poll the order status by orderId after submission and only treat status=matched (or amount_filled > 0) as a real fill.
What if my bot crashes between sending an order and seeing the response?
On restart, query open orders and recent fills via the SDK. Reconcile against your local diary/state - if you sent an order at time T but no record exists, query orders since T and match by client_order_id. If still missing, the order never reached the matching engine and you can safely re-send.