Prerequisites: preprod credentials and a preprod keypair (Authentication),
both of your firm names
(Firms, participants and accounts), and — for the test
harness — a funded preprod pool. Preprod access does not require a signed agreement. One partner
lost roughly three weeks believing it did.
What preprod does and does not simulate
Fidelity matrix
The endpoints themselves:
The gRPC hostname form is
grpc-api.{env}.polymarketexchange.com. The form grpc-preprod.polymarketexchange.com appears on the current /trader-guide/environments page and is wrong; it has been wrong since the correction in changelog v0.0.25 on 2026-04-17 missed that page.
Liquidity: what is actually on the book
pmsim is the live liquidity provider in preprod. Symbol coverage is thin and uneven: roughly 48% of astatc- symbols, 3.7% of aec- symbols and 0.1% of aec-atp symbols carry any LP presence at all. Coverage is per environment; production liquidity comes from market makers and does not follow these numbers.
Depth, where it exists, sits at the edges of the book. Of 121 open ATP instruments in one sweep, only a small minority showed any resting depth, and those showed it at prices like a 0.06 bid. Books also go empty for minutes at a time.
Why a thin book blocks you completely and does not affect market makers
CreateVendorOrder accepts TIME_IN_FORCE_FILL_OR_KILL and nothing else, so you are structurally always the taker and can only consume resting depth. With no resting depth there is nothing to take, and your order comes back EXPIRED — an FOK that never crossed, which is not a rejection and carries no reason by design. Market makers are unaffected because they post the resting depth rather than taking it, which is why “preprod is fine” and “preprod is unusable” are both true statements from different partners.
The practical consequence: you cannot produce a fill on demand in preprod, so you cannot deterministically test anything downstream of a fill. See Test harness for the workarounds that exist.
Resolution: markets do not resolve
No market resolves in preprod. Instruments reachINSTRUMENT_STATE_EXPIRED in large batches — 363, 455, 544 and 549 instruments in a single observed session — with none resolving to an outcome.
One partner asked six times over 40 days to be able to test a settlement flow and never could. If your plan includes a preprod settlement test, it does not currently work; raise it in Slack as a resolution request against a specific market you already hold rather than waiting for one to resolve on its own.
atpcq is absent from the preprod resolution config, so it will not resolve even if a resolution is run for you.
Non-parity items to design around
account_beneficial_owneris unsupported in preprod. Do not treat a preprod pass on an onboarding payload as proof the field works in production.atpcqis absent from the resolution config. Pick a different series for anything resolution-shaped.priceScale100 and 1000 are both live in production, and two of 121 open ATP instruments publishpriceScale: 1000withfractionalQtyScale: 100. Whichever instruments preprod happens to serve you is not a sample of production scale combinations. Read both scales per instrument. → Money on the wire- Tennis
home_team_name,away_team_nameandtournament_nameare populated in preprod. The same fields have been reported empty in production.
Preprod-only error strings
Neither string indicates a fault in your client. Both are worth reporting verbatim, with the environment named.
Funding a preprod pool
There is no self-service test funding. Preprod pools are funded manually by us on a Slack request. Ask in your shared channel, name the firm, and ask for an amount that covers your whole test plan rather than one run — a request per run costs you a round trip through a human each time. Test cash behaves like production cash once it is there: you move it into a participant account withTransfer, and it stays in that account. → Move cash
Maintenance and data retention
The FAQ presents a stable Thursday 2am–4am ET window. Through August and September 2026 preprod actually ran near-weekly one-off windows at 2–8am, 3–7am, 4–7am and 5–8am. Stored execution history is archived during maintenance, and execution queries for the period before a window return empty. This is a retention fact, not a bug: if your reconciliation test reads yesterday’s executions after an overnight window, it will legitimately read nothing. Persist your own copy of every execution as it arrives on drop copy and reconcile against that. → Daily reconciliationWhat preprod does not give you
- No preprod status page.
status.polymarketexchange.comcovers production only. - No working preprod health endpoint.
GET /v1/healthis documented as{"status":"ok"}with no auth, but partners have reported 404 in production and 401 in preprod against it. - No published maintenance calendar, per above.
Test harness
This section tells you which parts of your integration you can drive to a known outcome on demand, and which you cannot, so you size your test plan against what exists rather than what you would expect to exist.Start from the examples repo
github.com/Polymarket-US/partner-api-examples contains runnable scripts plus a full end-to-end test covering order placement, execution and balance update via drop copy. It is not linked from any other docs page, which is why most partners have never seen it. Clone it before you write your own harness; it is the only worked example of the full money path we publish.
The repo is not a substitute for pinning your protos. The proto bundle is an unversioned, anonymous Drive zip with no changelog and no checksum, so neither side can tell which build you hold. → Protos and SDKs
What you can deterministically test
The fixtures that do not exist
Say this plainly, because four partners have each built a plan around one of them: the KYC magic-value pattern has no equivalent for orders, transfers, market state or settlement. There is no way to ask the platform for a fill, a rejection, a state transition or a resolution. Four partners have asked for an on-demand “resolve this market” facility by name. It does not exist. One asked six times over 40 days and never got a settlement test.Workarounds that actually work
Self-seed depth from a second preprod session
Because you are always the taker, the only way to get a fill is for depth to exist at your price. You can create that depth yourself: run a second preprod session that rests an order through the legacyOrderEntryAPI/insertOrder, then take it with CreateVendorOrder from your integration session.
OrderEntryAPI is exactly CreateOrderSubscription, InsertOrder and CancelOrder. Day orders cancel at the traded-day roll as of 2026-09-13 (v0.0.89), so a seeded resting order does not survive the roll; use GTD or re-seed each day.
Ask for a resolution on a market you already hold
Take a position first, then ask in Slack for that specific symbol to be resolved. Asking for “a market that resolves” produces nothing, because none do. Do not pick anatpcq symbol — atpcq is absent from the preprod resolution config.
Ask for test funds in one request
Preprod pools are funded manually by us on a Slack request. Ask once, for the whole test plan.Test data rules that will bite you
One SSN maps to exactly one exchange account, platform-wide, because the trading account ID is derived from it. Reusing a test SSN across test users points all of them at a single account, which breaks transfers withNOT_FOUND and requires manual database cleanup by us. That failure produced a 66-reply incident thread. Generate a distinct SSN per test user and keep a registry of which ones you have burned, because there is no re-KYC, reset or PII-update path — a user whose SSN is already in use cannot be re-KYC’d by you.
Other constraints your generator must respect:
external_idis max 49 characters.postal_codemust be five digits.- Requests are
snake_case, REST responses arecamelCase, webhooks aresnake_case. A test that asserts one casing across all three will fail on two of them. - Set
docv_eligible: trueunless you are deliberately testing the slow path. Without it, REVIEW cases go to manual review, 1–2 business days, which will stall a test run for two days. - Treat the Digital Intelligence
session_tokenas required even though it is optional on paper. It is what keeps the REVIEW rate down.
kyc.approved webhook before you enable trading for a test user. On an ACCEPT, participantId can come back empty while provisioning finishes, with observed gaps from ~500 ms up to 26 minutes. A harness that reads participantId straight off the ACCEPT response will pass locally and fail in CI. → Firms, participants and accounts
Assertions worth building into the harness
- Every order response asserts on
statusbeing one of exactlyACCEPTED,REJECTED,PENDING. An exchange-level rejection arrives as gRPCOKwithstatus = REJECTED, so a test that only checks the gRPC status code will pass on a rejected order. - Every de-scaling assertion reads
priceScaleandfractionalQtyScaleoff the instrument in the same test, and asserts the divisor is their product. A hard-coded 10,000 passes on most instruments and is wrong by 10× on the rest. - Every fee assertion computes
Fee = Θ × C × p × (1 − p)withCas the de-scaled contract count. Using raworder_qtyon afractional_quantity_scale = 100instrument overstates the fee 100×, and instruments with scale1make the naive math accidentally correct, so this bug hides until your first scale-100 fill. - Every stream test counts open streams against the 20 concurrent streams per firm cap, across all gRPC subscriptions, and asserts you are under it before opening another.