Skip to main content
This page covers the Predictions REST order endpoints under /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:
Read 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 same code 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

A 408 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:
  1. Find out what happened: wait for the order’s update on the user_orders WebSocket channel, or read it back with Get Orders, allowing for the read delay.
  2. Resubmit only once you know the first attempt did not take effect.
Sending a 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 with 400 invalid_parameters and no details field. In Batch Create Orders, only that entry is rejected: the batch still returns 201, and the entry carries an error with code invalid_parameters and an empty details.
  • On an amend, a malformed client_order_id or updated_client_order_id is also a 400, but the code is a parser message that starts with ID_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 HTTP 409 on Create Order, an entry error inside the 201 on 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_exists to 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 with 400 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 as reduce_only without immediate_or_cancel, an expiration_time in milliseconds, a count that is not a valid quantity, or an empty orders array. An empty batch cancel returns 400 invalid_parameters.
  • 400 with a top-level code: the body cannot be parsed, as described at the top of this page.
  • 401 or 403 permission_denied: the request is not authenticated, or the API key is not allowed to call the endpoint.
  • 400 or 403 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. A 503 is returned instead if that check cannot be completed.
  • 429 too_many_requests: the whole batch does not fit in your Write budget.
Reported per entry. Once those checks pass, entries are sent one by one, and a failure to convert, route or place an entry, including any rejection by the exchange, is reported inside a 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 return 404 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.