CreateVendorOrder.
Before this page:
- a token and the order-write grant on your client (Authentication);
- the participant’s
participantId, taken from thekyc.approvedwebhook (Firms, participants and accounts); - the participant’s
provisionedAccount, the account you captured at onboarding (External IDs, participants and accounts) — it goes inorder.account; - the participant account funded, because every order is prefunded (Move cash);
- the instrument’s two scales and the fee formula (Money on the wire).
It is fill-or-kill limit orders, and nothing else
CreateVendorOrder accepts limit orders with TIME_IN_FORCE_FILL_OR_KILL and nothing else. Any
other time-in-force is rejected outright with
Three request facts that are not in the reflected schema
order.clord_id is required by the runtime. PreviewOrder ignores it and CreateVendorOrder
requires it. Reflection advertised the older schema without it for a period, so a client generated
from that build compiles, sends no clord_id, and fails at runtime. Send it on every order and keep
it unique per order — it is the identifier you will match execution reports on.
The participant is named only by order.account. There is no separate customer-account field on
this request, and x-participant-id is not used here. The header is required on account-scoped
reads such as positions and reports, and it does nothing on this RPC.
The value you put in order.account is the provisionedAccount you captured at onboarding — see
External IDs, participants and accounts. It is opaque:
copy it verbatim and never build it.
[VERIFY] — which call returns
provisionedAccount is not published. The kyc.approved webhook
is documented as carrying participantId, and nothing states where provisionedAccount comes back.Record whatever account string we hand you at onboarding against your user row, and confirm the
source call with your integration lead.funding_request_ids are values we return, not values you send. The dedupe key is
idempotency_key. See Recover a lost order.
The call
[VERIFY] — five things in the snippets below are not published, and one of them sits inside a
runnable command. Only
order.account, order.clord_id, order.time_in_force, order.symbol,
order_qty, cash_order_qty, idempotency_key, funding_request_ids and failure_reason are
confirmed. Unconfirmed, and to be checked against your own generated code and proto copy before you
send anything:- the fully-qualified service name
polymarket.v1.VendorOrderAPI/CreateVendorOrder, which thegrpcurltab passes as the method argument; - the request, order and enum type names, and the generated stub names;
- the field name carrying the limit price, shown below as
order.price; - the side enum values, shown below as
SIDE_BUYandSIDE_SELL; - your own
protocpackage layout — the bundle is unversioned, with no checksum or changelog.
status is the only field that decides what you do next, and ACCEPTED is not a fill. All three
values are specified under Order outcomes.
Four worked order shapes
All four usepriceScale: 100 and fractionalQtyScale: 100 and a YES price of **1.00 and losing contracts at $0.00.
The [VERIFY] cells below are the field name carrying the limit price and the two side enum
values. The scaling, the quantities and the money are confirmed; those three spellings are not.
1. Buy YES — quantity-denominated
Maximum cost **0.32), which is what gets prefunded. Maximum return **1.00. Fee on a full fill:
0.06 × 5 × 0.32 × 0.68 = $0.0653 → $0.07 after banker’s
rounding, which at that coefficient arrives as commission_notional_collected: "700"
(700 / 10,000). At a different coefficient both the cent figure and the wire figure change — recompute,
do not copy.
2. Take the NO side — quantity-denominated
A
SELL does not pay you cash. It spends complementary NO collateral, and the resulting short
carries an obligation of **up to 5.00 maximum obligation.
Maximum return **0.00.
3. Cash-denominated buy
cash_order_qty is scaled by priceScale, so 2000 at priceScale: 100 is 2,000
and not 20.00 exactly, which is the point of denominating in cash: the
prefund equals the field. At 62.50 if YES settles
at $1.00.
Send order_qty or cash_order_qty, not both.
4. Cash-denominated sell
This is the shape partners get backwards. On a
SELL, cash_order_qty = 2000 means **spend 20.00; you are committing $20.00. A partner who
reads it the other way debits the user’s balance and credits it again, and their ledger diverges by
twice the order size on every short.
Rate limits on this call
When you do exceed a limit,Global Rate Limit Exceeded arrives as an execution-report rejection,
not as an HTTP or gRPC error — so an order-placement path that only inspects the RPC result will
record it as accepted. See Order outcomes.
API reference: Trading. Opens on the public documentation site in a new tab.
Where it disagrees with this page, this page is authoritative for the partner surface.
You are always the taker
If you are an IB, this differs.
State two things explicitly to a regulatory reviewer, because neither is reachable here.
There is no modify or replace on the partner surface at all, so an order-handling procedure
you file cannot include one. And day orders cancel at the traded-day roll, with GTD as the
alternative — neither applies on a fill-or-kill-only surface.
The structural consequence of fill-or-kill
CreateVendorOrder accepts TIME_IN_FORCE_FILL_OR_KILL and nothing else, so your orders never rest.
An order that never rests can never be the passive side of a trade. You can only consume resting
depth, and you can never earn the maker rebate.
This is a property of the order surface, not a tier you can be upgraded into or a volume threshold you
can trade through. No configuration changes it.
Both coefficients, and the one you pay
C is the de-scaled contract count and p is the
de-scaled YES price.
Θ is a coefficient, not a percentage of principal.
p × (1 − p) peaks at p = 0.50, so the fee is
largest mid-book and smallest at the extremes:
Two numbers worth carrying into a pricing conversation:
- Your worst case is $0.015 per contract, at
p = 0.50. - A market maker on the other side of that same trade receives 0.018125** at
p = 0.50— the full0.06 − (−0.0125) = 0.0725spread, scaled byp × (1 − p).
Why a thin preprod book blocks you and not a market maker
Taker-only order entry and a shallow book combine badly. A market maker posts resting depth, so it can trade in preprod regardless of how thin the book is. You can only cross depth somebody else posted, so when the book is empty you cannot transact at all. The same environment is fully functional for one partner type and completely blocked for yours. The numbers behind that:pmsimis the live liquidity provider, and symbol coverage is thin and uneven:astatc-about 48%,aec-about 3.7%,aec-atpabout 0.1%. A second liquidity source is configured but not running.- Books are shallow and intermittent — “straight 0 for a few minutes” is normal, not an incident.
- Of 121 open ATP instruments, only a small minority showed any resting depth, and that depth sat at the edges: a 0.06 bid. A fill-or-kill order at a realistic price crosses nothing.
- Preprod markets do not resolve. Instruments reach
INSTRUMENT_STATE_EXPIREDin large batches — 363, 455, 544 and 549 in a single session — with none resolving. One partner asked six times over 40 days to be able to test a settlement flow and never could.
EXPIRED on nearly every preprod order, and treat a preprod fill as a lucky event rather
than a test you can rely on repeating. EXPIRED is not a defect —
Order outcomes explains why it carries no reason.
The only known way to manufacture a preprod fill contradicts the UAT attestation you are asked to
sign. That contradiction is on Cancelling; escalate it rather than choosing a side.