Get Order History (WebSocket)
Retrieve order history for the authenticated subaccount through the WebSocket connection with comprehensive filtering, sorting, and pagination capabilities, providing access to orders in any status (open, filled, partiallyFilled, cancelled, rejected, expired) with flexible query options.
Request-Response vs Subscriptions
This method provides on-demand snapshots of order data with flexible filtering. For real-time updates when orders change, use SubAccount Updates Subscription.
| Method | Purpose | When to Use |
|---|---|---|
getOrderHistory | Comprehensive order querying | Initial load, order history, filtered searches, reconciliation |
| Order Updates Subscription | Real-time notifications | Live UI updates, event handling |
Endpoint
ws.send() wss://api.synthetix.io/v1/ws/tradeRequest
Request Format
{
"id": "getorders-1",
"method": "post",
"params": {
"action": "getOrderHistory",
"status": ["open", "filled"],
"symbol": "BTC-USDT",
"fromTime": 1704067200000,
"toTime": 1704153600000,
"limit": 100,
"offset": 0,
"sortBy": "createdTime",
"sortOrder": "desc",
"subAccountId": "1867542890123456789",
"nonce": 1704067200000,
"signature": {
"v": 28,
"r": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
"s": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890"
}
}
}Parameters
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Client-generated unique request identifier |
method | string | Yes | Must be "post" |
params | object | Yes | Contains all parameters for the request |
Params Object
| Parameter | Type | Required | Description |
|---|---|---|---|
action | string | Yes | Must be "getOrderHistory" |
status | string[] | No | Filter by order status: ["open", "filled", "partiallyFilled", "cancelled", "rejected", "started", "cancelling", "modifying", "unknown"] |
symbol | string | No | Filter orders by specific market symbol |
fromTime | integer | No | Start timestamp in milliseconds (inclusive) |
toTime | integer | No | End timestamp in milliseconds (inclusive) |
limit | integer | No | Maximum number of orders to return (default: 50, max: 1000) |
offset | integer | No | Number of orders to skip for pagination (default: 0) |
sortBy | string | No | Sort field: "createdTime", "updatedTime", "filledQuantity" (default: "createdTime") |
sortOrder | string | No | Sort direction: "asc" or "desc" (default: "desc") |
subAccountId | string | Yes | Subaccount identifier |
nonce | integer | Yes | Incrementing nonce (Unix ms timestamp as number) |
signature | object | Yes | EIP-712 signature |
Filter Combinations
The endpoint supports powerful filter combinations:
- All Orders: Omit
statusto get orders in any status - Open Orders Only:
"status": ["open"] - Historical Orders:
"status": ["filled", "cancelled"]with time range - Recent Activity: Use
fromTimewithouttoTimefor orders since a specific time - Symbol-Specific: Combine
symbolwith any other filters
Response Format
Success Response
{
"id": "getorders-1",
"status": 200,
"result": [
{
"orderId": "1958787130134106112",
"symbol": "BTC-USDT",
"side": "buy",
"type": "LIMIT",
"quantity": "10.0",
"price": "45000.00",
"triggerPrice": "",
"triggerPriceType": "",
"timeInForce": "GTC",
"reduceOnly": false,
"postOnly": false,
"closePosition": false,
"createdTime": 1755846234000,
"updatedTime": 1755846234000,
"filledQuantity": "10.0"
},
{
"orderId": "1958787130134106113",
"symbol": "ETH-USDT",
"side": "sell",
"type": "LIMIT",
"quantity": "5.0",
"price": "2800.00",
"triggerPrice": "",
"triggerPriceType": "",
"timeInForce": "GTC",
"reduceOnly": false,
"postOnly": false,
"closePosition": false,
"createdTime": 1755846235000,
"updatedTime": 1755846235000,
"filledQuantity": "2.0"
}
]
}Migration Note: Order Type Structure
Important: The API is transitioning from a nested order type structure to a flattened one for better EIP-712 compatibility:
| Old Structure | New Structure |
|---|---|
"type": "limit", "timeInForce": "gtc" | "orderType": "limitGtc" |
"type": "limit", "timeInForce": "ioc" | "orderType": "limitIoc" |
"type": "limit", "timeInForce": "alo" | "orderType": "limitAlo" |
"type": "market" | "orderType": "market" |
"type": "trigger", "triggerType": "stopLoss" | "orderType": "triggerSl" |
"type": "trigger", "triggerType": "takeProfit" | "orderType": "triggerTp" |
During the migration period, the API may return orders in either format. Clients should be prepared to handle both structures.
Error Response
{
"id": "getorders-1",
"status": 400,
"result": null,
"error": {
"code": 400,
"message": "Failed to get orders"
}
}Implementation Example
class OrderQuery {
constructor(ws, signer) {
this.ws = ws;
this.signer = signer;
this.requestId = 0;
}
async getOrderHistory(options = {}) {
const {
status = null,
symbol = null,
fromTime = null,
toTime = null,
limit = 50,
offset = 0,
sortBy = "createdTime",
sortOrder = "desc",
subAccountId
} = options;
// Generate nonce
const nonce = Date.now();
// Create query signature
const signature = await this.signGetOrders(options, nonce);
// Build action object
const action = {
type: "getOrderHistory",
limit,
offset,
sortBy,
sortOrder
};
if (status) action.status = status;
if (symbol) action.symbol = symbol;
if (fromTime) action.fromTime = fromTime;
if (toTime) action.toTime = toTime;
// Build request
const request = {
id: `getorders-${Date.now()}`,
method: "post",
params: {
action: "getOrderHistory",
status,
symbol,
fromTime,
toTime,
limit,
offset,
sortBy,
sortOrder,
subAccountId: subAccountId.toString(),
nonce,
signature
}
};
// Send and wait for response
return this.sendRequest(request);
}
async signGetOrders(options, nonce) {
const environment = process.env.ENVIRONMENT || 'testnet'; // 'mainnet' or 'testnet'
const domain = {
name: "Synthetix",
version: "1",
chainId: 1,
verifyingContract: "0x0000000000000000000000000000000000000000"
};
const types = {
GetOrders: [
{ name: "action", type: "GetOrdersAction" },
{ name: "subAccountId", type: "string" },
{ name: "nonce", type: "uint256" }
],
GetOrdersAction: [
{ name: "action", type: "string" },
{ name: "status", type: "string" },
{ name: "symbol", type: "string" },
{ name: "fromTime", type: "uint256" },
{ name: "toTime", type: "uint256" },
{ name: "limit", type: "uint256" },
{ name: "offset", type: "uint256" },
{ name: "sortBy", type: "string" },
{ name: "sortOrder", type: "string" }
]
};
const message = {
action: {
type: "getOrderHistory",
status: JSON.stringify(options.status || []),
symbol: options.symbol || "",
fromTime: options.fromTime || 0,
toTime: options.toTime || 0,
limit: options.limit || 50,
offset: options.offset || 0,
sortBy: options.sortBy || "createdTime",
sortOrder: options.sortOrder || "desc"
},
subAccountId: options.subAccountId,
nonce
};
const signature = await this.signer._signTypedData(domain, types, message);
return ethers.utils.splitSignature(signature);
}
sendRequest(request) {
return new Promise((resolve, reject) => {
this.pendingRequests.set(request.requestId, { resolve, reject });
this.ws.send(JSON.stringify(request));
setTimeout(() => {
if (this.pendingRequests.has(request.requestId)) {
this.pendingRequests.delete(request.requestId);
reject(new Error('Request timeout'));
}
}, 10000); // 10 second timeout
});
}
}
// Usage Examples
// Get All Open Orders (Legacy getOpenOrders equivalent)
async function getAllOpenOrders(query, subAccountId) {
try {
const result = await query.getOrderHistory({
subAccountId,
status: ["open"]
});
console.log(`Found ${result.orders.length} open orders`);
return result.orders;
} catch (error) {
console.error('Failed to get open orders:', error);
throw error;
}
}
// Get Order History with Time Range
async function getOrderHistory(query, subAccountId, fromTime, toTime) {
try {
const result = await query.getOrderHistory({
subAccountId,
fromTime,
toTime,
limit: 100
});
console.log(`Found ${result.orders.length} historical orders`);
return result.orders;
} catch (error) {
console.error('Failed to get order history:', error);
throw error;
}
}
// Filter by Symbol and Status
async function getOrderHistoryBySymbolAndStatus(query, subAccountId, symbol, status) {
try {
const result = await query.getOrderHistory({
subAccountId,
symbol,
status,
limit: 50
});
console.log(`Found ${result.orders.length} ${status} orders for ${symbol}`);
return result.orders;
} catch (error) {
console.error(`Failed to get orders for ${symbol}:`, error);
throw error;
}
}
// Recent Orders with Custom Sorting
async function getRecentOrders(query, subAccountId, hoursBack = 24) {
const fromTime = Date.now() - (hoursBack * 60 * 60 * 1000);
try {
const result = await query.getOrderHistory({
subAccountId,
fromTime,
sortBy: "updatedTime",
sortOrder: "desc",
limit: 25
});
console.log(`Found ${result.orders.length} recent orders`);
return result.orders;
} catch (error) {
console.error('Failed to get recent orders:', error);
throw error;
}
}
// Paginated Results
async function getPaginatedOrders(query, subAccountId, pageSize = 100) {
const allOrders = [];
let offset = 0;
let hasMore = true;
while (hasMore) {
try {
const result = await query.getOrderHistory({
subAccountId,
limit: pageSize,
offset,
sortBy: "createdTime",
sortOrder: "asc"
});
allOrders.push(...result.orders);
hasMore = result.pagination.hasMore;
offset += pageSize;
console.log(`Fetched ${result.orders.length} orders (total: ${allOrders.length})`);
} catch (error) {
console.error(`Failed to fetch page at offset ${offset}:`, error);
break;
}
}
return allOrders;
}Integration with Subscriptions
Combine request-response queries with subscriptions for complete order management:
class OrderManager {
constructor(ws, query, subAccountId) {
this.ws = ws;
this.query = query;
this.subAccountId = subAccountId;
this.orders = new Map();
}
async initialize() {
// 1. Get initial state (open orders only)
const result = await this.query.getOrderHistory({
subAccountId: this.subAccountId,
status: ["open"]
});
result.orders.forEach(order => {
this.orders.set(order.orderId, order);
});
// 2. Subscribe to real-time updates
this.ws.send(JSON.stringify({
id: "subscribe-orders",
method: "subscribe",
params: {
type: "orders",
subAccountId: this.subAccountId
}
}));
}
handleOrderUpdate(update) {
// Handle subscription events
const { eventType, orderId } = update.data;
if (eventType === 'cancelled' || eventType === 'filled') {
this.orders.delete(orderId);
} else {
this.orders.set(orderId, update.data);
}
}
async reconcile() {
// Periodic verification
const current = await this.query.getOrderHistory({
subAccountId: this.subAccountId,
status: ["open"]
});
// Sync any discrepancies
this.orders.clear();
current.orders.forEach(order => {
this.orders.set(order.orderId, order);
});
}
}Code Examples
Get All Open Orders
{
"id": "getorders-open",
"method": "post",
"params": {
"action": "getOrderHistory",
"status": ["open"],
"subAccountId": "1867542890123456789",
"nonce": 1704067200000,
"signature": {
"v": 28,
"r": "0x19480589384695193600abcdef19480589384695193600abcdef19480589384695193600abcdef19480589384695193600abcdef",
"s": "0xabcdef19480589384695193600abcdef19480589384695193600abcdef19480589384695193600abcdef19480589384695193600"
}
}
}Get Order History
{
"id": "getorders-history",
"method": "post",
"params": {
"action": "getOrderHistory",
"fromTime": 1704067200000,
"toTime": 1704153600000,
"limit": 100,
"subAccountId": "1867542890123456789",
"nonce": 1704067200000,
"signature": {
"v": 28,
"r": "0x19480589384695193600abcdef19480589384695193600abcdef19480589384695193600abcdef19480589384695193600abcdef",
"s": "0xabcdef19480589384695193600abcdef19480589384695193600abcdef19480589384695193600abcdef19480589384695193600"
}
}
}Use Cases
Trading Interface
- Active Orders Management: Use
"status": ["open"]to display orders that can be modified or cancelled - Order History: Use time ranges to show historical trading activity
- Symbol-Specific Views: Filter by symbol for market-specific order management
Analytics and Reporting
- Performance Analysis: Filter by time range and status to calculate trading metrics
- Audit Trail: Retrieve complete order history for compliance and reporting
- Risk Management: Monitor open orders across all symbols
WebSocket Integration
- Legacy Compatibility: Replace
getOpenOrdersrequests with appropriate filters - Efficient Pagination: Use offset-based pagination for large datasets
- Real-time Updates: Combine with WebSocket subscriptions for live order status updates
Advanced Filtering
Status Combinations
// Get all non-open orders
{ status: ["filled", "cancelled", "rejected", "partiallyFilled"] }
// Get only successful orders
{ status: ["filled", "partiallyFilled"] }
// Get failed orders
{ status: ["cancelled", "rejected"] }
// Get orders in transition states
{ status: ["cancelling", "modifying"] }Time-based Queries
// Orders from last 24 hours
{ fromTime: Date.now() - 86400000 }
// Orders in specific date range
{ fromTime: 1704067200000, toTime: 1704153600000 }
// Orders since specific time (no end)
{ fromTime: 1704067200000 }Order Object Structure
| Field | Type | Description |
|---|---|---|
orderId | string | Unique order identifier (64-bit unsigned integer as string) |
clientOrderId | string | Client-provided order identifier (128-bit hex string with 0x prefix) |
symbol | string | Trading pair symbol (e.g., "BTC-USDT") |
side | string | Order side: "buy" or "sell" |
type | string | Order type: "LIMIT", "MARKET", "STOP_LOSS", "TAKE_PROFIT" |
quantity | string | Original order quantity |
price | string | Order price (empty for market orders) |
triggerPrice | string | Trigger price for conditional orders |
triggerPriceType | string | Trigger price type: "mark", "last", or "index" |
timeInForce | string | Time in force: "GTC", "IOC", or "FOK" |
reduceOnly | boolean | Whether order only reduces existing position |
postOnly | boolean | Whether order must be maker (no immediate match) |
createdTime | integer | Order creation timestamp (Unix seconds) |
updatedTime | integer | Last update timestamp (Unix seconds) |
filledQuantity | string | Quantity that has been filled |
takeProfitOrderId | string | ID of linked take-profit order (if exists) |
stopLossOrderId | string | ID of linked stop-loss order (if exists) |
closePosition | boolean | Whether order closes entire position |
Multiple Fills Handling
When an order has multiple partial fills at different prices:
filledPrice: Volume-weighted average price (VWAP) of all fillsfilledQuantity: Total quantity filled across all partial fills- Example: Order for 10 units fills 3 @ $100 and 5 @ $102:
filledPrice: "101.25" (VWAP: (3×100 + 5×102) ÷ 8 = $101.25)filledQuantity: "8"
Implementation Notes
- Use Symbol Filters: Request only needed symbols to reduce payload
- Implement Pagination: Handle large order sets efficiently
- Cache Strategically: Cache order data but validate freshness
- Monitor Changes: Use real-time updates for order state changes
- Handle Timeouts: Implement reasonable timeout values
- Error Recovery: Retry failed requests with backoff
Validation Rules
noncemust be a timestamp in milliseconds and must be increasing- Must be signed by account owner or authorized delegate
- Subaccount ID must be valid and accessible
- Time range must be valid (fromTime ≤ toTime)
- Limit must be between 1 and 1000 (default: 50)
- Offset must be non-negative
- Status values must be valid:
["open", "filled", "partiallyFilled", "cancelled", "rejected", "started", "cancelling", "modifying", "unknown"] - Symbol must be a valid trading pair (if specified)
- Sort fields must be valid:
["createdTime", "updatedTime", "filledQuantity"] - Sort order must be either
"asc"or"desc"
Signing
All trading methods are signed using EIP-712. Each successful trading request will contain:
- A piece of structured data that includes the sender address
- A signature of the hash of that structured data, signed by the sender
For detailed information on EIP-712 signing, see EIP-712 Signing.
Nonce Management
The nonce system prevents replay attacks and ensures order uniqueness:
- Use current timestamp in milliseconds as nonce
- Each nonce must be greater than the previous one
- Recommended: Use
Date.now()or equivalent - If nonce conflicts occur, increment by 1 and retry
| Error | Description |
|---|---|
| Invalid signature | EIP-712 signature validation failed |
| Invalid market symbol | Market symbol not recognized |
| Nonce already used | Nonce must be greater than previous value |
| Rate limit exceeded | Too many requests in time window |
| Request expired | expiresAfter timestamp has passed |
Next Steps
- SubAccount Updates Subscription - Real-time order change notifications (consolidated subscription)
- Get Positions - Position querying via WebSocket
- Cancel Orders - Order cancellation via WebSocket
- REST Alternative - REST API comparison