Errors & limits
08 / 08

Error shape and rate limits

Every failure has the same JSON shape, written by the API's exception filter. Rate limits are counted per route and per client IP.

Updated

The body

{ "statusCode": 401, "message": "Invalid or revoked API key", "path": "/agent/job" }
  • statusCode
    Always
    yes
    Meaning
    The HTTP status, repeated in the body
  • message
    Always
    yes
    Meaning
    A string, or an array of strings when body validation fails
  • code
    Always
    no
    Meaning
    A stable machine-readable code, present only when the error sets one
  • path
    Always
    yes
    Meaning
    The request URL that failed, query string included

There is no request id or timestamp in the body. Log the status, path and your own request time on your side.

Two endpoints return an error object with a success status instead: for a bad type or amount the quote replies {"error": "…"} with a 200 and build-tx with a 201, so check the body on any 2xx. 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
  • 422
    When you get it
    An order that can't take the change: repricing a TWAP, DCA or closed order, or a DCA the wallet can't fund
  • 429
    When you get it
    Throttled — see the limits below
  • 503
    When you get it
    Token detail when the chain read fails and the token isn't indexed yet — retry shortly

Rate limits

  • Default
    Limit
    200 requests / minute per route, per client IP (GET /config reports it as platform.rateLimit)
  • POST /wallet/sign-and-submit
    Limit
    30 / minute per IP, plus 20 signing requests / minute per user, which returns 403 "Signing rate limit exceeded"
  • /auth/login
    Limit
    10 / 15 min
  • /auth/reauth
    Limit
    5 / 15 min
  • POST /wallet/export-key
    Limit
    3 / hour — exporting a private key is throttled hardest
  • Logo reads, IPFS reads, public profiles
    Limit
    Exempt — they're hot paths for every page render

The default is config-driven and can change: read GET /config rather than hardcoding a guess. Throttled routes send X-RateLimit-Limit-global, X-RateLimit-Remaining-global and X-RateLimit-Reset-global; a 429 adds Retry-After-global, the seconds to wait.

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.