Error Codes
All PAX errors return the canonical envelope { ok: false, error: { code, message, details? }, meta: { request_id } }. Reference request_id in support tickets.
{
"ok": false,
"error": {
"code": "MARKET_NOT_FOUND",
"message": "Market m_a1b2c3d4 does not exist or is not visible to your account.",
"details": {
"market_id": "m_a1b2c3d4"
}
},
"meta": {
"request_id": "req_01hjk3n0abcdef",
"server_ts_ms": 1786190400000
}
}
Authentication errors (4xx)
| HTTP | Code | Description | Resolution |
| 401 |
MISSING_AUTH |
No credential header found |
Send X-Api-Key or Authorization: Bearer or 5×POLY_* HMAC headers |
| 401 |
INVALID_KEY |
Key does not exist or is malformed |
Re-check key from dashboard. Note the sk_live_/sk_test_ prefix must match environment. |
| 401 |
WRONG_ENV_KEY |
sk_live_* used on sandbox, or sk_test_* used on production |
Use the matching key for the environment you're calling. See Auth guide. |
| 401 |
KEY_REVOKED |
Key was revoked via DELETE /v1/keys/{id} |
Mint a new key. Revoke takes effect within 5s cluster-wide. |
| 401 |
INVALID_SIGNATURE |
HMAC signature does not match, or timestamp outside 30s skew |
Re-check signing code — see signing examples. Sync your clock (NTP). |
| 401 |
SESSION_EXPIRED |
Session bearer token expired (7d idle or 24h without refresh) |
Re-authenticate via POST /api/auth/login. |
| 403 |
INSUFFICIENT_PERMISSIONS |
Key permissions do not include the required action |
Mint new key with permissions: ["read","trade"]. Revoke old key. |
| 403 |
PII_REDACTED |
Attempted to fetch PII fields (email/phone) via a public path |
Only self-view paths (/v1/account, /v1/account/*) expose PII. |
Validation errors (400)
| Code | Description | Resolution |
MISSING_FIELD | Required field absent from body/query | See details.field. Re-check REST spec for the endpoint. |
INVALID_TYPE | Field has wrong type (e.g., number where string expected) | Money fields must be decimal string, not JSON number. Timestamps in *_at_ms variant are integers. |
INVALID_ENUM | Field value not in allowed set | See details.field + details.allowed. E.g., side must be buy or sell. |
TEMPLATE_PARAMS_INVALID | Params don't match template.params_schema on POST /v1/markets | Fetch template via GET /v1/markets/templates; re-check params_schema for required fields. |
INVALID_ORDER_SIZE | Order size below template.min_bet or above max_bet | See details.min / details.max. |
INVALID_PRICE | Order price outside [0, 1] for probability markets, or not a multiple of tick_size | See details.tick_size. Round to nearest valid tick. |
DUPLICATE_CLIENT_ORDER_ID | Same client_order_id used within past 24h | Use unique IDs (e.g., UUID). PAX guarantees idempotency for retries with same ID. |
Resource errors (404 / 409)
| HTTP | Code | Description | Resolution |
| 404 | MARKET_NOT_FOUND | Market ID does not exist or not visible | Verify market ID via GET /v1/markets. |
| 404 | ORDER_NOT_FOUND | Order ID does not exist or belongs to another account | Verify via GET /v1/account/orders. |
| 404 | TEMPLATE_NOT_FOUND | Template ID does not exist or is deprecated | Fetch active templates via GET /v1/markets/templates. |
| 404 | SANDBOX_ONLY | Endpoint (faucet/reset/mock-oracle) called on production | Use https://sandbox.predictasiax.com. |
| 409 | ORDER_ALREADY_FILLED | Cannot cancel — already filled | No action needed. Check final status via GET /v1/account/orders. |
| 409 | MARKET_CLOSED | Market has locked / settling / resolved | Check market.status field before placing orders. |
Funds errors (402 / 400)
| HTTP | Code | Description | Resolution |
| 402 | INSUFFICIENT_BALANCE | Not enough balance_free to cover order + fee | Check GET /v1/account. Sandbox: POST /v1/sandbox/faucet for 10k test USDT. |
| 402 | INSUFFICIENT_CREATOR_DEPOSIT | Attempted POST /v1/markets but balance below template.min_creator_deposit | Deposit more, or pick a template with lower min_creator_deposit. |
| 400 | SELF_TRADE_PREVENTION | Order would match your own resting order | Cancel resting order first, or set self_trade: cancel_taker on order. |
| HTTP | Code | Description | Resolution |
| 503 | READ_ONLY_MODE | All write endpoints gated by ops team (safety / maintenance) | Check status page. Reads (GET /v1/markets, WS subs) remain available. |
| 503 | CIRCUIT_BREAKER_OPEN | Trading paused on this market/asset (extreme volatility, oracle deviation) | Retry after 30s. WS circuit_breaker_cleared event fires when reopened. |
| 503 | MAINTENANCE | Scheduled maintenance window | See status page for expected duration. |
| 500 | INTERNAL_ERROR | Unexpected server error (bug, DB issue, etc.) | Retry with backoff. Include request_id in support ticket if persistent. |
| 502 | UPSTREAM_ERROR | Downstream dependency failed | Retry with backoff. Auto-recovers usually <30s. |
| 504 | UPSTREAM_TIMEOUT | Downstream did not respond within SLA | Retry once immediately; then backoff. |
Rate limit errors (429)
| Code | Description | Resolution |
RATE_LIMITED | Per-key rate limit exceeded | See Retry-After header (seconds). See Rate limits. |
IP_RATE_LIMITED | Per-IP burst limit exceeded (public reads) | Same as above, plus mint API key to get per-key higher quota. |
GLOBAL_RATE_LIMITED | Global burst on shared resource | Retry after Retry-After. Rare — indicates ops incident. |
WebSocket errors
WS errors arrive as event messages with type: "error":
{ "type": "error", "code": "SUBSCRIPTION_NOT_ALLOWED", "channel": "account", "message": "AUTH required for private channel" }
| Code | Description | Resolution |
SUBSCRIPTION_NOT_ALLOWED | Subscribed to private channel without AUTH | Send {"method":"AUTH","token":"sk_..."} first. |
INVALID_METHOD | Unknown method verb | Only SUBSCRIBE / UNSUBSCRIBE / AUTH / LOCALE supported. |
HEARTBEAT_MISSED | Client did not respond to server ping within 60s | Connection reaped. Reconnect + resubscribe. |
Recommended retry policy
- 4xx errors (except 429) — do not retry. Fix the request.
- 429 — respect
Retry-After header. If absent, exponential backoff starting at 1s, cap 60s.
- 500 / 502 / 504 — retry once immediately. If fails, exponential backoff.
- 503 — respect the specific code:
READ_ONLY_MODE: don't retry until status page clears.
CIRCUIT_BREAKER_OPEN: wait for WS circuit_breaker_cleared.
MAINTENANCE: wait per status page.
Always send client_order_id for POST /v1/orders.
PAX guarantees idempotency within 24h — a network timeout followed by retry with the same client_order_id returns the original order without double-placement.