Retrying an Order: clientOrderId and Duplicate Protection
The hardest moment in an order client is the timeout. The request left, no response came back, and the order may be live, rejected, or never received. Sending it again risks a duplicate. Not sending it risks a position you think you have and do not. The client order id is the field that resolves the ambiguity, but the two largest venues resolve it in opposite directions.

Cover artwork: CoinSoso · AI-generated, not a photograph of a real venue, product or market capture.
Key takeaways
- A client order id is assigned by the client, not the venue, and it is the only handle that survives a lost response.
- Binance treats newClientOrderId as unique among open orders and rejects a duplicate while the original is still open.
- Coinbase requires client_order_id and, on a duplicate, does not create a second order; it returns the order that already carries the id.
- The two behaviours are both safe for a retry, but only if the client queries by its own id instead of guessing from the error.
- A client order id that is reused after the original closes can collide in your own records even when the venue permits it.
The timeout is the hard case
A successful request returns an order id. A rejected request returns an error. A timeout returns nothing, and the order may be in any of three states: never received, received and rejected, or received and live.
Most order clients handle the first two states well. The third is the problem. If the order is live and the client sends it again, the account ends up with two positions where one was intended. If the client assumes the order is dead and does not send it, the account ends up with none.
Venues cannot solve this for the client, because the venue does not know whether the client’s request was lost on the way out or the response was lost on the way back. What the venue can do is give the client a field it controls, so that a retry carries the same identity as the original. That field is the client order id.
An identifier the client owns
Every order has two identifiers. The exchange assigns an order id when it accepts the order. The client assigns a client order id and sends it with the request.
The exchange id is the authoritative handle after acceptance, but it is useless during the ambiguous moment, because the client does not have it yet. The client order id exists before the request is sent. It can be written to a local record, included in the request, and used in a query if the response never arrives.
The field is optional on some venues and required on others, and the behaviour on a duplicate is not the same. The two implementations below are both defensible and both require different client logic.
Binance: unique among open orders
Binance’s Spot API calls the field newClientOrderId. It is optional: the documentation says it is “Automatically generated if not sent.” When it is sent, it is “A unique id among open orders.”
The uniqueness rule is narrower than “unique forever.” Binance states: “Orders with the same newClientOrderID can be accepted only when the previous one is filled,” and “otherwise the order will be rejected.”
Two consequences follow.
First, a duplicate sent while the original is open is rejected. The client gets an error, not a second order. That is the desired outcome for a retry: the account cannot end up with two live orders carrying the same identity.
Second, the identifier can become reusable once the original is no longer open. The venue’s rule is about open orders, not about the full history. A client that reuses an id after the original fills may be allowed to do so, and then its local record contains two orders with the same key. The venue is satisfied; the client’s bookkeeping is not.
There is a third detail in the cancel path. Binance’s cancel request also accepts a newClientOrderId, described as “Used to uniquely identify this cancel.” That id identifies the cancel instruction, not the order being cancelled. A client that stores only one id per order will overwrite the order’s identity with the cancel’s identity unless it keeps them separate.
Coinbase: required, and duplicate returns the original
Coinbase’s Advanced Trade API takes the other approach. The field is client_order_id, it is required, and the documentation defines it as “A unique ID provided for the order (used for identification purposes).”
The duplicate behaviour is the interesting part. Coinbase says: “If the ID provided is not unique, the order will not be created,” and “the order corresponding with that ID will be returned instead.”
That is idempotency in the strict sense. A retry with the same id does not produce an error and does not produce a second order. It returns the original order, which is exactly the information the client needed when the first response was lost. The client can then read the order’s status and decide what to do next.
The difference from Binance is not a difference in safety. Both venues prevent a duplicate live order. The difference is in what the client receives. Binance returns a rejection, so the client must query separately to learn the original’s state. Coinbase returns the original, so the retry itself is the query.
A client that handles only one of these behaviours will either treat Coinbase’s returned order as a failure (because it expected an error) or treat Binance’s rejection as a transport failure and retry again (because it expected an order object). Both mistakes are easy to make and both are visible only under a timeout.
Designing the identifier
The identifier has to satisfy two competing requirements: it must be unique, and it must be reconstructible after a crash.
A random UUID satisfies uniqueness and fails reconstruction. If the process dies after sending the order but before writing the id to disk, the retry cannot know which id was used.
A monotonic sequence satisfies reconstruction and fails uniqueness across restarts unless the sequence is persisted. A process that restarts at zero and reuses ids will collide with orders from the previous run.
A practical pattern combines both:
<strategy>-<utc-date>-<batch>-<sequence>
The strategy prefix keeps different strategies from colliding. The date and batch make the id reconstructible from the trading plan. The sequence distinguishes orders within the batch. The whole string is written to durable storage before the request is sent, so a crash leaves a record of what might be in flight.
The length and character set matter too. Binance publishes limits on client order ids, and a client that generates a 64-character string with punctuation may find that the venue rejects the id before it ever considers the order. Keeping the id to letters, digits and a separator is the safe default.
Querying after a timeout
The retry logic should not begin with a resend. It should begin with a query.
For Binance, the order can be queried by origClientOrderId, which looks up the order by the id the client assigned. That is the direct way to resolve the ambiguity: if the order exists, its status says whether it is live, filled or cancelled; if it does not exist, the id is free to use.
For Coinbase, the same information can be obtained by resending the create request with the same client_order_id, because the duplicate path returns the existing order. That is a legitimate use of the retry, but it depends on the client understanding that a returned order is not a new order.
The wrong approach is to list recent orders and look for one that “looks like” the missing one. Two orders with the same symbol, side and size are indistinguishable in a list, and the client may match the wrong one. The client id is the only field that was chosen by the client and is therefore the only field that can be matched with certainty.
What the id does not fix
The client order id resolves the identity problem. It does not resolve four other problems.
It does not guarantee the order was accepted. A rejected order and a lost order both produce no order id. The client id tells the client which request to query; it does not tell the client that the request succeeded.
It does not make a partial fill whole. If the order filled partially before the timeout, the client has a position and an open remainder. The query returns the order’s status and filled quantity, and the client has to decide whether to cancel the remainder or let it work. The execution report guide explains the fields that carry that information.
It does not survive a cancel-replace silently. A cancel-replace creates a new order with a new id. The old id points to a cancelled order, and the new id points to the replacement. A client that keeps one id per “intent” has to update the mapping when the replacement is created. The amend and cancel-replace guide covers the operations and their queue-priority consequences.
It does not protect against rate limits. A retry loop that hammers the query endpoint can hit the request-weight limit and produce a different kind of failure. The rate limit guide explains how weights are counted and why the retry itself has a cost.
A retry sequence that is safe on both venues
The following sequence works on Binance and Coinbase, with one branch for each duplicate behaviour.
- Before sending, write the client id and the full request to durable storage. The record must exist before the network call, or a crash leaves no trace.
- Send the order. If the response contains an order id, store it against the client id and stop.
- If the response is a rejection, read the error. A duplicate-id rejection on Binance means the original exists; query it by
origClientOrderId. Any other rejection is a real rejection; do not retry the same parameters. - If the response is a timeout, do not resend blindly. Query by the client id. If the order exists, read its status. If it does not exist, the id is free and the order was never accepted.
- If the venue returns an existing order on a duplicate, as Coinbase does, treat the returned order as the answer to the query, not as a new order.
- Record the final state. The id, the exchange order id, the status and the filled quantity are the minimum fields needed for reconciliation.
A checklist for order clients
- Assign a client id to every order, even on venues where the field is optional.
- Persist the id before the request leaves the process.
- Keep the client id and the exchange order id as separate fields.
- Keep the cancel request’s id separate from the order’s id on Binance.
- Treat Binance’s duplicate rejection and Coinbase’s returned order as two valid duplicate behaviours.
- Query by client id after a timeout instead of listing recent orders.
- Update the mapping when an amend or cancel-replace creates a new order.
- Do not reuse an id after the original closes unless your records can distinguish the two.
Sources and what they support
- Binance Spot API, Trading endpoints — states that
newClientOrderIdis optional and auto-generated if not sent, that it is unique among open orders, that orders with the same id are accepted only when the previous one is filled and are otherwise rejected, and that a cancel request carries its ownnewClientOrderId. Read 2026-11-03. - Coinbase Advanced Trade API, Create order — states that
client_order_idis required and unique, that a non-unique id means the order is not created, and that the order corresponding with that id is returned instead. Read 2026-11-03.
Risk note. Order-id behaviour and duplicate handling were read from Binance’s and Coinbase’s API documentation on 2026-11-03. Venues can change validation rules, and an id that is accepted today may be rejected tomorrow. Nothing here is investment advice.
Sources
How this was researched. Written from primary documentation and the exchange or protocol's own published material, with sources listed at the end of the article.
Primary sources for the factual claims on this page. A date appears only where a human has actually read the source; where it is missing, the check is still outstanding.
Frequently asked questions
Is clientOrderId the same as the exchange's order id?
No. The exchange assigns its own order id and returns it in the response. The client order id is assigned by the client and sent with the request. The two are linked after acceptance, but only the client id is known before the request is sent, which is why it is the one that can be used to resolve a timeout.
What happens if I send the same clientOrderId twice on Binance?
While the first order is still open, Binance rejects the duplicate. The documentation describes newClientOrderId as unique among open orders and says orders with the same id can be accepted only when the previous one is filled. Once the first order is no longer open, the identifier may become reusable, which is a reason to keep your own record of every id you have ever sent.
What happens on Coinbase if client_order_id is duplicated?
Coinbase's documentation says the order will not be created and the order corresponding with that id will be returned instead. That makes a retry idempotent: the client gets back the original order rather than a second one. The field is required on the create-order request, so a client that omits it cannot rely on this behaviour.
Should the client order id be random or deterministic?
It should be deterministic enough to reconstruct after a crash and unique enough not to collide. A common pattern is a strategy prefix, a date or batch number, and a sequence number, stored before the request is sent. A purely random id is unique but cannot be reconstructed, and a purely sequential id collides after a restart unless the sequence is persisted.
Does a client order id survive an amend?
On Binance, an amend can reuse the current clientOrderId by sending it as the newClientOrderId. That is useful for keeping one identity across the change, but it also means the id no longer points to the original order's parameters. A client that stores the id as a key must update the record when the order is amended.
Where to go next
Independent research. Each card shows the platform’s invite code, and its Create Account button is a referral link that may earn us a commission at no extra cost to you. Whether an account can be opened is decided by the platform at sign-up.
Binance

One of the largest global exchanges by traded volume, with a very broad product range.
20% Trading Fee Rebate
Use invite code con888 when signing up to enjoy trading fee benefits.
Download from the exchange's official download service. Verify the source and follow the on-screen installation instructions.
Do not install an APK from an unknown source, or one that has been modified or cannot be checked.
OKX

A large global exchange offering spot, derivatives and a self-custody wallet.
20% Trading Fee Rebate
Enter invite code con888 when signing up to check available trading fee benefits.
Download from the exchange's official download service. Verify the source and follow the on-screen installation instructions.
Do not install an APK from an unknown source, or one that has been modified or cannot be checked.