Skip to main content

Error Handling

Every API response uses standard HTTP status codes with structured JSON error bodies.

Error Response Format

By default, the /v1/* surface returns the Polymarket-shape single-field envelope — one error key holding a human-readable description:
For stable error handling, branch on the X-Polysim-Code header, not on the body text — the body holds the prose, the header holds the code. The X-Request-Id header echoes the request id for log correlation. 402 UPGRADE_REQUIRED is the one carve-out to the PM-shape default: its body adds feature_key and upgrade_url alongside error so SDKs can render an upsell flow without an extra round-trip. Every other status code (401, 403, 404, 429, 5xx, …) sticks with the single-field shape and exposes the machine code via X-Polysim-Code.
For the runtime allowlist gate (ACCESS_RESTRICTED — an already-issued key whose account isn’t on the API v1 allowlist, or is flagged / under review), the body is PM-shape and the code is in the header:
The closed-beta key-issuance gate uses the same shape: POST /v1/keys and POST /v1/keys/bootstrap return 403 CLOSED_BETA (the default for every non-admitted caller, including paying Pro / Pro+; the API_PRO_COMING_SOON variant appears only once self-serve issuance is enabled) with the machine code in the X-Polysim-Code header and the human message in the body’s error field. The feature_key / upgrade_url hints are body fields only on 402 responses, not these 403s. See Closed Beta Errors below.
Verbose body opt-in. Send X-Polysim-Verbose: true on any request to get the legacy multi-field shape:
Useful when writing or debugging an SDK; PM-shape is the default so Polymarket-CLOB SDK ports work without translation.

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.

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:
After a buy fills you accumulate position; subsequent SELLs against that position are accepted up to the held quantity. Use 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 the X-Polysim-Code header). Both are retryable and both carry Retry-After — branch on either, or simply treat any 429 as a back-off signal:
A bot that branches only on RATE_LIMIT_EXCEEDED will miss the RATE_LIMITED 429s from the trade-write paths (and vice-versa). The robust pattern is to back off on resp.status_code == 429 regardless of which code is in the header.
The per-tier limits (authoritative source: GET /v1/keys/tiers): Closed-beta cohort keys run at the enterprise tier until the cutoff (2026-08-31), then auto-downgrade to free + read-only. If a static value here ever disagrees with GET /v1/keys/tiers, the endpoint wins.

Closed Beta Errors

The API is in an ongoing closed beta. Key issuance is cohort-gated: 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.