Skip to main content
All websocket channels may return an error frame instead of the expected response. Error frames share a single, consistent schema.

Error Envelope

{
  "err": {
    "error_code": "RateLimited",
    "message": "You have exceeded the allowed message rate.",
    "incoming_message": {
      "type": "add_order",
      "params": {
        "symbol": "AAPL",
        "side": "BUY",
        "order_type": "LIMIT",
        "order_time_in_force": "GTC",
        "quantity": 10.0,
        "price": 200.0,
        "client_order_id": "my-order-001",
        "take_profit": null,
        "stop_loss": null
      }
    }
  }
}

Fields

FieldTypeRequiredDescription
error_codestringYesMachine-readable error code (see list below). Matches server enum variant exactly.
messagestring/nullNoHuman-readable context. May be null.
incoming_messageobject/nullNoEcho of the request that caused the error (your original message), when available.
Note: incoming_message uses the same tagged format as requests ({ "type": "<snake_case>", "params": { ... } }).

Error Codes

These values map 1:1 to the server enum:
CodeWhen it happensSuggested Client Action
RateLimitedYou’ve exceeded the allowed message rate (weighted per message type).Backoff and retry later. Consider batching and respecting published limits.
InvalidJSONFormatMessage is not valid JSON or doesn’t match the expected schema.Validate JSON and schema before sending. Include required fields and correct types/enum values.
AlreadyAuthenticatedYou attempted to authenticate again after a successful auth on the same connection.Do not re-auth on an already authenticated socket.
InvalidParameterOne or more parameters are out of range, missing, or invalid for the specified message.Correct the parameter(s) and retry.
PermissionDeniedThe API key or user lacks permission for the requested action.Check key scopes/roles; contact support if needed.
ServerErrorUnexpected internal error.Safe to retry after a short delay; if persistent, contact support with timestamp and incoming_message.
InvalidOrderThe order is invalid or malformed.Check order parameters and format; correct the order and retry.
AlreadySubscribedToLeaderboardThe user is already subscribed to a leaderboard.Do not attempt to subscribe again; check current subscriptions first.
KycRequiredThe user needs to complete KYC verification.Complete KYC verification before proceeding with the requested action.
TncRequiredThe user needs to agree to the latest Terms and Conditions.Agree to the latest Terms and Conditions.

Rate limiting notes

The server computes message weight by the message "type" (e.g., "add_order", "cancel_order") and applies limits over a sliding window. Some messages are heavier than others. If you see RateLimited, delay and reduce frequency; prefer fewer, larger requests when possible.

Examples

1) Invalid JSON

{
  "err": {
    "error_code": "InvalidJSONFormat",
    "message": "Expected field `symbol` (string).",
    "incoming_message": {
      "type": "add_order",
      "params": {
        "side": "BUY",
        "order_type": "LIMIT",
        "order_time_in_force": "GTC",
        "quantity": 10.0,
        "price": 200.0
      }
    }
  }
}

2) Already authenticated

{
  "err": {
    "error_code": "AlreadyAuthenticated",
    "message": "Connection is already authenticated.",
    "incoming_message": { "type": "auth", "params": { "api_key": "****" } }
  }
}

3) Invalid parameter

{
  "err": {
    "error_code": "InvalidParameter",
    "message": "price must be >= min_tick and within price bands.",
    "incoming_message": {
      "type": "modify_order",
      "params": {
        "symbol": "AAPL",
        "order_id": "cd1a2b0c-...-f3",
        "quantity": 10.0,
        "price": 0.0001,
        "take_profit": 0.0,
        "stop_loss": 0.0,
        "side": "BUY"
      }
    }
  }
}

4) Permission denied

{
  "err": {
    "error_code": "PermissionDenied",
    "message": "API key does not allow trading on this account.",
    "incoming_message": {
      "type": "transfer_balance",
      "params": { "amount": 100.0, "to_api_key": "****" }
    }
  }
}

5) Rate limited

{
  "err": {
    "error_code": "RateLimited",
    "message": "Too many requests. Please retry after 200ms.",
    "incoming_message": { "type": "cancel_all_orders", "params": {} }
  }
}

6) Server error

{
  "err": {
    "error_code": "ServerError",
    "message": "Unexpected error processing request.",
    "incoming_message": { "type": "get_user_trades", "params": { "limit": 50 } }
  }
}

Client Handling Patterns

import json
import websocket

def on_message(ws, message):
    msg = json.loads(message)
    if msg.get("type") == "error":
        code = msg.get("error_code")
        print(f"[ERROR] {code}: {msg.get('message')}")
        # basic strategy
        if code == "RateLimited":
            # implement backoff before retrying your last request
            return
        elif code in ("InvalidJSONFormat", "InvalidParameter"):
            # fix your payload; don't auto-retry
            return
        elif code in ("PermissionDenied", "AlreadyAuthenticated"):
            # adjust flow; no retry
            return
        else:
            # ServerError or unknown: small backoff and optional retry
            return
    else:
        print("OK:", msg)
import WebSocket from "ws";

const ws = new WebSocket("wss://trade.qfex.com?api_key=YOUR_API_KEY");

ws.on("message", (raw) => {
  const msg = JSON.parse(raw.toString());
  if (msg.type === "error") {
    const { error_code, message } = msg;
    console.error(`[ERROR] ${error_code}: ${message || ""}`);

    switch (error_code) {
      case "RateLimited":
        // schedule retry w/ backoff
        break;
      case "InvalidJSONFormat":
      case "InvalidParameter":
        // fix payload, do not blind-retry
        break;
      case "PermissionDenied":
      case "AlreadyAuthenticated":
        // update client state/flow
        break;
      default:
        // transient server error: optional retry with jitter
        break;
    }
    return;
  }

  // handle normal messages…
});
type ErrorFrame struct {
    Type            string          `json:"type"`
    ErrorCode       string          `json:"error_code"`
    Message         *string         `json:"message,omitempty"`
    IncomingMessage json.RawMessage `json:"incoming_message,omitempty"`
}

func handleMessage(raw []byte) {
    // peek at "type"
    var meta struct{ Type string `json:"type"` }
    if err := json.Unmarshal(raw, &meta); err != nil { return }

    if meta.Type == "error" {
        var ef ErrorFrame
        if err := json.Unmarshal(raw, &ef); err == nil {
            log.Printf("[ERROR] %s: %s", ef.ErrorCode, deref(ef.Message))
            // handle per code...
        }
        return
    }

    // normal flow...
}

Authentication & Errors

  • trade.qfex.com requires authentication within 1 minute of connecting.
  • Attempting to re-authenticate an already authenticated connection yields AlreadyAuthenticated.

Troubleshooting Checklist

  1. Validate JSON (types/enums/required fields).
  2. Respect rate limits (reduce burstiness; batch where possible).
  3. Ensure your API key has the right permissions.
  4. Log incoming_message from error frames to quickly reproduce issues.
  5. For persistent ServerError, contact [email protected] with timestamp and offending incoming_message.