POST https://trade-uk.sandbox.zodiamarkets.com/api/3/trades
List Trades
Retrieve a list of executed trades with optional filtering. Returns a single record for each trade, whilst the transaction endpoint returns a separate record for each leg of the trade.
Request
Body Parameters
All parameters are optional. If no parameters are provided, returns all trades.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
tradeRef | String | No | - | Filter by Zodia Markets trade reference |
clientRef | String | No | - | Filter by your custom client reference |
tradeState | String | No | - | Filter by trade state: PENDING_SETTLEMENT, SETTLED, CANCELLED |
tradeSide | String | No | - | Filter by trade side: BUY or SELL |
tradeClass | String | No | - | Filter by trade class: OTC or RFS |
from | String | No | - | Start date/time filter (ISO 8601 format) for createdAt |
to | String | No | - | End date/time filter (ISO 8601 format) for createdAt |
accountGrpUuid | String | No | - | Filter by specific account group UUID |
max | Integer | No | 50 | Maximum results to return (1-200) |
offset | Integer | No | 0 | Offset for pagination |
Example Requests
Get all trades (no filter):
{}Filter by trade reference:
{
"tradeRef": "ZODIA-TEST1-20APR2026-121507-USDC-EUR-0"
}Filter by date range:
{
"from": "2026-04-01T00:00:00Z",
"to": "2026-04-30T23:59:59Z",
"max": 200,
"offset": 0
}Filter by state and side:
{
"tradeState": "PENDING_SETTLEMENT",
"tradeSide": "BUY",
"max": 50
}Filter by account group:
{
"accountGrpUuid": "2073252c-81ed-41be-bf4d-d51b8f2246b8",
"from": "2026-04-20T00:00:00Z",
"to": "2026-04-20T23:59:59Z"
}Filter RFS trades:
{
"tradeClass": "RFS",
"tradeState": "SETTLED"
}Response
Success Response (200 OK)
{
"data": [
{
"uuid": "5488fcd4-3ca3-11f1-a3e2-17462098afd7",
"createdAt": "2026-04-20T10:25:35Z",
"updatedAt": "2026-04-20T10:25:43Z",
"tradeRef": "f3a964ad27074a9780b5010b3514d2e0",
"clientRef": "8309ec3c-83a3-490c-aab5-4748d7436ad8",
"tradeSide": "BUY",
"settlementAmount": {
"amount": 1001.00000000,
"currency": "EUR"
},
"tradedAmount": {
"amount": 1175.04882000,
"currency": "USDC"
},
"user": "TEST1",
"accountGroup": {
"uuid": "9073262c-81ed-41be-bf4d-d51b8f2246b8",
"name": "Default"
},
"tradeClass": "RFS",
"state": "PENDING_SETTLEMENT",
"settlementDate": "2026-04-20",
"quoteId": "RqAXfadhm5Q0RIqVJF8gNjEvPiIxYEBf0+pMlAlwOxdTbiYrFgU8UzV2TlApMzUzDBkhGApDSoJBavG+Ea92CTUoKQ8LPzs+B0J5fgtUeCsjLzob+yDsFlb1byHWtS5kqrqMu27bCHgcdnypCP4whuk7ew8u1wGaDNN+1AXXA1rEEPuEJjA6DFYKMSEsDQceFxsDYGIWCxh6ETtMUTV7DxQFIyBWRlE5KigeSRQTK3UoBlgDEDUrQkZjWycNAwE2MwFNCWhISURJbjxYEVd5Lh0FVUh3VmEDJS9VEAk7N1JSBlYNDgE5OR0RcxYPXyQ9HQV2Ex0GJRIlLzgMCgoJFzs3PS4=",
"beneficiary": {
"uuid": "f4761297-1d77-4d08-a6ae-742bafb0d773",
"name": "MY COMPANY NAME"
},
"networkId": "ZM_TRANSFER",
"executedPrice": "[USDC/EUR] 0.85187950",
"executorLogin": "SUB-USDTTEST",
"paymentReason": "VASA",
}
],
"total": 1
}Response Fields
Root Object
| Field | Type | Description |
|---|---|---|
data | Array | Array of trade objects |
total | Integer | Total number of trades matching the filter criteria |
Trade Object
| Field | Type | Description |
|---|---|---|
uuid | String | Unique trade identifier |
createdAt | String | Trade creation timestamp (ISO 8601) |
updatedAt | String | Last update timestamp (ISO 8601) |
tradeRef | String | Zodia Markets trade reference |
clientRef | String | Your custom client reference (if provided during RFS trade execution) |
tradeSide | String | BUY or SELL from your perspective |
settlementAmount | Object | Amount to be settled (what you pay/receive) |
tradedAmount | Object | Amount traded (what you buy/sell) |
user | String | User Account shortcode |
accountGroup | Object | Account group information |
tradeClass | String | Trade classification: OTC or RFS |
state | String | Current trade state |
settlementDate | String | Expected settlement date (YYYY-MM-DD) |
quoteId | String | Quote ID from price stream (for RFS trades, not present for OTC) |
beneficiary | Object | Third-party beneficiary details (not present for standard settlement) |
sender | Object | Third-party sender details (not present for standard settlement) |
networkId | String | Settlement network identifier (not present for standard settlement) |
executedPrice | String | Execution price with currency pair |
executorLogin | String | The user who entered the trade (For OTC this would be the ZM Trader) |
paymentReason | String | Payment Reason for third party receipt/delivery trades |
Settlement/Traded Amount Object
| Field | Type | Description |
|---|---|---|
amount | Number | Amount value |
currency | String | Currency code (ISO 4217 for fiat, asset symbol for crypto) |
Account Group Object
| Field | Type | Description |
|---|---|---|
uuid | String | Account group UUID |
name | String | Account group name |
Beneficiary Object
| Field | Type | Description |
|---|---|---|
uuid | String | Beneficiary UUID |
name | String | Beneficiary name |
Sender Object
| Field | Type | Description |
|---|---|---|
uuid | String | Sender UUID |
name | String | Sender name |
Trade States
Trades progress through various states from execution to settlement:
| State | Description |
|---|---|
PENDING_SETTLEMENT | Trade executed, awaiting settlement |
SETTLED | Trade fully settled |
CANCELLED | Trade cancelled before settlement |
FAILED | Settlement failed |
Trade Classes
| Class | Description |
|---|---|
OTC | Over-the-counter trades executed via Zodia Markets trading desk |
RFS | Request for Stream trades executed via e-Trader or WebSocket API |
Key Difference:
- RFS trades include a
quoteIdfield (from the WebSocket price stream) - OTC trades have no
quoteId
Pagination
Use max (1-200) and offset parameters to paginate through large result sets.
Example: Retrieve All Trades
def get_all_trades(filters=None):
"""Retrieve all trades matching filters"""
all_trades = []
offset = 0
max_per_page = 200
if filters is None:
filters = {}
while True:
body = {
**filters,
'max': max_per_page,
'offset': offset
}
response = make_api_request('POST', 'api/3/trades', body)
trades = response['data']
all_trades.extend(trades)
# Stop if we got fewer results than requested
if len(trades) < max_per_page:
break
offset += max_per_page
return all_tradesError Responses
400 Bad Request
{
"error": "INVALID_PARAMETER",
"message": "Invalid date format. Use ISO 8601 format (e.g., 2026-04-20T00:00:00Z)"
}401 Unauthorized
{
"error": "INVALID_SIGNATURE",
"message": "Request signature validation failed"
}404 Not Found
{
"error": "INVALID_ACCOUNT_GROUP",
"message": "Account group not found"
}Code Examples
Python
import json
import hmac
import hashlib
import time
import requests
from datetime import datetime, timedelta
# Configuration
api_key = "your_api_key"
api_secret = "your_api_secret"
base_url = "https://trade-uk.sandbox.zodiamarkets.com"
# Request body - Get trades from last 7 days
today = datetime.utcnow()
week_ago = today - timedelta(days=7)
body = {
"from": week_ago.strftime("%Y-%m-%dT%H:%M:%SZ"),
"to": today.strftime("%Y-%m-%dT%H:%M:%SZ"),
"tradeState": "PENDING_SETTLEMENT",
"max": 50,
"offset": 0
}
body_json = json.dumps(body)
# Generate signature
path = "api/3/trades"
tonce = str(int(time.time() * 1000000))
message = f"{path}\0{body_json}"
signature = hmac.new(
api_secret.encode(),
message.encode(),
hashlib.sha512
).hexdigest()
# Make request
headers = {
"Rest-Key": api_key,
"Rest-Sign": signature,
"Content-Type": "application/json"
}
response = requests.post(
f"{base_url}/{path}",
headers=headers,
data=body_json
)
# Process response
if response.status_code == 200:
data = response.json()
print(f"Found {data['total']} trades")
for trade in data['data']:
print(f"\nTrade: {trade['tradeRef']}")
print(f" Client Ref: {trade['clientRef']}")
print(f" Class: {trade['tradeClass']}")
print(f" Side: {trade['tradeSide']}")
print(f" State: {trade['tradeState']}")
print(f" Traded: {trade['tradedAmount']['amount']} {trade['tradedAmount']['currency']}")
print(f" Settlement: {trade['settlementAmount']['amount']} {trade['settlementAmount']['currency']}")
print(f" Price: {trade['executedPrice']}")
print(f" Settlement Date: {trade['settlementDate']}")
if trade.get('quoteId'):
print(f" Quote ID: {trade['quoteId'][:50]}...")
if trade['beneficiary']:
print(f" Third-party: {trade['beneficiary']['name']}")
else:
print(f"Error: {response.status_code}")
print(response.text)JavaScript (Node.js)
const crypto = require('crypto');
const axios = require('axios');
// Configuration
const apiKey = 'your_api_key';
const apiSecret = 'your_api_secret';
const baseUrl = 'https://trade-uk.sandbox.zodiamarkets.com';
// Request body - Get trades from last 7 days
const today = new Date();
const weekAgo = new Date(today.getTime() - 7 * 24 * 60 * 60 * 1000);
const body = {
from: weekAgo.toISOString(),
to: today.toISOString(),
tradeState: 'PENDING_SETTLEMENT',
max: 50,
offset: 0
};
const bodyJson = JSON.stringify(body);
// Generate signature
const path = 'api/3/trades';
const tonce = Date.now() * 1000;
const message = `${path}\0${bodyJson}`;
const signature = crypto
.createHmac('sha512', apiSecret)
.update(message)
.digest('hex');
// Make request
const headers = {
'Rest-Key': apiKey,
'Rest-Sign': signature,
'Content-Type': 'application/json'
};
axios.post(`${baseUrl}/${path}`, body, { headers })
.then(response => {
console.log(`Found ${response.data.total} trades`);
response.data.data.forEach(trade => {
console.log(`\nTrade: ${trade.tradeRef}`);
console.log(` Client Ref: ${trade.clientRef}`);
console.log(` Class: ${trade.tradeClass}`);
console.log(` Side: ${trade.tradeSide}`);
console.log(` State: ${trade.state}`);
console.log(` Traded: ${trade.tradedAmount.amount} ${trade.tradedAmount.currency}`);
console.log(` Settlement: ${trade.settlementAmount.amount} ${trade.settlementAmount.currency}`);
console.log(` Price: ${trade.executedPrice}`);
console.log(` Settlement Date: ${trade.settlementDate}`);
if (trade.quoteId) {
console.log(` Quote ID: ${trade.quoteId.substring(0, 50)}...`);
}
if (trade.beneficiary) {
console.log(` Third-party: ${trade.beneficiary.name}`);
}
});
})
.catch(error => {
console.error('Error:', error.response?.status);
console.error(error.response?.data);
});cURL
curl -X POST https://trade-uk.sandbox.zodiamarkets.com/api/3/trades \
-H "Rest-Key: your_api_key" \
-H "Rest-Sign: your_hmac_signature" \
-H "Content-Type: application/json" \
-d '{
"from": "2026-04-01T00:00:00Z",
"to": "2026-04-30T23:59:59Z",
"state": "PENDING_SETTLEMENT",
"max": 50,
"offset": 0
}'Common Use Cases
Get Recent Trades
from datetime import datetime, timedelta
def get_recent_trades(days=7):
"""Get trades from the last N days"""
today = datetime.utcnow()
start_date = today - timedelta(days=days)
body = {
'from': start_date.strftime('%Y-%m-%dT%H:%M:%SZ'),
'to': today.strftime('%Y-%m-%dT%H:%M:%SZ')
}
response = make_api_request('POST', 'api/3/trades', body)
return response['data']
trades = get_recent_trades(7)
print(f"Found {len(trades)} trades in the last 7 days")Get Pending Settlements
def get_pending_settlements():
"""Get all trades pending settlement"""
body = {
'tradeState': 'PENDING_SETTLEMENT'
}
response = make_api_request('POST', 'api/3/trades', body)
return response['data']
pending = get_pending_settlements()
for trade in pending:
print(f"{trade['tradeRef']}: {trade['settlementDate']}")Get Trades by Account Group
def get_trades_by_account_group(account_group_uuid):
"""Get trades for specific account group"""
body = {
'accountGrpUuid': account_group_uuid
}
response = make_api_request('POST', 'api/3/trades', body)
return response['data']
trades = get_trades_by_account_group('2073252c-81ed-41be-bf4d-d51b8f2246b8')Find Trade by Reference
def find_trade_by_ref(trade_ref):
"""Find specific trade by trade reference"""
body = {
'tradeRef': trade_ref
}
response = make_api_request('POST', 'api/3/trades', body)
if response['data']:
return response['data'][0]
return None
trade = find_trade_by_ref('f4a964ad27074a9780b5010b3514d2e0')
if trade:
print(f"Found trade: {trade['state']}")Find Trade by Client Reference
def find_trade_by_client_ref(client_ref):
"""Find trade by your custom client reference"""
body = {
'clientRef': client_ref
}
response = make_api_request('POST', 'api/3/trades', body)
if response['data']:
return response['data'][0]
return None
trade = find_trade_by_client_ref('8305ec3c-83a3-490c-aab5-4748d7436ad8')
if trade:
print(f"Found trade: {trade['tradeRef']}")Get RFS Trades with Quote IDs
def get_rfs_trades():
"""Get all RFS trades (includes quote IDs)"""
body = {
'tradeClass': 'RFS'
}
response = make_api_request('POST', 'api/3/trades', body)
return response['data']
rfs_trades = get_rfs_trades()
for trade in rfs_trades:
print(f"Trade: {trade['tradeRef']}")
if trade.get('quoteId'):
print(f" Quote ID: {trade['quoteId'][:50]}...")Get Third-Party Settlement Trades
def get_third_party_trades():
"""Get all trades with third-party settlement"""
response = make_api_request('POST', 'api/3/trades', {})
third_party = [
t for t in response['data']
if t['beneficiary'] is not None
]
return third_party
trades = get_third_party_trades()
for trade in trades:
print(f"{trade['tradeRef']}: {trade['beneficiary']['name']}")Calculate Total Volume by Currency
from collections import defaultdict
def calculate_volume_by_currency():
"""Calculate total traded volume by currency"""
response = make_api_request('POST', 'api/3/trades', {})
volume = defaultdict(float)
for trade in response['data']:
currency = trade['tradedAmount']['currency']
amount = trade['tradedAmount']['amount']
volume[currency] += amount
return dict(volume)
volumes = calculate_volume_by_currency()
for currency, total in volumes.items():
print(f"{currency}: {total:,.2f}")Get Trades for Specific Day
def get_trades_for_date(date_str):
"""Get all trades for a specific date (YYYY-MM-DD)"""
from_time = f"{date_str}T00:00:00Z"
to_time = f"{date_str}T23:59:59Z"
body = {
'from': from_time,
'to': to_time
}
response = make_api_request('POST', 'api/3/trades', body)
return response['data']
trades = get_trades_for_date('2026-04-20')
print(f"Trades on 2026-04-20: {len(trades)}")Reconcile Trades with Client References
def reconcile_trades(client_refs):
"""Check which client references have completed trades"""
completed = []
missing = []
for client_ref in client_refs:
body = {'clientRef': client_ref}
response = make_api_request('POST', 'api/3/trades', body)
if response['data']:
trade = response['data'][0]
completed.append({
'clientRef': client_ref,
'tradeRef': trade['tradeRef'],
'state': trade['state']
})
else:
missing.append(client_ref)
return completed, missing
# Usage
my_refs = [
'8305ec3c-83a3-490c-aab5-4748d7436ad8',
'other-client-ref-123'
]
completed, missing = reconcile_trades(my_refs)
print(f"Completed: {len(completed)}")
print(f"Missing: {len(missing)}")Date Range Filtering
ISO 8601 Format
Use ISO 8601 format for from and to parameters:
Format: YYYY-MM-DDTHH:MM:SSZ
Examples:
2026-04-20T00:00:00Z- Start of day (UTC)2026-04-20T23:59:59Z- End of day (UTC)2026-04-20T10:25:35Z- Specific time (UTC)
Common Date Ranges
Today's trades:
{
"from": "2026-04-20T00:00:00Z",
"to": "2026-04-20T23:59:59Z"
}Last 30 days:
{
"from": "2026-03-21T00:00:00Z",
"to": "2026-04-20T23:59:59Z"
}Specific month:
{
"from": "2026-04-01T00:00:00Z",
"to": "2026-04-30T23:59:59Z"
}Best Practices
Always Use Pagination for Large Datasets
# Good - Paginated request
def get_all_trades_paginated():
all_trades = []
offset = 0
while True:
response = make_api_request('POST', 'api/3/trades', {
'max': 200,
'offset': offset
})
trades = response['data']
all_trades.extend(trades)
if len(trades) < 200:
break
offset += 200
return all_trades
# Bad - Requesting without pagination
response = make_api_request('POST', 'api/3/trades', {})
# May timeout or return incomplete dataFilter at API Level, Not Client Side
# Good - Filter with API parameters
response = make_api_request('POST', 'api/3/trades', {
'state': 'PENDING_SETTLEMENT',
'tradeClass': 'RFS'
})
# Bad - Fetch all and filter locally
all_trades = make_api_request('POST', 'api/3/trades', {})
filtered = [t for t in all_trades['data'] if t['state'] == 'PENDING_SETTLEMENT']
# Wastes bandwidth and timeUse Specific Date Ranges
# Good - Specific date range
body = {
'from': '2026-04-01T00:00:00Z',
'to': '2026-04-30T23:59:59Z'
}
# Bad - Fetching all trades without date filter
body = {} # Returns all trades ever - slow!Use Client References for Tracking
# Good - Provide client reference when executing orders
order = {
'messageType': 'order',
'quoteId': quote_id,
'tradeSide': 'BUY',
'clientRequestId': 'ORDER-123', # This becomes clientRef
...
}
# Later, easily retrieve your trade
trade = find_trade_by_client_ref('ORDER-123')Related Documentation
- Order Execution - Execute trades via WebSocket (clientRequestId becomes clientRef)
- Beneficiaries - Manage third-party settlement beneficiaries
- Account Groups - Understanding account structure