Get Trades List

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.

ParameterTypeRequiredDefaultDescription
tradeRefStringNo-Filter by Zodia Markets trade reference
clientRefStringNo-Filter by your custom client reference
tradeStateStringNo-Filter by trade state: PENDING_SETTLEMENT, SETTLED, CANCELLED
tradeSideStringNo-Filter by trade side: BUY or SELL
tradeClassStringNo-Filter by trade class: OTC or RFS
fromStringNo-Start date/time filter (ISO 8601 format) for createdAt
toStringNo-End date/time filter (ISO 8601 format) for createdAt
accountGrpUuidStringNo-Filter by specific account group UUID
maxIntegerNo50Maximum results to return (1-200)
offsetIntegerNo0Offset 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

FieldTypeDescription
dataArrayArray of trade objects
totalIntegerTotal number of trades matching the filter criteria

Trade Object

FieldTypeDescription
uuidStringUnique trade identifier
createdAtStringTrade creation timestamp (ISO 8601)
updatedAtStringLast update timestamp (ISO 8601)
tradeRefStringZodia Markets trade reference
clientRefStringYour custom client reference (if provided during RFS trade execution)
tradeSideStringBUY or SELL from your perspective
settlementAmountObjectAmount to be settled (what you pay/receive)
tradedAmountObjectAmount traded (what you buy/sell)
userStringUser Account shortcode
accountGroupObjectAccount group information
tradeClassStringTrade classification: OTC or RFS
stateStringCurrent trade state
settlementDateStringExpected settlement date (YYYY-MM-DD)
quoteIdStringQuote ID from price stream (for RFS trades, not present for OTC)
beneficiaryObjectThird-party beneficiary details (not present for standard settlement)
senderObjectThird-party sender details (not present for standard settlement)
networkIdStringSettlement network identifier (not present for standard settlement)
executedPriceStringExecution price with currency pair
executorLoginStringThe user who entered the trade (For OTC this would be the ZM Trader)
paymentReasonStringPayment Reason for third party receipt/delivery trades

Settlement/Traded Amount Object

FieldTypeDescription
amountNumberAmount value
currencyStringCurrency code (ISO 4217 for fiat, asset symbol for crypto)

Account Group Object

FieldTypeDescription
uuidStringAccount group UUID
nameStringAccount group name

Beneficiary Object

FieldTypeDescription
uuidStringBeneficiary UUID
nameStringBeneficiary name

Sender Object

FieldTypeDescription
uuidStringSender UUID
nameStringSender name

Trade States

Trades progress through various states from execution to settlement:

StateDescription
PENDING_SETTLEMENTTrade executed, awaiting settlement
SETTLEDTrade fully settled
CANCELLEDTrade cancelled before settlement
FAILEDSettlement failed

Trade Classes

ClassDescription
OTCOver-the-counter trades executed via Zodia Markets trading desk
RFSRequest for Stream trades executed via e-Trader or WebSocket API

Key Difference:

  • RFS trades include a quoteId field (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_trades

Error 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 data

Filter 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 time

Use 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