Skip to main content
Polymarket is a wallet venue. Orders are placed from the pod wallet, and an outcome is addressed by its tokenId. The order and read surface is in the Polymarket reference, and @parlayx/sdk and Python parlayx expose it through client.polymarket.

Sizing and pricing

A market BUY uses amount in dollars. amount caps the dollars spent and says nothing about the price paid per share. maxFillPrice optionally sets the highest price paid for each share. A limit order uses size in outcome shares and price. A market SELL uses amount in outcome shares. maxFillPrice sets a BUY’s per-share ceiling and minFillPrice sets a SELL’s per-share floor. Both take a price strictly between 0 and 1 and apply to FOK and FAK. A SELL without minFillPrice is not unbounded: it uses a default derived from the market’s midpoint.
A bound can leave an order unfilled. Under FAK the order fills what it can within the bound and cancels the rest; under FOK it does not fill at all.
Polymarket quantizes sizes to its own grid and sets its own minimum. A size the venue cannot execute is rejected rather than rounded down silently. A share-denominated size finer than 2 decimal places is rejected, and the error names the size that does execute. That covers the shares on a SELL and the size on a limit order of either side. A market BUY sized in dollars is unaffected.
Closing a position can leave a residue, and the residue cannot be sold. Selling 14.28 of a 14.285713-share position leaves 0.005713 shares behind. That remainder sits under the venue’s minimum order size, so no further sell order can clear it.It is not lost. It redeems with the rest of the position once the market resolves. Treat a sub-share leftover as awaiting resolution, not as a failed close.

Execution policy

A limit order can carry an expiry with timeInForce: { "type": "GTD", "expiresAt": "..." }. Set expiresAt to an ISO 8601 timestamp at least 3 minutes and at most 30 days ahead. The timestamp you send is passed through unchanged.
Polymarket expires a GTD order shortly before expiresAt, typically about a minute early. Allow for that when choosing how long a quote should rest.

Reading orders and fills

A 201 means the order was accepted. On the response that placed the order, status is the state Polymarket acknowledged, and three fields report the match it stated at that moment. No later read answers this question: an order read reports the fills accumulated since, so it cannot tell you what the placing call itself crossed. Polymarket states each side of the trade, so filledShares and filledAmountUsd are its own figures passed through unchanged. averagePrice is derived from them and rounded, so the three do not multiply back together:
These fields describe the match reported at submission and do not confirm settlement. All three are null when the summary is unavailable, including on idempotent replays; null does not mean zero fills. Use filledAmountUsd for the reported amount because averagePrice is rounded. Use confirmed fills for final accounting.
A summary is unavailable in three situations: the order rested without crossing, you retried under an idempotency key that already exists, or the acknowledgement did not carry the figures. A retry placed nothing and returns the stored order, so read the order back for what that order has filled since.

Batch submit

POST /v1/polymarket/orders/batch takes { "orders": [...] }, each element an order in the same shape as POST /v1/polymarket/orders, under one Idempotency-Key header for the whole batch. Re-send the identical batch under the same key to retry it. It answers with orders, one element per request entry in request order: orders[i] is the answer for the order you sent at position i. An order has no id of your own yet, so position is the only way to match an entry to its input.
  • success: true: the entry was accepted, not confirmed at the venue, and a success is not a fill. orderId and status are set, error is null, and the fill fields report as on a single submit. For a new order status comes before the venue’s acknowledgement. A replayed entry reports the order’s current state, so it can read PENDING, OPEN or a terminal status, and a pending success is still not venue confirmation or a fill.
  • success: false: error says why, and status and the fill fields are null. Read error.code before treating the entry as not placed:
    • ORDER_RECONCILIATION_REQUIRED: the outcome is unknown and the order may be live, so this is not proof nothing was placed. Read it back by the entry’s orderId.
    • INTERNAL_ERROR with a null orderId: the outcome is unknown, so this is not proof nothing was placed either. Re-send the identical batch under the same Idempotency-Key.
    • Any other code: the entry was refused and not placed.
The key covers the whole request, including entries refused as invalid, so the same key with any different batch is refused with 422 IDEMPOTENCY_CONFLICT. To fix a refused entry, send only the corrected order as a new batch under a new key: re-sending the whole batch under a new key places its accepted orders again.

Reading an order back

An expired order reports status: "CANCELLED" with cancellationReason: "EXPIRED" and error: null. An order you cancelled yourself reports cancellationReason: null. Two reads settle late. An expired order can still read OPEN for a few minutes after it has left the venue’s book while final fills are confirmed, so that status alone does not mean the order is still resting. And FILLED can appear before the order read’s own filledShares and averagePrice and its fills list are populated. Those are the order’s accumulated totals and are a different question from the submit response’s match summary, which describes one call.