Errors & limits
08 / 08

Error shape and rate limits

Failures are standard NestJS error bodies — a status code plus a message. Throttling is per endpoint class, not one global bucket.

The body

{ "statusCode": 404, "message": "Token not found: 0x…" }

Some read endpoints return an error object with a 200 instead — the quote and build-tx validators reply {"error": "…"} rather than raising, so check the body even on a 200. Validation errors that reject a request body return a 400 with a message or an array of messages.

Status codes

  • 400
    When you get it
    Body fails validation — bad order params, malformed address, oversized upload fields
  • 401
    When you get it
    A guarded route hit without credentials — missing X-API-Key, or an expired session cookie
  • 403
    When you get it
    Authenticated but refused: read-only key on sign-and-submit, key used from a non-allowlisted IP, polling someone else's agent job
  • 404
    When you get it
    Unknown token, quote asset, stock symbol, or route — 404s are for things that don't exist, not things you're not allowed to see
  • 413
    When you get it
    Image upload over 5 MB
  • 429
    When you get it
    Throttled — see the limits below

Rate limits

  • Global default
    Limit
    From GET /config — platform.rateLimit (requests / window seconds)
  • POST /wallet/sign-and-submit
    Limit
    30 / minute — the tightest limit in the API
  • GET /auth/login
    Limit
    10 / 15 min
  • POST /wallet/export-key
    Limit
    3 / hour — exporting a private key is throttled hardest
  • Logo reads, public profiles
    Limit
    Exempt — they're hot paths for every page render

The global number is config-driven and can change — read GET /config rather than hardcoding a guess. On a 429, back off and retry; there is no Retry-After contract yet.

429 is not a bug to retry around blindly

If a loop is hitting the throttle, the answer is almost always a stream — /tokens/stream pushes every price update over SSE and removes the polling entirely.