/portfolio/events/orders: create, amend, decrease, cancel and their batch forms.
An order the API or the exchange rejects normally has this body. Switch on code; message and details are for people.
details is usually omitted when there is nothing to add. Errors inside a batch response always include it, as an empty string when there is nothing to add.
A request body the API cannot parse, such as malformed JSON or a field of the wrong type, is a 400 whose code, message and details sit at the top level of the body instead of under error:
error.code first and fall back to a top-level code. A body that parses but omits a required field is the usual shape. Depending on the field, the code is missing_parameters or invalid_order (on Create Order, a missing ticker gives the first and a missing side, count or price the second), so handle both and read details.
Most codes are lowercase with underscores. A few rejections come straight from the matching engine and keep its spelling, in upper case or as a sentence with underscores in place of spaces. They are listed below exactly as returned. Match codes case-sensitively. On a
400, 403, 404, 409, 410 or 429, treat an unknown code as a rejection. A 408, any 5xx or a dropped connection is different: see When the outcome is unknown.A success response does not mean the order is resting
201 from Create Order means the exchange accepted the order. Read fill_count and remaining_count to see what happened to it. These outcomes are successes, not errors:
Two outcomes that look similar are returned as errors (shown here for Create Order):
Error codes
The statuses below are what a single-order request returns. In a batch request the samecode appears on the affected entry’s error inside the 201 or 200 response, unless the failure is one of those listed under Batch requests as rejecting the whole batch.
400 Bad Request
A request with an
exchange_index below -1 or above the highest exchange shard also returns 400, with a code that states the valid range.
404 Not Found
409 Conflict
Other statuses
When the outcome is unknown
A408 or 5xx response, a timeout, or a connection that drops before the response arrives does not tell you whether the exchange processed the request. The same is true of an entry inside a successful batch response whose error.code is one of those failure codes. The order may have been placed, amended or canceled. Do not assume it was rejected, and do not resubmit blindly:
- Find out what happened: wait for the order’s update on the
user_ordersWebSocket channel, or read it back with Get Orders, allowing for the read delay. - Resubmit only once you know the first attempt did not take effect.
client_order_id on every new order helps: a resubmission is rejected with order_already_exists while the first order is still open. It does not protect you once that order has filled, canceled or expired.
Client order IDs
client_order_id is optional. When you send one:
- It can be up to 64 characters, using letters, digits and
_,-,:,+,=,/. A UUID fits. On Create Order, any other value is rejected with400 invalid_parametersand nodetailsfield. In Batch Create Orders, only that entry is rejected: the batch still returns201, and the entry carries anerrorwith codeinvalid_parametersand an emptydetails. - On an amend, a malformed
client_order_idorupdated_client_order_idis also a400, but thecodeis a parser message that starts withID_parse_error. Match it by prefix. - It must be unique per subaccount on the market’s exchange shard. The same ID can be used by two subaccounts, or on two shards, without conflict.
- A duplicate is rejected with code
order_already_exists: an HTTP409on Create Order, an entry error inside the201on Batch Create Orders. The response does not include the original order, so look it up with Get Orders. - An ID is reserved while its order is open. Once the order fills, cancels or expires the ID may still be rejected for a while, but that is not guaranteed: it can become usable again immediately. Do not rely on
order_already_existsto detect a retry of an order that may already have closed, and do not reuse IDs; generate a new one for every order. - An amend can replace it with
updated_client_order_id, which must not be in use by another open order.
Open order limit
In production, the default limit is 200,000 resting orders per account on each exchange shard. The limit counts every subaccount together. Some accounts have a different limit, and so does the demo environment. The check looks at the orders already resting, not at the new order: once the account is at its limit, every new order on that shard is rejected with400 too_many_open_orders, including immediate_or_cancel, fill_or_kill and reduce_only orders that would never rest. Cancel resting orders until the account is below its limit before submitting more.
Requests sent to the wrong exchange shard
Every market trades on one exchange shard, and an order can only be changed on the shard where it rests. A request that reaches a different shard is answered by that shard, which knows nothing about the order. For single-order requests:
In each case the order is untouched: a
404 from a cancel does not mean the order is gone. A batch cancel entry sent to the wrong shard gets the same code on that entry. Funding the wrong shard does not fix an insufficient_shard_balance returned for one of these requests.
To avoid this, send market_ticker on every cancel and decrease and leave exchange_index out, so the request is routed by ticker. Create and amend already carry the ticker.
Batch requests
Batch Create Orders and Batch Cancel Orders process their entries one at a time, in request order. A batch is not atomic. Rejected as a whole. The request is authenticated, parsed and checked before any order is sent. A failure at this stage returns an error status, and no order in the batch is submitted, even if only one entry caused it. Examples:400 too_many_orders_in_batch: the batch exceeds your tier’s maximum. Every tier can send at least 20 entries; above that, the maximum is the number of default-cost orders your per-second Write budget covers.400 invalid_order: any one order fails request validation, such asreduce_onlywithoutimmediate_or_cancel, anexpiration_timein milliseconds, a count that is not a valid quantity, or an emptyordersarray. An empty batch cancel returns400 invalid_parameters.400with a top-levelcode: the body cannot be parsed, as described at the top of this page.401or403 permission_denied: the request is not authenticated, or the API key is not allowed to call the endpoint.400or403 subaccount_is_restricted_by_API_key: any one entry names a subaccount that is out of range or that the API key is not allowed to use.- A location-restriction
403: any one entry in a batch create is for a market the account cannot trade from its location. A503is returned instead if that check cannot be completed. 429 too_many_requests: the whole batch does not fit in your Write budget.
201 (create) or 200 (cancel) response. Each entry of orders is in request order and either succeeded or carries an error object with the code, message and details described on this page. A failed create has no order_id; a failed cancel has reduced_by of 0.00. Entries have no HTTP status of their own, so check every entry for error.
An entry error is not always a rejection. If its code is one of the rejection codes on this page, that order was not placed or that cancel did not happen. If it is service_unavailable, internal_server_error, or a timeout or cancellation code such as context_deadline_exceeded, the exchange may still have processed that entry, even though the batch returned 201 or 200: a missing order_id or a reduced_by of 0.00 does not prove nothing happened. Handle those entries as described in When the outcome is unknown.
Reading an order back
Get Order, Get Orders and the queue position endpoints are updated asynchronously after the matching engine processes a write. An order created, amended or canceled moments ago can briefly return404 not_found or its previous state. The response of the write itself is the only immediate result. The user_orders WebSocket channel is also delivered asynchronously, so its update can arrive after the write returns; use it as the stream to wait on for confirmation and later changes.