Error Handling
Every API response uses standard HTTP status codes with structured JSON error bodies.Error Response Format
By default, the/v1/* surface returns PolySimulator’s stable two-field
envelope: error holds the machine-readable code and message holds the
human-readable description.
For stable error handling, branch on
error or the identical
X-Polysim-Code header, never on message. The X-Request-Id header
echoes the request id for log correlation.
This is a documented PolySimulator extension to Polymarket’s single-field
{"error": "<message>"} shape. The runtime envelope did not change when
this documentation was corrected on 2026-08-29.
402 UPGRADE_REQUIRED is the one enriched response: its body also
adds feature_key and upgrade_url so SDKs can render an upsell flow
without an extra round-trip.
ACCESS_RESTRICTED — an already-issued
key whose account isn’t on the API v1 allowlist, or is flagged / under
review), the body uses the same two-field envelope:
["read", "trade"] key (free keys issued on or after 2026-09-05 trade
against a $100 non-renewable API wallet; free keys issued before that
date stay read-only). 403 TIER_REQUIRES_UPGRADE is the expected
refusal when a free caller asks for a paid tier, for admin, or for
trade while holding an expired paid grant.
CLOSED_BETA / API_PRO_COMING_SOON remain valid residual codes for
an emergency close. The feature_key / upgrade_url hints are body
fields only on 402 responses, not these 403s. See
Open Beta Errors below.
HTTP Status Codes
Common Error Codes
Trading Errors
This table is the canonical trading error-code reference — other trading pages link here rather than restating the codes.There is no
409 LIMIT_PRICE_NOT_MET, 409 IDEMPOTENCY_CONFLICT,
CANNOT_CANCEL, or HTTP_409 trading code — those names appeared in
earlier drafts but are not emitted by the engine. Use the codes above.Deadline, busy, and concurrency errors
Order writes are serialized per user, not per API key. A second write waits briefly for the first; if the lock is still held, it returns409 TRADE_IN_PROGRESS with Retry-After: 1. Separate keys for the same
user do not bypass this lock. Wait, then retry the same logical request
with the same client_order_id.
Order status values
OrderResponse.status (and the status filter on GET /v1/orders) use
the PolySimulator-native enum — note the double-L CANCELLED:
SDKs ported from Polymarket read the PM-shape
ORDER_STATUS_* enum from
GET /v1/data/orders instead — where the cancelled member is the
single-L ORDER_STATUS_CANCELED (PM’s exact spelling). The native
GET /v1/orders path uses double-L CANCELLED; the PM-shape
GET /v1/data/orders path uses single-L ORDER_STATUS_CANCELED. See
CLOB Compatibility.
No naked shorts — INSUFFICIENT_POSITION explained
PolySimulator (and Polymarket itself) requires every SELL order to be backed by an
existing position in that exact token_id. There is no margin, no borrow, no
synthetic short. If you try to sell shares you don’t hold, the order is rejected
with 400 INSUFFICIENT_POSITION (header X-Polysim-Code: INSUFFICIENT_POSITION).
For binary markets (every /markets/{id} with two outcomes), the standard
market-maker idiom is a two-sided buy rather than a buy + a short:
GET /v1/account/positions to check
your inventory per token_id before submitting a SELL.
Authentication Errors
Rate Limit Errors
The backend emits two distinct 429 codes (in theX-Polysim-Code
header). Both are retryable and both carry Retry-After — branch on
either, or simply treat any 429 as a back-off signal:
The per-tier limits (authoritative source:
GET /v1/keys/tiers):
Legacy cohort keys (if any remain) run at the enterprise tier until
their
beta_until cutoff, then auto-downgrade to free + read-only. If
a static value here ever disagrees with GET /v1/keys/tiers, the
endpoint wins.
Open Beta Errors
Default open beta is self-serve. Key issuance is not waitlist-gated:
Legacy beta-issued keys carry an
X-API-Beta-Cutoff response header on every request after the cutoff date — SDKs can pivot to read-only mode without an extra round-trip.
Retry Strategy
WebSocket Error Handling
WebSocket connections use custom close codes:Best Practices
Always check status codes
Never assume a 2xx response. Parse the status code and handle each category appropriately.
Use Retry-After header
On 429 responses, the
Retry-After header tells you exactly how long to wait. Don’t guess.Don't retry 4xx errors
Client errors (400-422) indicate a problem with your request. Fix the payload instead of retrying.
Log error details
Always log the full error response body for debugging — the
details field often contains actionable info.