Code Examples
Signed request helpers, REST examples, WebSocket subscriptions, and integration patterns.
Disclaimer
The following examples are simplified onboarding examples intended for API exploration and integration learning.
They are NOT production-ready trading bots.
Production systems should additionally implement:
-
reconnect handling
-
rate limit tracking
-
persistence
-
retry policies
-
risk management
-
monitoring
-
security hardening
Python helper example for signed requests
import base64
import hashlib
import json
import time
from typing import Optional
import requests
from nacl.signing import SigningKey
BASE_URL = "https://api.exness.com"
API_KEY = "<EXN_API_KEY>"
# Private key bytes must be loaded securely.
# Do not hardcode private keys in production code.
PRIVATE_KEY_HEX = "<ed25519_private_key_hex>"
def b64url_no_padding(data: bytes) -> str:
return base64.urlsafe_b64encode(data).decode("ascii").rstrip("=")
def body_hash(body: bytes) -> str:
return b64url_no_padding(hashlib.sha256(body).digest())
def sign_request(
method: str,
path: str,
body: Optional[dict] = None,
idempotency_key: str = "",
) -> dict:
method = method.upper()
timestamp = int(time.time() * 1000)
if body is None:
body_bytes = b""
else:
body_bytes = json.dumps(body, separators=(",", ":"), ensure_ascii=False).encode("utf-8")
payload = {
"api_key": API_KEY,
"idempotency_key": idempotency_key,
"timestamp": timestamp,
"sign_version": 1,
"method": method,
"path": path,
"body_hash": body_hash(body_bytes),
}
payload_bytes = json.dumps(payload, separators=(",", ":"), ensure_ascii=False).encode("utf-8")
signing_key = SigningKey(bytes.fromhex(PRIVATE_KEY_HEX))
signature = signing_key.sign(payload_bytes).signature
return {
"headers": {
"EXN-API-KEY": API_KEY,
"EXN-IDEMPOTENCY-KEY": idempotency_key,
"EXN-TIMESTAMP": str(timestamp),
"EXN-SIGN-VERSION": "1",
"EXN-DATA": b64url_no_padding(payload_bytes),
"EXN-SIGN": b64url_no_padding(signature),
"Content-Type": "application/json",
},
"body_bytes": body_bytes,
}
def signed_get(path: str):
signed = sign_request("GET", path, body=None, idempotency_key="")
return requests.get(BASE_URL + path, headers=signed["headers"])
def signed_post(path: str, body: dict, idempotency_key: str):
signed = sign_request("POST", path, body=body, idempotency_key=idempotency_key)
return requests.post(BASE_URL + path, headers=signed["headers"], data=signed["body_bytes"])
Python: open position and wait for transaction event
import asyncio
import json
import uuid
import websockets
def open_position():
client_request_id = str(uuid.uuid4())
body = {
"instrument": "EURUSD",
"side": "buy",
"volume": "0.01",
"comment": "Test position",
}
ack = api_post(
path=f"/v1/trading/accounts/{ACCOUNT_ID}/positions",
body=body,
idempotency_key=client_request_id,
)
print("ACK:", ack)
return ack["operation_id"], ack.get("client_request_id", client_request_id)
async def listen_transactions(expected_operation_id: str):
ws_path = f"/v1/server-events/accounts/{ACCOUNT_ID}/ws/events"
ws_url = WS_BASE_URL + ws_path
async with websockets.connect(
ws_url,
additional_headers=signed_ws_headers(ws_path),
) as ws:
await ws.send(json.dumps({
"id": "subscribe-transactions",
"subscribe": {
"event": "transactions"
}
}))
while True:
raw_message = await ws.recv()
message = json.loads(raw_message)
print("WS:", message)
if message.get("event_type") == "transaction_event":
if message.get("operation_id") == expected_operation_id:
print("Matched transaction:", message)
return message
async def main():
operation_id, client_request_id = open_position()
transaction = await listen_transactions(operation_id)
if transaction.get("status") == "success":
print("Position opened successfully")
else:
print("Operation was not successful:", transaction)
asyncio.run(main())
Python: place pending order
import uuid
client_request_id = str(uuid.uuid4())
body = {
"type": "limit",
"instrument": "EURUSD",
"side": "buy",
"volume": "0.01",
"price": "1.0800",
"stop_loss_price": "1.0750",
"take_profit_price": "1.0900",
"comment": "Buy limit for test",
}
ack = api_post(
path=f"/v1/trading/accounts/{ACCOUNT_ID}/orders",
body=body,
idempotency_key=client_request_id,
)
print("ACK:", ack)
JavaScript: helper for signed WebSocket requests
const crypto = require("crypto");
const BASE_URL = "https://api.exness.com";
const WS_BASE_URL = "wss://api.exness.com";
const ACCOUNT_ID = "<account_id>";
const API_KEY = "<EXN_API_KEY>";
// Private key must be loaded securely.
// Do not hardcode private keys in production code.
const PRIVATE_KEY_HEX = "<ED25519_PRIVATE_KEY_HEX>";
function b64urlNoPadding(buffer) {
return Buffer.from(buffer)
.toString("base64url")
.replace(/=+$/, "");
}
function bodyHash(bodyBuffer) {
return b64urlNoPadding(crypto.createHash("sha256").update(bodyBuffer).digest());
}
function compactJson(data) {
return Buffer.from(JSON.stringify(data));
}
function signRequest(method, path, body = null, idempotencyKey = "") {
const timestamp = Date.now();
const bodyBuffer = body === null
? Buffer.from("")
: compactJson(body);
const payload = {
api_key: API_KEY,
idempotency_key: idempotencyKey,
timestamp,
sign_version: 1,
method: method.toUpperCase(),
path,
body_hash: bodyHash(bodyBuffer)
};
const payloadBuffer = compactJson(payload);
const privateKey = crypto.createPrivateKey({
key: Buffer.from(PRIVATE_KEY_HEX, "hex"),
format: "der",
type: "pkcs8"
});
const signature = crypto.sign(null, payloadBuffer, privateKey);
return {
headers: {
"EXN-API-KEY": API_KEY,
"EXN-IDEMPOTENCY-KEY": idempotencyKey,
"EXN-TIMESTAMP": String(timestamp),
"EXN-SIGN-VERSION": "1",
"EXN-DATA": b64urlNoPadding(payloadBuffer),
"EXN-SIGN": b64urlNoPadding(signature),
"Content-Type": "application/json"
},
bodyBuffer
};
}
function signedWsHeaders(path) {
return signRequest("GET", path, null, "").headers;
}
Python: subscribe to live XAUUSD ticks
import base64
import hashlib
import json
import time
from nacl.signing import SigningKey
import websocket
API_KEY = "<EXN_API_KEY>"
PRIVATE_KEY_HEX = "<ED25519_PRIVATE_KEY_HEX>"
ACCOUNT_ID = "<account_id>"
WS_BASE_URL = "wss://<resolved_access_point_host>"
def b64url_no_padding(data: bytes) -> str:
return base64.urlsafe_b64encode(data).decode("ascii").rstrip("=")
def generate_signed_ws_headers(signing_path: str) -> list[str]:
timestamp = int(time.time() * 1000)
body_bytes = b""
body_hash = b64url_no_padding(hashlib.sha256(body_bytes).digest())
payload = {
"api_key": API_KEY,
"idempotency_key": "",
"timestamp": timestamp,
"sign_version": 1,
"method": "GET",
"path": signing_path,
"body_hash": body_hash,
}
payload_bytes = json.dumps(payload, separators=(",", ":"), ensure_ascii=False).encode("utf-8")
signing_key = SigningKey(bytes.fromhex(PRIVATE_KEY_HEX.strip())[:32])
signature = signing_key.sign(payload_bytes).signature
return [
f"EXN-API-KEY: {API_KEY}",
"EXN-IDEMPOTENCY-KEY: ",
f"EXN-TIMESTAMP: {timestamp}",
"EXN-SIGN-VERSION: 1",
f"EXN-DATA: {b64url_no_padding(payload_bytes)}",
f"EXN-SIGN: {b64url_no_padding(signature)}",
]
def stream_xauusd_ticks() -> None:
ticks_path = f"/v1/server-events/accounts/{ACCOUNT_ID}/ws/ticks"
ws_url = f"{WS_BASE_URL}{ticks_path}"
ws = websocket.create_connection(ws_url, header=generate_signed_ws_headers(ticks_path))
ws.send(json.dumps({
"id": "ticks-xauusd-1",
"subscribe": {
"event": "ticks",
"instruments": ["XAUUSD"],
},
}))
while True:
message = json.loads(ws.recv())
print(message)
stream_xauusd_ticks()
JavaScript: ticks bot
const WebSocket = require("ws");
const ticksPath = `/v1/server-events/accounts/${ACCOUNT_ID}/ws/ticks`;
const ticksUrl = `${WS_BASE_URL}${ticksPath}`;
const ws = new WebSocket(ticksUrl, {
headers: signedWsHeaders(ticksPath)
});
ws.on("open", () => {
ws.send(JSON.stringify({
id: "ticks-1",
subscribe: {
event: "ticks",
instruments: ["EURUSD", "GBPUSD"]
}
}));
});
ws.on("message", (data) => {
const message = JSON.parse(data.toString());
if (message.instrument === "EURUSD") {
console.log("EURUSD tick:", {
bid: message.bid,
ask: message.ask,
timestamp: message.timestamp
});
}
});
ws.on("error", (error) => {
console.error("WebSocket error:", error);
});
ws.on("close", (code, reason) => {
console.log("WebSocket closed:", code, reason.toString());
});
JavaScript: account equity monitor
const WebSocket = require("ws");
const eventsPath = `/v1/server-events/accounts/${ACCOUNT_ID}/ws/events`;
const eventsUrl = `${WS_BASE_URL}${eventsPath}`;
const ws = new WebSocket(eventsUrl, {
headers: signedWsHeaders(eventsPath)
});
ws.on("open", () => {
ws.send(JSON.stringify({
id: "account-state-1",
subscribe: {
event: "account_state"
}
}));
});
ws.on("message", (data) => {
const message = JSON.parse(data.toString());
if (message.event_type === "account_state_event") {
const accountState = message.payload.account_state;
console.log("Equity:", accountState.equity);
console.log("Balance:", accountState.balance);
console.log("Used margin:", accountState.used_margin);
}
});
ws.on("error", (error) => {
console.error("WebSocket error:", error);
});
ws.on("close", (code, reason) => {
console.log("WebSocket closed:", code, reason.toString());
});
Python: get available instruments and subscribe to updates
import asyncio
import json
import websockets
def get_available_instruments():
response = api_get(f"/v1/configuration/accounts/{ACCOUNT_ID}/instruments")
return response["instruments"]
async def subscribe_instruments(instruments):
ws_path = f"/v1/server-events/accounts/{ACCOUNT_ID}/ws/events"
ws_url = WS_BASE_URL + ws_path
async with websockets.connect(
ws_url,
additional_headers=signed_ws_headers(ws_path),
) as ws:
await ws.send(json.dumps({
"id": "instrument-subscription-1",
"subscribe": {
"event": "instruments",
"instruments": instruments
}
}))
while True:
message = json.loads(await ws.recv())
if message.get("event_type") == "instrument_event":
print("Instrument update:", message)
async def main():
instruments = get_available_instruments()
selected = instruments[:2]
print("Subscribing to instrument updates:", selected)
await subscribe_instruments(selected)
asyncio.run(main())
Python: Subscribe to HMR
import asyncio
import json
import websockets
def get_available_instruments():
response = api_get(f"/v1/configuration/accounts/{ACCOUNT_ID}/instruments")
return response["instruments"]
async def subscribe_hmr(instruments):
ws_path = f"/v1/server-events/accounts/{ACCOUNT_ID}/ws/events"
ws_url = WS_BASE_URL + ws_path
async with websockets.connect(
ws_url,
additional_headers=signed_ws_headers(ws_path),
) as ws:
await ws.send(json.dumps({
"id": "hmr-subscription-1",
"subscribe": {
"event": "hmr",
"instruments": instruments
}
}))
hmr_periods_by_id = {}
while True:
message = json.loads(await ws.recv())
if message.get("event_type") == "hmr_snapshot":
hmr_periods_by_id = {
period["period_id"]: period
for period in message.get("periods", [])
}
print("HMR snapshot received")
print("Current HMR periods:", hmr_periods_by_id)
elif message.get("event_type") == "hmr_update":
for period in message.get("upserted_periods", []):
hmr_periods_by_id[period["period_id"]] = period
for period_id in message.get("removed_period_ids", []):
hmr_periods_by_id.pop(period_id, None)
print("HMR update received")
print("Current HMR periods:", hmr_periods_by_id)
async def main():
available = get_available_instruments()
selected = [
instrument
for instrument in ["EURUSD", "XAUUSD"]
if instrument in available
]
if not selected:
raise RuntimeError("No selected HMR instruments are available for this account")
print("Subscribing to HMR for:", selected)
await subscribe_hmr(selected)
asyncio.run(main())
Python: Subscribe to Transactions
import asyncio
import json
import websockets
async def subscribe_transactions():
ws_path = f"/v1/server-events/accounts/{ACCOUNT_ID}/ws/events"
ws_url = WS_BASE_URL + ws_path
async with websockets.connect(
ws_url,
additional_headers=signed_ws_headers(ws_path),
) as ws:
await ws.send(json.dumps({
"id": "transactions-subscription-1",
"subscribe": {
"event": "transactions"
}
}))
while True:
message = json.loads(await ws.recv())
if message.get("event_type") == "trading_state_snapshot":
print("Initial trading snapshot:", message)
elif message.get("event_type") == "transaction_event":
print("Transaction event:", message)
asyncio.run(subscribe_transactions())
Python: Correlate ACK and transaction_event
ack = {
"operation_id": "789",
"client_request_id": "order-20260316-0001",
"status": "accepted"
}
event = {
"event_type": "transaction_event",
"operation_id": "789",
"client_request_id": "order-20260316-0001",
"status": "success"
}
if ack["operation_id"] == event.get("operation_id"):
print("Matched by operation_id")
if ack.get("client_request_id") == event.get("client_request_id"):
print("Matched by client_request_id")
Python: Poll Operation Status
operation_id = "789"
status = api_get(
f"/v1/trading/accounts/{ACCOUNT_ID}/operations/{operation_id}"
)
print("Operation status:", status)
if status["status"] == "confirmed":
print("Operation completed successfully")
elif status["status"] in ("rejected", "failed"):
print("Operation did not complete successfully")
print("Error code:", status.get("error_code"))
print("Error message:", status.get("error_message"))
else:
print("Operation is still pending")
Python: Get Rate Limits
limits = api_get(
f"/v1/configuration/accounts/{ACCOUNT_ID}/rate-limits"
)
print("Rate limits:", limits)