Error Handling
All errors follow a consistent JSON shape:
{ "error": "human-readable description or error code" }Some errors include additional context fields (trade_id, code, etc.).
Auth errors
| Status | Error code | What it means |
|---|---|---|
401 | invalid_api_key | Key not found or malformed |
401 | api_key_expired | Key has passed its expires_at timestamp |
401 | api_key_revoked | Key was explicitly revoked |
403 | ip_not_allowed | Request came from an IP not in the key's allowlist |
403 | insufficient_scope | Key lacks the required scope for this endpoint |
403 | privy_required | Endpoint requires Privy JWT (e.g. wallet/credential management) |
429 | api_key_rate_limited | Per-key rate limit exceeded |
429 | too_many_failed_attempts | Too many failed auth attempts from this IP — blocked for 15 minutes (Retry-After: 900) |
429 | rate_limit_exceeded | General per-IP or per-user request limit hit (applies to all requests, authenticated or not) |
Trade errors
| Status | Error | What it means |
|---|---|---|
400 | Various validation errors | Missing required fields, invalid enum values, missing idempotency key, etc. |
404 | trade not found | Trade ID doesn't exist OR belongs to another user |
200 | "idempotent": true in body | Not an error: the idempotency key was used before — returns the original trade_id |
409 | max N concurrent maker trades on {exchange} — already has M active | Per-(wallet, maker-exchange) concurrency cap hit; N varies by venue |
503 | system is in maintenance mode ("code": "system_maintenance") | System is in full maintenance |
503 | {exchange} is in maintenance mode | A specific exchange is in maintenance and you tried to use it |
Account errors
| Status | Error | What it means |
|---|---|---|
400 | invalid wallet address | Format check failed (not a hex address, wrong length) |
403 | privy_required | Wallet/credential mutations require browser auth |
Server errors
| Status | Error | What it means |
|---|---|---|
500 | Short handler-specific message (e.g. failed to create trade) | Internal error. The API returns a brief generic string, never internal exception details; the real cause is logged server-side |
502 | (generic) | Upstream / load balancer issue. Retry with backoff. |
503 | system is in maintenance mode | See "Trade errors" above |
504 | (generic) | Request timeout. Some endpoints (e.g. event polls during high load) can occasionally time out; retry. |
Retry guidance
- Transient 5xx — exponential backoff with jitter. Start at 1s, double up to 30s.
- 429 — back off; the rate-limit window resets within ~1s for per-key
limits.
too_many_failed_attemptsblocks last 15 minutes — fix the credential rather than retrying. - 401 invalid_api_key — don't retry. Investigate the key.
- 401 api_key_expired — don't retry; the key needs replacing.
- 404 trade not found — don't retry; verify the
trade_id. - Idempotent replay (200 with
idempotent: true) — treat as success.