Skip to main content
Connections to trade.qfex.com require authentication. You must authenticate within 1 minute of connecting or the server will close the connection. Authentication uses an HMAC-SHA256 signature or a valid JWT token.

How it works

  1. Connect to wss://trade.qfex.com?api_key=YOUR_PUBLIC_KEY.
  2. Generate a cryptographically secure random nonce (hex encoded, max 100 characters) and capture the current Unix timestamp.
  3. Build the string ${nonce}:${unix_ts} and compute an HMAC-SHA256 using your secret key. Hex-encode the result to get the signature.
  4. Send the auth payload with the hmac block shown below. The nonce must be unique within a 15 minute window.
  5. If you want the authenticated Trade WebSocket session to use a subaccount, include an optional account_id field alongside either hmac or jwt. When omitted, the connection uses the primary account.
{
  "type": "auth",
  "params": {
    "hmac": {
      "public_key": "qfex_pub_xxxxx",
      "nonce": "c0ffee...",
      "unix_ts": 1760545414,
      "signature": "5f2e..."
    },
    "account_id": "11111111-1111-1111-1111-111111111111"
  }
}
Alternatively, authenticate with a JWT:
{
  "type": "auth",
  "params": {
    "jwt": "eyJhbGciOiJFUzI1NiIsImtpZCI6ImNiYzVmZWNmLTlhODItNGFlNy04NDFkLTBkMTdjMjUzMWM3OCIsInR5cCI6IkpXVCJ9...",
    "account_id": "11111111-1111-1111-1111-111111111111"
  }
}

Sample Code

# Python (websocket-client)
# pip install websocket-client
import hashlib
import hmac
import json
import secrets
import time

import websocket

PUB_KEY = "qfex_pub_xxxxx"
SECRET_KEY = "qfex_secret_yyyyyy"


def build_auth_message():
    nonce = secrets.token_hex(16)
    unix_ts = int(time.time())
    payload = f"{nonce}:{unix_ts}".encode()
    signature = hmac.new(SECRET_KEY.encode(), payload, hashlib.sha256).hexdigest()
    return {
        "type": "auth",
        "params": {
            "hmac": {
                "public_key": PUB_KEY,
                "nonce": nonce,
                "unix_ts": unix_ts,
                "signature": signature,
            }
        },
    }


def on_open(ws):
    ws.send(json.dumps(build_auth_message()))


def on_message(_, message):
    print("Message:", message)


def on_error(_, error):
    print("Error:", error)


def on_close(_, code, reason):
    print("Closed:", code, reason)


ws = websocket.WebSocketApp(
    "wss://trade.qfex.com?api_key=" + PUB_KEY,
    on_open=on_open,
    on_message=on_message,
    on_error=on_error,
    on_close=on_close,
)
ws.run_forever()
// Node.js (ws)
// npm i ws
import crypto from "crypto";
import WebSocket from "ws";

const pubKey = "qfex_pub_xxxxx";
const secretKey = "qfex_secret_yyyyyy";

function buildAuthMessage() {
  const nonce = crypto.randomBytes(16).toString("hex");
  const unixTs = Math.floor(Date.now() / 1000);
  const payload = `${nonce}:${unixTs}`;
  const signature = crypto
    .createHmac("sha256", secretKey)
    .update(payload)
    .digest("hex");

  return {
    type: "auth",
    params: {
      hmac: {
        public_key: pubKey,
        nonce,
        unix_ts: unixTs,
        signature,
      },
    },
  };
}

const ws = new WebSocket(`wss://trade.qfex.com?api_key=${pubKey}`);

ws.on("open", () => {
  ws.send(JSON.stringify(buildAuthMessage()));
});

ws.on("message", (msg) => {
  console.log("Message:", msg.toString());
});

ws.on("error", (err) => console.error("WS error:", err));
ws.on("close", (code, reason) => {
  console.log("Closed:", code, reason?.toString() ?? "");
});
// Go (gorilla/websocket)
// go get github.com/gorilla/websocket
package main

import (
	"crypto/hmac"
	"crypto/rand"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"log"
	"os"
	"os/signal"
	"time"

	"github.com/gorilla/websocket"
)

func main() {
	pubKey := "qfex_pub_xxxxx"
	secretKey := []byte("qfex_secret_yyyyyy")

	c, _, err := websocket.DefaultDialer.Dial("wss://trade.qfex.com?api_key="+pubKey, nil)
	if err != nil {
		log.Fatal("dial:", err)
	}
	defer c.Close()

	nonceBytes := make([]byte, 16)
	if _, err := rand.Read(nonceBytes); err != nil {
		log.Fatal("nonce:", err)
	}
	nonce := hex.EncodeToString(nonceBytes)
	unixTs := time.Now().Unix()
	message := fmt.Sprintf("%s:%d", nonce, unixTs)

	h := hmac.New(sha256.New, secretKey)
	h.Write([]byte(message))
	signature := hex.EncodeToString(h.Sum(nil))

	auth := map[string]any{
		"type": "auth",
		"params": map[string]any{
			"hmac": map[string]any{
				"public_key": pubKey,
				"nonce":      nonce,
				"unix_ts":    unixTs,
				"signature":  signature,
			},
		},
	}

	if err := c.WriteJSON(auth); err != nil {
		log.Fatal("write auth:", err)
	}

	go func() {
		for {
			_, message, err := c.ReadMessage()
			if err != nil {
				log.Println("read:", err)
				return
			}
			fmt.Printf("Message: %s\n", message)
		}
	}()

	interrupt := make(chan os.Signal, 1)
	signal.Notify(interrupt, os.Interrupt)
	<-interrupt
}
// Java (OkHttp WebSocket)
// Gradle: implementation("com.squareup.okhttp3:okhttp:4.12.0")
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.concurrent.TimeUnit;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import okhttp3.*;

public class AuthWs {
  private static final String PUB_KEY = "qfex_pub_xxxxx";
  private static final String SECRET_KEY = "qfex_secret_yyyyyy";

  public static void main(String[] args) {
    OkHttpClient client = new OkHttpClient.Builder()
        .pingInterval(20, TimeUnit.SECONDS)
        .build();

    Request request = new Request.Builder()
        .url("wss://trade.qfex.com?api_key=" + PUB_KEY)
        .build();

    WebSocketListener listener = new WebSocketListener() {
      @Override public void onOpen(WebSocket ws, Response response) {
        ws.send(buildAuthPayload());
      }

      @Override public void onMessage(WebSocket ws, String text) {
        System.out.println("Message: " + text);
      }

      @Override public void onFailure(WebSocket ws, Throwable t, Response r) {
        System.err.println("WS error: " + t.getMessage());
      }
    };

    client.newWebSocket(request, listener);

    try {
      Thread.sleep(Long.MAX_VALUE);
    } catch (InterruptedException ignored) {
      Thread.currentThread().interrupt();
    }
  }

  private static String buildAuthPayload() {
    SecureRandom random = new SecureRandom();
    byte[] nonceBytes = new byte[16];
    random.nextBytes(nonceBytes);
    String nonce = bytesToHex(nonceBytes);

    long unixTs = System.currentTimeMillis() / 1000;
    String data = nonce + ":" + unixTs;

    String signature;
    try {
      Mac mac = Mac.getInstance("HmacSHA256");
      SecretKeySpec keySpec = new SecretKeySpec(SECRET_KEY.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
      mac.init(keySpec);
      signature = bytesToHex(mac.doFinal(data.getBytes(StandardCharsets.UTF_8)));
    } catch (Exception e) {
      throw new IllegalStateException("Unable to sign payload", e);
    }

    return String.format(
        "{\"type\":\"auth\",\"params\":{\"hmac\":{\"public_key\":\"%s\",\"nonce\":\"%s\",\"unix_ts\":%d,\"signature\":\"%s\"}}}",
        PUB_KEY, nonce, unixTs, signature);
  }

  private static String bytesToHex(byte[] bytes) {
    StringBuilder sb = new StringBuilder(bytes.length * 2);
    for (byte b : bytes) {
      sb.append(String.format("%02x", b));
    }
    return sb.toString();
  }
}

Example Response

{ "type": "auth", "result": "success" }
After the client successfully authenticates, the server keeps the connection alive by periodically sending heartbeat messages in the form of WebSocket ping frames.