Subscribe request for two-way Crypto and FX pricing
Price Streams
Subscribe to real-time two-way prices (bid and offer) for trading. Price streams continuously update with live market prices for your specified quantity and currency pair.
What You'll Receive
- Two-way prices - Both bid (buy from Zodia) and offer (sell to Zodia) prices
- Tradeable quotes - Each price includes a unique quote ID for order execution
- Continuous updates - New prices automatically replace previous ones
- Quantity-specific - Prices tailored to your requested trade size
Important Notes
- Partial fills are available - order request can be less than or equal to quoted amount
- Full order book depth is not provided - only your requested size
- If connection is lost, download transactions via REST API for reconciliation, or subscribe to transactions via the Webhook.
Subscribe to Price Stream
Request a continuous stream of prices for a specific currency pair and quantity.
{
"messageType": "subscribe",
"instrument": "USDC.AED",
"quantity": "100000",
"currency": "AED",
"accountGrpUuid": "a6898bdd-856b-4259-b6e2-6ef66f2282e1",
"tenor": "T"
}Request Fields
| Field | Required | Type | Description |
|---|---|---|---|
messageType | Yes | String | Must be subscribe |
instrument | Yes | String | Currency pair (e.g., USDC.AED, BTC.USD) |
quantity | Yes | String | Amount to quote in the specified currency |
currency | Yes | String | Currency for quantity - must be either base or quote currency of the pair |
accountGrpUuid | Yes | String | Account group UUID (from Account Groups message). |
tenor | Yes | String | Settlement date: T (closest available day) or T1 (closest available day + 1 day) |
tag | No | String | Reserved for future use |
Understanding Currency Pairs
Currency pairs are formatted as BASE.QUOTE:
- USDC.AED - Base currency: USDC, Quote currency: AED
- When you specify
currency: "AED"withquantity: "100000", you're requesting a price for 100,000 AED worth of USDC - When you specify
currency: "USDC"withquantity: "1000", you're requesting a price for 1,000 USDC
Settlement Tenors
| Tenor | Description | Use Case |
|---|---|---|
T | Today settlement | Price for closest available value date to today |
T1 | Tomorrow settlement | Price for closest available value date to tomorrow |
The actual settlement date is returned in the settleDate field of price updates. Contact your Relationship Manager for custom value date pricing.
Subscribe Response
Confirms your subscription was successful and provides a subscription ID.
{
"chanId": "",
"timestamp": 1718110925811,
"messageType": "subscribe",
"success": true,
"message": "Subscribed",
"subscriptionId": "13f07bc9-055f-4054-bb78-73fe9f325ee6",
"tag": "test-tag",
"instrument": "USDC.AED",
"quantity": "10000.000000",
"code": "",
"tenor": "T",
"settleDate": ""
}Response Fields
| Field | Required | Type | Description |
|---|---|---|---|
chanId | No | String | Reserved for future use (currently empty) |
timestamp | Yes | Long | Unix timestamp in milliseconds |
messageType | Yes | String | Always subscribe |
success | Yes | Boolean | true if subscription succeeded, false if failed |
message | Yes | String | Confirmation message or error description |
subscriptionId | Yes | String | Unique ID for this subscription - save this for unsubscribing |
instrument | Yes | String | Confirmed currency pair |
quantity | Yes | String | Confirmed quantity in base currency terms |
quoteAmount | Yes | String | Confirmed quantity in quote currency terms |
code | No | String | Error code (empty on success) |
tenor | No | String | Confirmed tenor |
settleDate | No | String | Reserved for future use (currently empty) |
tag | No | String | Your tag if provided |
Store the subscriptionId - you'll need it to unsubscribe later.
Receiving Price Updates
After successful subscription, you'll receive continuous price updates.
{
"chanId": "",
"timestamp": 1729156597244,
"messageType": "pricestream",
"instrument": "USDC.AED",
"quoteId": "cmEK+SwelROidy4Sn63WoWU2UJSAFOPy9Xi5UDpnCJPG5oH8ABAFv6fB...",
"tag": "a5cf32f7-ae66-4edc-8538-f015020d1952",
"offer": {
"price": "3.673050",
"quantity": "2722.533045",
"quoteAmount": "2722.533045"
},
"bid": {
"price": "3.672950",
"quantity": "2722.607169",
"quoteAmount": "2722.533045"
},
"tenor": "T",
"settleDate": "20251021"
}Price Stream Fields
| Field | Required | Type | Description |
|---|---|---|---|
chanId | No | String | Client identifier (can be ignored) |
timestamp | Yes | Long | Unix timestamp in milliseconds |
messageType | Yes | String | Always pricestream |
instrument | Yes | String | Currency pair |
quoteId | Yes | String | Unique quote identifier - use this to execute orders |
offer | Yes | Object | Sell side (you sell base currency to Zodia) |
offer.price | Yes | String | Offer price |
offer.quantity | Yes | String | Offer quantity in base currency |
offer.quoteAmount | Yes | String | Offer quantity in quote currency |
bid | Yes | Object | Buy side (you buy base currency from Zodia) |
bid.price | Yes | String | Bid price |
bid.quantity | Yes | String | Bid quantity in base currency |
bid.quoteAmount | Yes | String | Bid quantity in quote currency |
tenor | Yes | String | Settlement tenor for this quote |
settleDate | No | String | Actual settlement date in YYYYMMDD format |
tag | No | String | Your tag if provided |
Understanding Bid and Offer
For currency pair USDC.AED:
-
Bid (3.672950) - Price at which Zodia will buy USDC from you (you receive AED)
- You're selling USDC, buying AED
-
Offer (3.673050) - Price at which Zodia will sell USDC to you (you pay AED)
- You're buying USDC, selling AED
Quote Expiry
Quote expiry is not published on the stream. The default quote expiry time is 3 seconds.
Handling Price Updates - Example
import json
# Store active subscriptions
active_subscriptions = {}
def handle_websocket_message(message):
"""Process incoming WebSocket messages"""
msg = json.loads(message)
if msg['messageType'] == 'subscribe':
# Store subscription ID
if msg['success']:
subscription_id = msg['subscriptionId']
active_subscriptions[msg['instrument']] = subscription_id
print(f"✅ Subscribed to {msg['instrument']}: {subscription_id}")
else:
print(f"❌ Subscription failed: {msg['message']}")
elif msg['messageType'] == 'pricestream':
# Process price update
instrument = msg['instrument']
bid_price = float(msg['bid']['price'])
offer_price = float(msg['offer']['price'])
quote_id = msg['quoteId']
print(f"Price Update - {instrument}")
print(f" Bid: {bid_price} (qty: {msg['bid']['quantity']})")
print(f" Offer: {offer_price} (qty: {msg['offer']['quantity']})")
print(f" Spread: {(offer_price - bid_price):.6f}")
print(f" Quote ID: {quote_id}")
print(f" Settle: {msg.get('settleDate', 'N/A')}")
# Check if price meets your trading criteria
if should_trade(bid_price, offer_price):
execute_order(quote_id, 'BUY', msg['offer']['quantity'])// Store active subscriptions
const activeSubscriptions = {};
function handleWebSocketMessage(message) {
const msg = JSON.parse(message);
if (msg.messageType === 'subscribe') {
// Store subscription ID
if (msg.success) {
const subscriptionId = msg.subscriptionId;
activeSubscriptions[msg.instrument] = subscriptionId;
console.log(`✅ Subscribed to ${msg.instrument}: ${subscriptionId}`);
} else {
console.error(`❌ Subscription failed: ${msg.message}`);
}
}
else if (msg.messageType === 'pricestream') {
// Process price update
const instrument = msg.instrument;
const bidPrice = parseFloat(msg.bid.price);
const offerPrice = parseFloat(msg.offer.price);
const quoteId = msg.quoteId;
console.log(`Price Update - ${instrument}`);
console.log(` Bid: ${bidPrice} (qty: ${msg.bid.quantity})`);
console.log(` Offer: ${offerPrice} (qty: ${msg.offer.quantity})`);
console.log(` Spread: ${(offerPrice - bidPrice).toFixed(6)}`);
console.log(` Quote ID: ${quoteId}`);
console.log(` Settle: ${msg.settleDate || 'N/A'}`);
// Check if price meets your trading criteria
if (shouldTrade(bidPrice, offerPrice)) {
executeOrder(quoteId, 'BUY', msg.offer.quantity);
}
}
}Unsubscribe from Price Stream
Stop receiving price updates by unsubscribing using the subscription ID.
{
"messageType": "unsubscribe",
"subscriptionId": "13f07bc9-055f-4054-bb78-73fe9f325ee6"
}Request Fields
| Field | Required | Type | Description |
|---|---|---|---|
messageType | Yes | String | Must be unsubscribe |
subscriptionId | Yes | String | Subscription ID received in subscribe response |
Unsubscribe Response
{
"timestamp": 1718111157195,
"messageType": "unsubscribe",
"success": true,
"subscriptionId": "2ffc8d0c-21f5-4364-b2a6-4007218e57ee",
"message": "Cancelled subscription 2ffc8d0c-21f5-4364-b2a6-4007218e57ee...",
"instrument": "USDC.AED",
"quantity": "100000.000000",
"tenor": "T"
}Response Fields
| Field | Required | Type | Description |
|---|---|---|---|
timestamp | Yes | Long | Unix timestamp in milliseconds |
messageType | Yes | String | Always unsubscribe |
success | Yes | Boolean | true if unsubscribe succeeded |
subscriptionId | Yes | String | The subscription ID that was unsubscribed |
message | Yes | String | Confirmation message |
instrument | Yes | String | Currency pair that was unsubscribed |
quantity | Yes | Double | Quantity that was unsubscribed |
tenor | Yes | String | Tenor that was unsubscribed |
Stream Stop Message (TOD Tenor Only)
When using the TOD tenor, if the currency pair reaches its daily cut-off time, you'll receive a stream stop message.
NoteThis only applies to
TODtenor.TandT1tenors are not affected.
{
"instrument": "USDC.GBP",
"tenor": "TOD",
"chanId": "bd00d714-f1bd-49c2-970d-91791b4243ee",
"tag": "c8c423ff-6c6c-4cc0-a63d-a2a1251d0bbe",
"timestamp": 1747916826063,
"messageType": "streamStop"
}Stream Stop Fields
| Field | Required | Type | Description |
|---|---|---|---|
chanId | No | String | Client identifier (can be ignored) |
timestamp | Yes | Long | Unix timestamp in milliseconds |
messageType | Yes | String | Always streamStop |
instrument | Yes | String | Currency pair that stopped streaming |
tenor | Yes | String | The tenor that reached cut-off |
tag | No | String | Your tag if provided |
When you receive this message, the price stream for that tenor is no longer available. You can subscribe to a different tenor if needed.