0.001 x 60,000 = 60 USDT| Spot | Futures | |
|---|---|---|
| What you own | The actual coin | A contract that tracks the price |
| Money required | The full amount | A fraction of it, called margin |
| Borrowing involved | None | Yes. That is what leverage is |
| Worst case | The coin loses value | The exchange closes your position and your margin is gone |
| Can you be forced out? | No | Yes, this is called liquidation |
BASE-QUOTE, in capitals, joined by a hyphen: BTC-USDT, ETH-USDT.BTC) is the coin you are buying or selling.USDT) is the money you pay with or receive.BTC-USDT price of 60000 means one BTC costs 60,000 USDT.BTC-USDT:| Type | What you are saying | You control | The price field |
|---|---|---|---|
MARKET | "Fill me now, at whatever the best available price is" | The quantity only | Must be left out |
LIMIT | "Fill me only at my price or better. Wait if you have to" | The price and the quantity | Required |
MARKET order almost always fills straight away, but you cannot know the exact price in advance. A LIMIT order gets you the price you asked for, but it may sit open for a long time and may never fill at all.Sending a priceon aMARKETorder is rejected, not ignored. Leave the field out entirely.
available is what you can spend right now.locked is money that is already committed to an open order.available to locked. It is still your money. It is simply spoken for, so you cannot accidentally spend it twice.| Side | Reserves | How much |
|---|---|---|
BUY | The quote coin (USDT) | quantity x price, plus a buffer on MARKET orders |
SELL | The base coin (BTC) | quantity |
reservedAsset field on the order tells you which coin was set aside, and reservedAmount tells you how much. Trading fees are charged separately and are not part of the reserve, so leave a little headroom.SPOT, ASSET and FUTURE. Spot orders spend from the spot wallet only. If your money is sitting in the asset or futures wallet, you must move it in the web app first. The API deliberately cannot move funds between your own wallets.POST /v1/orders.available to locked.201 Created back with an order object.A
201does not mean the market accepted your order201 Createdonly means "we took your order and set your money aside". The market may still refuse it a moment later, and a refusal is not an HTTP error. Your HTTP client will not raise an exception, and a naiveif (response.ok)check will treat a rejected order as a success.Always read the statusfield in the response body. Never decide based on the HTTP status code alone.
status | What it means | What to do |
|---|---|---|
PENDING_SUBMIT | We accepted it and reserved your money. Not at the market yet | Wait, then poll |
SUBMITTED | Live at the market, waiting to fill | Wait, then poll |
FILLED | Fully traded | Done |
CANCELED | Cancelled. Reserve released | Done |
EXPIRED | Expired. Reserve released | Done |
REJECTED | Refused. Reserve released | Read rejectReason and venueCode from GET /v1/orders/{id} |
UNKNOWN | The market did not answer in time. Your money is still reserved | Poll GET /v1/orders/{id}. Do not place the order again |
UNKNOWN deserves real care. It does not mean the order failed. It means nobody knows yet, and a background process is working out the truth. If you re-send the order assuming it failed, you can end up with two live orders and double the exposure you intended.GET /v1/orders/{id} for one order, or GET /v1/orders?limit=50 for your recent orders, newest first.limit, so you can never read further back than your most recent 200 orders.GET /v1/market/symbols/v1/deposit/assets lists coins you can hold, which is a different thing from pairs you can trade.GET /v1/market/tickers # 24-hour summary of every pair
GET /v1/market/depth?symbol=BTC-USDT # the actual offers waiting in the bookMARKET order. The best ask is roughly what you will pay to buy, and the best bid is roughly what you will receive if you sell. The gap between them is the spread, and on a thin market it can be wide.GET /v1/transfers/balances?asset=USDTwallets.USDT.spot.available. That is what you can actually spend. Anything in locked is already committed to other orders.quantity x price of the quote coin. Buying 0.001 BTC at 60,000 USDT needs 60 USDT available.quantity of the base coin. Selling 0.001 BTC needs 0.001 BTC available.409 INSUFFICIENT_BALANCE.POST /v1/orders
Content-Type: application/json
Idempotency-Key: <a fresh unique string>
{
"symbol": "BTC-USDT",
"side": "BUY",
"type": "LIMIT",
"quantity": "0.001",
"price": "60000"
}Idempotency-Key header is what makes retrying safe. If your network drops and you never see the response, you can send the identical request with the identical key and get the original result back rather than placing a second order.201 Created. Now look inside:{
"id": "clw8n2k4x0003abcdefghij",
"status": "SUBMITTED",
"orderType": "LIMIT",
"reservedAmount": "60",
"reservedAsset": "USDT"
}status: "SUBMITTED" means it is live. status: "REJECTED" means it was refused, even though the HTTP code was 201.Note the naming difference: you send type, and you receiveorderType. They are not the same field name.timeInForcecomes back as"GTC"for LIMIT orders andnullfor MARKET orders, andpriceisnullon MARKET orders.
GET /v1/orders/{id}status reaches a terminal value: FILLED, CANCELED, EXPIRED or REJECTED. filledQty shows how much has traded so far, and avgPrice shows the average price you actually got.DELETE /v1/orders/{id}
Idempotency-Key: <a fresh unique string>status: "CANCELED" and the reserve released.A failed cancel is not a cancelled order, and it is not a failed order either. If this call returns 501,409or503, nothing was released and no status was written. Your order is still whateverGET /v1/orders/{id}says it is a moment later. Re-read it rather than assuming. Never treat a failed cancel as a reason to place a replacement order.
GET /v1/transfers/balances?asset=BTCavailable has gone up and your USDT has gone down. The trade is complete.| Mistake | What you see | Fix |
|---|---|---|
Treating 201 as "the trade worked" | Silent losses. Rejected orders counted as successes | Branch on the status field |
Sending price with a MARKET order | 400 VALIDATION_ERROR | Leave price out entirely for MARKET |
| Sending numbers instead of strings for money | Rounding errors, rejected values | Use strings: "0.001", never 0.001 |
Comparing money with == on strings | "1.5" and "1.50" look different but are equal | Compare through a decimal type |
| Adding a field the schema does not know | The whole request is rejected | The body schema is strict. Send only documented fields |
Filtering with ?symbol= on GET /v1/orders | You get orders for every symbol | There is no symbol filter. Filter in your own code |
| Retrying by resending identical signed bytes | 401 REPLAYED_REQUEST | Sign again with a fresh timestamp |
| Money sitting in the wrong wallet | 409 INSUFFICIENT_BALANCE while the app shows a balance | Spot orders spend from the spot wallet. Move funds in the web app |
Re-placing an order after UNKNOWN or a failed cancel | Two live orders, double the exposure | Poll and find out the real state first |