Skip to content

Read Order State

POST https://trade-uk.sandbox.zodiamarkets.com/api/3/zm/rest/orders/state

Look up the current state of an order you placed over the WebSocket, by the clientRequestId you sent on it.

This is the recovery path that does not require a WebSocket connection. Use it when an order’s outcome never reached you — the connection dropped, your process restarted, or you are reconciling after the fact.


Every /api/3 request carries its anti-replay element — tonce or nonce — in the signed body, so this read is a POST with a body even though it changes nothing. A request with no body, or with neither element, is rejected with 401.

Sign it exactly as any other /api/3 endpoint: see Generate Signature →.


A JSON array, not an object. clientRequestId is unique only within your own account and may be reused, so a lookup can match more than one order.

An order that is not yours returns 200 with an empty array — not 403, not 404. An empty array means “no order of yours matches”, and nothing more.

orderStatus Meaning
PENDING No outcome recorded yet. Returned for orders on either delivery model — it does not imply a subscription.
SUCCESS The order filled.
FAILED The order did not fill. code carries the reason.
INDETERMINATE The outcome is genuinely unknown — the order may have filled. Do not re-place it; confirm with your desk.

timestamp is the moment this response was produced, not the time of any order event. To reconcile against settled trades, use Get Trades →, where your clientRequestId appears as clientRef.

Order records are held for 24 hours. Beyond that, use Get Trades for anything that executed.


Python
import hashlib
import hmac
import json
import time
import requests
api_key = "<your_api_key>"
api_secret = "<your_api_secret>"
base_url = "https://trade-uk.sandbox.zodiamarkets.com"
path = "api/3/zm/rest/orders/state"
client_request_id = "TESTCLIENT1"
# Every signed request must carry a nonce or tonce
body = {"tonce": int(time.time() * 1000000)}
body_json = json.dumps(body)
# The signature covers the path and the body - never the query string
message = f"{path}\0{body_json}"
signature = hmac.new(api_secret.encode(), message.encode(), hashlib.sha512).hexdigest()
response = requests.post(
f"{base_url}/{path}",
params={"clientRequestId": client_request_id},
headers={
"Rest-Key": api_key,
"Rest-Sign": signature,
"Content-Type": "application/json",
},
data=body_json,
)
orders = response.json()
if not orders:
print(f"No order found for {client_request_id}")
for order in orders:
print(f"{order['transactionId']}: {order['orderStatus']}")
if order["orderStatus"] == "INDETERMINATE":
# The order may have executed. Do not re-place it.
escalate_to_desk(order)

Domain: Trading

POST https://trade-uk.sandbox.zodiamarkets.com/api/3/zm/rest/orders/state
Header Required Description
Rest-Key yes API key for authentication
Rest-Sign yes Calculated API Signature
Parameter In Type Required Description
clientRequestId query string yes The clientRequestId you sent on the order. Not unique — it may match more than one order, so the response is always an array.
Field Type Required Description
tonce integer (int64) yes The current Unix time in microseconds.
nonce integer (int64) Alternative to tonce. Every request must carry either nonce or tonce, and the value must parse as a whole number; a request with neither is rejected with 401.

200 — The matching orders, as a JSON array. An empty array when there are none.

Section titled “200 — The matching orders, as a JSON array. An empty array when there are none.”
Field Type Required Description
[].messageType string yes Always orderState
[].timestamp integer (int64) yes When this response was produced, in milliseconds. Not the time of any order event — it is set as the record is serialised for your read.
[].transactionId string yes Zodia Markets transaction reference for the order
[].clientRequestId string yes Your identifier from the order request. Empty until the order has an outcome recorded, including on a lookup by clientRequestId.
[].orderStatus string (enum) yes Outcome of the order. PENDING means no outcome has been recorded yet, and is returned for orders on either delivery model. INDETERMINATE means the outcome is genuinely unknown and the order may have executed — do not re-place it. — One of: SUCCESS, FAILED, INDETERMINATE, PENDING
[].subscriptionStatus string (enum) yes NOT_SUBSCRIBED for an order sent without autoSubscribe. SUBSCRIBED until you send orderUnsubscribe, then UNSUBSCRIBED. — One of: NOT_SUBSCRIBED, SUBSCRIBED, UNSUBSCRIBED
[].sessionId string The session the order is bound to. null for an order sent without autoSubscribe — the field is present on the wire, carrying null.
[].message string yes Outcome message, empty when there is none
[].code string yes Outcome code, empty when there is none. See the Response / Error Code Reference.

400 — clientRequestId or sessionId breaches the identifier rules. A request missing a required header or query parameter is also rejected with 400, but by the framework and with a different body.

Section titled “400 — clientRequestId or sessionId breaches the identifier rules. A request missing a required header or query parameter is also rejected with 400, but by the framework and with a different body.”
Field Type Required Description
resultCode string
timestamp string Unix time in milliseconds, as a string

401 — Authentication failed. A bad signature, a missing or stale tonce/nonce, and a replayed one are not distinguished.

Section titled “401 — Authentication failed. A bad signature, a missing or stale tonce/nonce, and a replayed one are not distinguished.”
Field Type Required Description
resultCode string
timestamp string Unix time in milliseconds, as a string