# BTSE API Documentation > Developer documentation for the BTSE API. Covers REST and WebSocket interfaces for Markets, Spot, Futures, Wallet, Earn, OTC, FIX, plus authentication, error handling, and client SDKs. OpenAPI 3.0.3 (REST) and AsyncAPI 2.6.0 (WebSocket) specs are the single source of truth and are served alongside the docs. Production hosts: `api.btse.com` (REST) and `wss://ws.btse.com` (WebSocket). Testnet hosts: `testapi.btse.io` and `wss://testws.btse.io`. --- # Quickstart — Your First Trade in 5 Minutes This guide walks you through a complete integration: fetch public market data, authenticate, place an order on testnet, and subscribe to real-time updates via WebSocket. --- ## Prerequisites - A BTSE account — sign up at - An API key with **Trading** permission — see [Authentication → Step 1](/authentication#step-1--create-an-api-key) - Python 3.8+ or Node.js 18+ installed > **Use testnet first:** All examples below target **testnet** so you can experiment without risking real funds. Switch to production URLs when you're ready. > > | Endpoint | Production | Testnet | > |---|---|---| > | Spot REST | https://api.btse.com/spot | https://testapi.btse.io/spot | > | Spot WS | wss://ws.btse.com/ws/spot | wss://testws.btse.io/ws/spot | --- ## Step 1 — Fetch Public Market Data (No Auth Required) Get a list of all tradeable spot markets to find the symbol you want to trade. **Endpoint:** `GET /public-api/market/v1/markets` No headers required. Returns price, volume, and trading pair configuration for every market. See [Markets Information](/markets/rest/get-markets) for the full field reference. > Python ```python import requests resp = requests.get("https://testapi.btse.io/public-api/market/v1/markets") symbols = resp.json()["data"]["symbols"] # Find BTC-USDT btc = next(s for s in symbols if s["symbol"] == "BTC-USDT") print(f"BTC-USDT minOrderSize: {btc['minOrderSize']}, active: {btc['active']}") ``` > Node.js ```javascript const resp = await fetch( "https://testapi.btse.io/public-api/market/v1/markets" ); const { data } = await resp.json(); const btc = data.symbols.find((s) => s.symbol === "BTC-USDT"); console.log(`BTC-USDT minOrderSize: ${btc.minOrderSize}, active: ${btc.active}`); ``` > curl ```bash curl https://testapi.btse.io/public-api/market/v1/markets?symbol=BTC-USDT ``` --- ## Step 2 — Sign an Authenticated Request Private endpoints require three headers. The signature is `HMAC-SHA384(secret, path + nonce + body)`. See [Authentication](/authentication) for the full specification. **Headers for every private request:** | Header | Value | |---|---| | `request-api` | Your API key | | `request-nonce` | Current UTC timestamp in milliseconds | | `request-sign` | HMAC-SHA384 signature | **Signature input** = `urlPath + nonce + bodyString` - For GET requests, `bodyString` is empty string `""` - For POST/PUT/DELETE, `bodyString` is the raw JSON body > Python — signing helper ```python import hmac, hashlib, time, json, requests API_KEY = "your-api-key" API_SECRET = "your-api-secret" BASE_URL = "https://testapi.btse.io/spot" def signed_request(method, path, body=None): nonce = str(int(time.time() * 1000)) body_str = json.dumps(body) if body else "" message = path + nonce + body_str signature = hmac.new( API_SECRET.encode(), message.encode(), hashlib.sha384 ).hexdigest() headers = { "request-api": API_KEY, "request-nonce": nonce, "request-sign": signature, "Content-Type": "application/json", } url = BASE_URL + path if method == "GET": return requests.get(url, headers=headers) elif method == "POST": return requests.post(url, headers=headers, json=body) elif method == "DELETE": return requests.delete(url, headers=headers) ``` > Node.js — signing helper ```javascript import crypto from "crypto"; const API_KEY = "your-api-key"; const API_SECRET = "your-api-secret"; const BASE_URL = "https://testapi.btse.io/spot"; async function signedRequest(method, path, body) { const nonce = Date.now().toString(); const bodyStr = body ? JSON.stringify(body) : ""; const message = path + nonce + bodyStr; const signature = crypto .createHmac("sha384", API_SECRET) .update(message) .digest("hex"); const headers = { "request-api": API_KEY, "request-nonce": nonce, "request-sign": signature, "Content-Type": "application/json", }; const resp = await fetch(BASE_URL + path, { method, headers, body: body ? JSON.stringify(body) : undefined, }); return resp.json(); } ``` --- ## Step 3 — Place a Test Order Place a limit buy order for BTC-USD on testnet. **Endpoint:** `POST /api/v3.3/order` **Required permission:** Trading The response includes `status: 2` (ORDER_INSERTED) on success. See [Error Codes → API Status Enum](/error-codes#api-status-enum) for all status codes. See [Create New Order](/spot/rest/create-order) for the full field reference. > Python ```python order = { "symbol": "BTC-USD", "side": "BUY", "type": "LIMIT", "price": 20000, "size": 0.001, "time_in_force": "GTC", } resp = signed_request("POST", "/api/v3.3/order", order) result = resp.json() print(f"Order ID: {result[0]['orderID']}, status: {result[0]['status']}") ``` > Node.js ```javascript const order = { symbol: "BTC-USD", side: "BUY", type: "LIMIT", price: 20000, size: 0.001, time_in_force: "GTC", }; const result = await signedRequest("POST", "/api/v3.3/order", order); console.log(`Order ID: ${result[0].orderID}, status: ${result[0].status}`); ``` > Response ```json [ { "status": 2, "symbol": "BTC-USD", "orderType": 76, "price": 20000.0, "side": "BUY", "size": 0.001, "orderID": "990db9b6-2ed4-4c68-b46e-827c88cc3884", "timestamp": 1660208800123, "triggerPrice": 0.0, "trigger": false, "averageFillPrice": 0.0, "fillSize": 0.0, "postOnly": false, "remainingSize": 0.001, "time_in_force": "GTC" } ] ``` --- ## Step 4 — Subscribe to Real-Time Updates via WebSocket Open a WebSocket connection to receive live order updates and trade fills. **Connection flow:** 1. Connect to wss://testws.btse.io/ws/spot 2. Send `ping` every 15 seconds to keep alive (server responds `pong`) 3. Authenticate with `authKeyExpires` to access private topics 4. Subscribe to topics: `notificationApiV3` (order updates), `fills` (trade fills) **WS authentication signature:** ``` HMAC-SHA384(secret, "/ws/spot" + nonce) ``` See [WebSocket Authentication](/spot/websocket/websocket-authentication) for details. > Python (using websockets library) ```python import asyncio, websockets, json, hmac, hashlib, time WS_URL = "wss://testws.btse.io/ws/spot" API_KEY = "your-api-key" API_SECRET = "your-api-secret" async def main(): async with websockets.connect(WS_URL) as ws: # 1. Authenticate nonce = str(int(time.time() * 1000)) sig = hmac.new( API_SECRET.encode(), f"/ws/spot{nonce}".encode(), hashlib.sha384, ).hexdigest() await ws.send(json.dumps({ "op": "authKeyExpires", "args": [API_KEY, nonce, sig], })) # 2. Subscribe to order notifications await ws.send(json.dumps({ "op": "subscribe", "args": ["notificationApiV3"], })) # 3. Listen for messages async for msg in ws: if msg == "pong": continue data = json.loads(msg) print(json.dumps(data, indent=2)) asyncio.run(main()) ``` > Node.js (using ws library) ```javascript import WebSocket from "ws"; import crypto from "crypto"; const WS_URL = "wss://testws.btse.io/ws/spot"; const API_KEY = "your-api-key"; const API_SECRET = "your-api-secret"; const ws = new WebSocket(WS_URL); ws.on("open", () => { // 1. Authenticate const nonce = Date.now().toString(); const sig = crypto .createHmac("sha384", API_SECRET) .update("/ws/spot" + nonce) .digest("hex"); ws.send(JSON.stringify({ op: "authKeyExpires", args: [API_KEY, nonce, sig], })); // 2. Subscribe to order notifications ws.send(JSON.stringify({ op: "subscribe", args: ["notificationApiV3"], })); // 3. Keep alive setInterval(() => ws.send("ping"), 15000); }); ws.on("message", (msg) => { const str = msg.toString(); if (str === "pong") return; console.log(JSON.parse(str)); }); ``` --- ## Step 5 — Cancel the Test Order Clean up by cancelling the order you placed. **Endpoint:** `DELETE /api/v3.3/order` Pass the `orderID` from Step 3. See [Cancel Order](/spot/rest/cancel-order) for the full reference. > Python ```python resp = signed_request( "DELETE", "/api/v3.3/order?symbol=BTC-USD&orderID=990db9b6-2ed4-4c68-b46e-827c88cc3884", ) print(resp.json()) ``` > Node.js ```javascript const result = await signedRequest( "DELETE", "/api/v3.3/order?symbol=BTC-USD&orderID=990db9b6-2ed4-4c68-b46e-827c88cc3884" ); console.log(result); ``` --- ## What's Next? | Goal | Resource | |------|----------| | Explore all spot endpoints | [Spot API Overview](/spot/overview) | | Try endpoints interactively | [API Explorer](/api-explorer/spot/spot-api) | | Trade futures | [Futures API Overview](/futures/overview) | | Manage wallets & withdrawals | [Wallet API Overview](/wallet/overview) | | Build a real-time orderbook | [Orderbook Incremental Updates](/spot/websocket/orderbook-incremental-updates) | | Understand error responses | [Error Codes](/error-codes) | --- # Authentication All private BTSE API endpoints require three HTTP headers on every request. Public endpoints (market data, orderbook, trades) do **not** require authentication. > **New here? Start here first.:** Set up your API key and understand the signing process before calling any private endpoint. Most integration failures are authentication issues. > > **Need to verify your signing code?** Use the [Auth Tester](/auth-tester) to compute signatures and send live test requests from your browser. --- ## Step 1 — Create an API Key 1. Log in to 2. Click **Account** in the top-right corner 3. Select the **API** tab 4. Click **New API** to generate a key and passphrase > **Important:** The passphrase is shown only once at creation time. Store it securely — you cannot retrieve it again. --- ## Step 2 — Understand the Three Headers Every authenticated request must include these three headers: | Header | Type | Description | | --- | --- | --- | | `request-api` | String | Your API key | | `request-nonce` | Long | Current UTC timestamp in **milliseconds** (e.g. `1624985375123`) | | `request-sign` | String | HMAC-SHA384 signature (see below) | --- ## Step 3 — Construct the Signature The signature is computed as: ``` HMAC-SHA384(secret, urlpath + nonce + bodyStr) ``` Where: - **`secret`** — Your API secret (the passphrase from Step 1) - **`urlpath`** — The path portion of the URL **only** (e.g. `/api/v3.3/order`). **Do not include query strings.** Query parameters are sent on the wire but are NOT part of the signed payload — including them produces `401 Signature verification failed`. - **`nonce`** — The same value you send in `request-nonce` - **`bodyStr`** — The raw JSON request body. Use an **empty string `""`** for GET requests (no body) The three parts are **concatenated directly** with no separators between them. ### Shell ```bash echo -n "/api/v3.3/order1624985375123{\"postOnly\":false,\"price\":8500.0,\"side\":\"BUY\",\"size\":0.002,\"stopPrice\":0.0,\"symbol\":\"BTC-USD\",\"time_in_force\":\"GTC\",\"trailValue\":0.0,\"triggerPrice\":0.0,\"txType\":\"LIMIT\",\"type\":\"LIMIT\"}" \ | openssl dgst -sha384 -hmac "YOUR_SECRET" ``` ### Python ```python import hmac, hashlib, time, json, requests API_KEY = "your-api-key" API_SECRET = "your-api-secret" BASE_URL = "https://api.btse.com/spot" def signed_request(method, path, body=None): nonce = str(int(time.time() * 1000)) body_str = json.dumps(body) if body else "" message = path + nonce + body_str signature = hmac.new( API_SECRET.encode(), message.encode(), hashlib.sha384 ).hexdigest() headers = { "request-api": API_KEY, "request-nonce": nonce, "request-sign": signature, "Content-Type": "application/json", } url = BASE_URL + path resp = requests.request(method, url, headers=headers, json=body if method != "GET" else None) return resp.json() # Example: query open orders orders = signed_request("GET", "/api/v3.3/user/open_orders") ``` ### JavaScript / Node.js ```javascript import crypto from "crypto"; const API_KEY = "your-api-key"; const API_SECRET = "your-api-secret"; const BASE_URL = "https://api.btse.com/spot"; async function signedRequest(method, path, body) { const nonce = Date.now().toString(); const bodyStr = body ? JSON.stringify(body) : ""; const message = path + nonce + bodyStr; const signature = crypto .createHmac("sha384", API_SECRET) .update(message) .digest("hex"); const resp = await fetch(BASE_URL + path, { method, headers: { "request-api": API_KEY, "request-nonce": nonce, "request-sign": signature, "Content-Type": "application/json", }, body: body ? JSON.stringify(body) : undefined, }); return resp.json(); } // Example: query open orders const orders = await signedRequest("GET", "/api/v3.3/user/open_orders"); ``` ### Go ```go package main import ( "crypto/hmac" "crypto/sha512" "encoding/hex" "fmt" "io" "net/http" "strconv" "strings" "time" ) const ( apiKey = "your-api-key" apiSecret = "your-api-secret" baseURL = "https://api.btse.com/spot" ) func signedRequest(method, path, body string) ([]byte, error) { nonce := strconv.FormatInt(time.Now().UnixMilli(), 10) message := path + nonce + body mac := hmac.New(sha512.New384, []byte(apiSecret)) mac.Write([]byte(message)) signature := hex.EncodeToString(mac.Sum(nil)) req, _ := http.NewRequest(method, baseURL+path, strings.NewReader(body)) req.Header.Set("request-api", apiKey) req.Header.Set("request-nonce", nonce) req.Header.Set("request-sign", signature) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) if err != nil { return nil, err } defer resp.Body.Close() return io.ReadAll(resp.Body) } func main() { data, err := signedRequest("GET", "/api/v3.3/user/open_orders", "") if err != nil { panic(err) } fmt.Println(string(data)) } ``` ### Worked example — Place an order Given these values: | Field | Value | | --- | --- | | `request-nonce` | `1624985375123` | | `request-api` | `4e9536c79f0fdd72bf04f2430982d3f61d9d76c996f0175bbba470d69d59816x` | | Secret | `848db84ac252b6726e5f6e7a711d9c96d9fd77d020151b45839a5b59c37203bx` | | URL path | `/api/v3.3/order` | | Request body | `{"postOnly":false,"price":8500.0,"side":"BUY","size":0.002,"stopPrice":0.0,"symbol":"BTC-USD","time_in_force":"GTC","trailValue":0.0,"triggerPrice":0.0,"txType":"LIMIT","type":"LIMIT"}` | String to sign: ``` /api/v3.3/order1624985375123{"postOnly":false,"price":8500.0,"side":"BUY","size":0.002,"stopPrice":0.0,"symbol":"BTC-USD","time_in_force":"GTC","trailValue":0.0,"triggerPrice":0.0,"txType":"LIMIT","type":"LIMIT"} ``` Resulting `request-sign`: ``` e9cd0babdf497b536d1e48bc9cf1fadad3426b36406b5747d77ae4e3cdc9ab556863f2d0cf78e0228c39a064ad43afb7 ``` --- ## Permission Types API keys carry one or more permissions. Endpoints enforce the minimum required permission. You can see which permission each endpoint needs from the badge at the top of its section. | Badge | Permission | What it unlocks | | --- | --- | --- | | 🌐 **Public** | None | Market data, orderbook, public trades | | 🔒 **Read** | Read | Account balances, order status, trade history, wallet history | | 🔒 **Trading** | Trading | Place, amend, cancel orders | | 🔒 **Transfer** | Transfer | Withdraw, deposit, transfer between wallets | --- ## Rate Limits {#rate-limits} Rate limits are enforced per API endpoint and per user account. | Category | Per API | Per User | | --- | --- | --- | | **Query** | 15 req/s | 30 req/s | | **Orders** | 75 req/s | 75 req/s | ### Tiered blocking Exceeding a rate limit triggers a tiered block: | Tier | Duration | | --- | --- | | 1st breach | 1 second | | 2nd breach | 5 minutes | | 3rd breach | 15 minutes | The block timer resets if no further violations occur within 1 hour, or after the 15-minute block expires. A `Retry-After` header is included in all `429` responses indicating the unlock timestamp. --- ## Environment Test your integration without risking real funds. Testnet endpoints mirror production behaviour. | Endpoint | Production | Testnet | |---|---|---| | Markets REST | https://api.btse.com | https://testapi.btse.io | | Spot REST | https://api.btse.com/spot | https://testapi.btse.io/spot | | Spot WS | wss://ws.btse.com/ws/spot | wss://testws.btse.io/ws/spot | | Futures REST | https://api.btse.com/futures | https://testapi.btse.io/futures | | Futures WS | wss://ws.btse.com/ws/futures | wss://testws.btse.io/ws/futures | | Wallet REST | https://api.btse.com | https://testapi.btse.io | | Earn REST | https://api.btse.com/spot | https://testapi.btse.io/spot | | OTC REST | https://api.btse.com | https://testapi.btse.io | | OTC WS | wss://ws.btse.com/ws/otc | wss://testws.btse.io/ws/otc | --- ## SDKs and Libraries BTSE provides auto-generated SDK packages for Python, JavaScript, and Java. See [Client SDKs](/sdks) to download. The signing examples above (Python, JavaScript, Go) can be used as a starting point for manual integration in any other language. **Other resources:** - The [API Explorer](/api-explorer/spot/spot-api) provides interactive request testing with auto-generated code snippets - The OpenAPI specs in this documentation can be used with standard OpenAPI code generators (e.g. `openapi-generator-cli`) to produce client libraries in any language - Download specs from the [API Explorer](/api-explorer/spot/spot-api) — each product's overview page includes a spec download link If you build a community SDK and would like it listed here, contact . --- # Error Codes --- ## HTTP Status Codes All API responses return a standard HTTP status code. Treat any non-`200` response as an error. | Code | Meaning | What to do | | --- | --- | --- | | `200` | Success | Parse the response body for your data | | `400` | Bad Request — invalid or missing parameters | Check request params against the endpoint spec | | `401` | Unauthorized — missing or invalid credentials | Verify API key, nonce freshness, and signature | | `403` | Forbidden — insufficient permission | Check your API key has the required permission level | | `404` | Not Found — resource does not exist | Verify the endpoint path and any IDs | | `405` | Method Not Allowed — wrong HTTP method | Use the correct method (GET/POST/PUT/DELETE) | | `408` | Request Timeout — exceeded 30 seconds | Retry once; if persistent, check server status | | `429` | Too Many Requests — rate limit exceeded | Back off and retry after `Retry-After` header value | | `451` | Unavailable For Legal Reasons — account banned | Contact support | | `500` | Internal Server Error | Retry with backoff; report if persistent | ### Error Response Format Non-`200` responses return a JSON body: ```json { "status": 400, "error": "Bad Request", "code": 301, "message": "Invalid order size" } ``` | Field | Description | |---|---| | `status` | HTTP status code | | `error` | HTTP status text | | `code` | Internal error code (see API Status Enum below) | | `message` | Human-readable error description | ### Handling `429` (Rate Limit) When rate-limited, the response includes a `Retry-After` header indicating the number of **seconds** to wait before retrying ([RFC 7231 §7.1.3](https://datatracker.ietf.org/doc/html/rfc7231#section-7.1.3)): ``` HTTP/1.1 429 Too Many Requests Retry-After: 60 ``` **Strategy:** Parse the `Retry-After` value as an integer number of seconds, wait that long, then resume. Standard HTTP client libraries (`urllib3.Retry`, `axios-retry`, OkHttp) handle this automatically. Do not retry immediately — repeated violations trigger escalating blocks (1s → 5min → 15min). See [Authentication → Rate Limits](/authentication#rate-limits). ### Handling `401` (Unauthorized) Common causes: - **Wrong nonce** — the nonce must be the current UTC time in milliseconds. Clock drift > a few seconds causes rejection. - **Wrong signature** — the signing input is `path + nonce + body` with no separators. Verify the concatenation. - **Body mismatch** — the body you sign must be identical to the body you send. Watch for JSON key ordering if your language reorders keys. --- ## Common Error Strings → Likely Cause {#common-error-strings} The error `message` field is often opaque on its own. Use this table to map the literal string the API returned to the most common root cause, ordered by frequency. | Error string in response | Status | Most likely cause | What to check first | | --- | --- | --- | --- | | `api parameter is mandatory` | `401` | **Wrong header names.** The required headers are `request-api`, `request-nonce`, `request-sign` — *not* `X-BTSE-APIKEY`, `BTSE-API-KEY`, etc. | Header names match the [Authentication](/authentication) spec exactly, lowercase, hyphen-separated. | | `Authentication Failed` (code `10002`) | `401` | **Signature payload mismatch.** The string you hashed is not what the server reconstructed. | The signed `urlpath` excludes any query string; the body you signed is byte-identical to the body sent. See the [Signed Request Walkthrough](/signed-request-walkthrough). | | `Signature verification failed` | `401` | Same as above — query string included in the signed `urlpath`, or body re-serialized between signing and sending. | Print the exact bytes passed to HMAC, compare with the spec. | | `Invalid nonce` / nonce reuse | `401` | **Clock drift or reused nonce.** Nonce must be current UTC in **milliseconds**, monotonically increasing per API key. | Use `time.time() * 1000` (Python) / `Date.now()` (JS). NTP-sync your machine if drift > a few seconds. | | `Invalid request parameters` (code `-2`) | `400` | Unknown symbol, malformed `walletName`, missing required param. | Diff your request against the endpoint's spec page. | | `Invalid order size` (code `301`) | `400` | Size below the symbol's minimum or above the maximum. | Check `minOrderSize` / `maxOrderSize` from `/api/v3.3/market_summary` (Spot) or the Futures market info endpoint. | | `Invalid order price` (code `302`) | `400` | Price outside the contract's tick-size grid, or price filter (`PRICE_FILTER`). | Round price to the symbol's tick size before submitting. | | `Maximum open orders exceeded` (code `304`) | `400` | Per-user or per-symbol open-order limit reached. | Cancel stale orders or use `cancelAllAfter` (dead-man's switch). | | `Order not found` (code `16`) | `404` | The `orderID` / `clOrderID` doesn't match any active or recent order. | Verify the ID. After 24h cancelled/filled orders may no longer be queryable by ID — use trade-history endpoints instead. | | `Insufficient balance` (code `8`) | `400` | Wallet balance below required amount including fees. | Check available (not total) balance, account for fees and any open orders that already reserve funds. | | `Rate limit exceeded` (code `303`) | `429` | API rate limit tier triggered. | See [Authentication → Rate Limits](/authentication#rate-limits). Back off using `Retry-After`. | | `Account is undergoing liquidation` (code `64`) | `400` | Futures account in liquidation — new orders blocked. | Wait for liquidation to complete; query position status. | | `Order is undergoing auto-deleveraging` (code `1004`) | `400` | Position selected for ADL — cannot amend/cancel. | Position will be force-reduced; monitor via WebSocket notifications. | > **Tip for AI/agent integrations:** when you encounter an error string that isn't in this table, paste the literal `message` field into a search of this file before assuming it's a new bug. Most "weird" BTSE errors are one of the rows above, surfaced with slightly different wording. --- ## API Status Enum Internal numeric status codes appear in order and WebSocket response payloads. | Code | Constant | Description | | --- | --- | --- | | `-2` | `INVALID_REQUEST` | Invalid request parameters (e.g. unknown symbol, malformed `walletName`) | | `-1` | `TIMEOUT` | Request timed out — verify order status separately | | `1` | `MARKET_UNAVAILABLE` | Futures market is unavailable | | `2` | `ORDER_INSERTED` | Order inserted successfully | | `4` | `ORDER_FULLY_TRANSACTED` | Order fully filled | | `5` | `ORDER_PARTIALLY_TRANSACTED` | Order partially filled | | `6` | `ORDER_CANCELLED` | Order cancelled successfully | | `7` | `ORDER_REFUNDED` | Order refunded | | `8` | `INSUFFICIENT_BALANCE` | Insufficient account balance | | `9` | `TRIGGER_INSERTED` | Trigger order inserted successfully | | `10` | `TRIGGER_ACTIVATED` | Trigger order activated | | `11` | `ERROR_INVALID_CURRENCY` | Invalid currency specified | | `12` | `ERROR_UPDATE_RISK_LIMIT` | Error updating risk limit | | `13` | `ERROR_INVALID_LEVERAGE` | Invalid leverage value | | `15` | `ORDER_REJECTED` | Order rejected | | `16` | `ORDER_NOTFOUND` | Order not found by the provided `orderID` or `clOrderID` | | `17` | `REQUEST_FAILED` | Request failed — verify order status | | `20` | `SUCCESS` | Action completed successfully | | `21` | `FREEZE_SUCCESSFUL` | Freeze action succeeded | | `27` | `TRANSFER_SUCCESSFUL` | Funds transferred between futures and spot successfully | | `28` | `TRANSFER_UNSUCCESSFUL` | Transfer between spot and futures failed | | `29` | `QUERY_GET_ORDERS` | Response to a get-orders query | | `31` | `QUERY_GET_POSITIONS` | Response to a get-positions query | | `33` | `QUERY_GET_ALL_POSITIONS_ORDERS` | Response to a get-all-positions-orders query | | `34` | `QUERY_WALLET` | Response to a wallet query | | `36` | `QUERY_FUTURES_MARGIN` | Response to a futures-margin query | | `41` | `ERROR_INVALID_RISK_LIMIT` | Invalid risk limit specified | | `51` | `QUERY_GET_ORDERS_WITH_LIMIT` | Response to a paginated get-orders query (with limit) | | `64` | `STATUS_LIQUIDATION` | Account is undergoing liquidation | | `65` | `STATUS_ACTIVE` | Order is active | | `66` | `MODE_BUY` | Buy side | | `76` | `ORDER_TYPE_LIMIT` | Limit order | | `77` | `ORDER_TYPE_MARKET` | Market order | | `80` | `ORDER_TYPE_PEG` | Peg / Algo order | | `81` | `ORDER_TYPE_OTC` | OTC order | | `83` | `MODE_SELL` | Sell side | | `85` | `STATUS_PROCESSING` | Order is processing | | `88` | `STATUS_INACTIVE` | Order is inactive | | `101` | `FUTURES_ORDER_PRICE_OUTSIDE_LIQUIDATION_PRICE` | Futures order price is outside liquidation price | | `110` | `FUTURES_FUNDING` | Futures funding event | | `123` | `AMEND_ORDER` | Order amended successfully | | `124` | `UNFREEZE_SUCCESSFUL` | Unfreeze action succeeded | | `300` | `ERROR_MAX_ORDER_SIZE_EXCEEDED` | Order size exceeds the maximum allowed | | `301` | `ERROR_INVALID_ORDER_SIZE` | Invalid order size | | `302` | `ERROR_INVALID_ORDER_PRICE` | Invalid order price | | `303` | `ERROR_RATE_LIMITS_EXCEEDED` | Rate limit exceeded | | `304` | `ERROR_MAX_OPEN_ORDER_EXCEEDED` | Maximum open orders exceeded | | `1003` | `ORDER_LIQUIDATION` | Order is undergoing liquidation | | `1004` | `ORDER_ADL` | Order is undergoing auto-deleveraging (ADL) | | `30410` | `BLOCK_TRADE_COMPLETE_SUCCESS` | Block trade completed successfully | --- # WebSocket Guide This page covers connection lifecycle, heartbeat, reconnection strategy, and best practices that apply to **all** BTSE WebSocket endpoints (Spot, Futures, OTC). For product-specific topics and subscription details, see the WebSocket API section in each product's sidebar. --- ## Endpoints | Endpoint | Production | Testnet | |---|---|---| | Spot WS | wss://ws.btse.com/ws/spot | wss://testws.btse.io/ws/spot | | Spot OSS | wss://ws.btse.com/ws/oss/spot | wss://testws.btse.io/ws/oss/spot | | Futures WS | wss://ws.btse.com/ws/futures | wss://testws.btse.io/ws/futures | | Futures OSS | wss://ws.btse.com/ws/oss/futures | wss://testws.btse.io/ws/oss/futures | | OTC WS | wss://ws.btse.com/ws/otc | wss://testws.btse.io/ws/otc | | OTC OSS | wss://ws.btse.com/ws/oss/otc | wss://testws.btse.io/ws/oss/otc | > **Main vs OSS:** The OSS (Order Stream Service) endpoint is a dedicated, high-throughput channel for orderbook data only. Use the **main** endpoint for everything else (trades, authentication, notifications, fills). --- ## Connection Lifecycle ``` Connect → (optional) Authenticate → Subscribe → Receive messages ↑ | └──────────── ping every 15s to keep alive ──────────┘ ``` 1. **Connect** — open a WebSocket to the appropriate endpoint 2. **Authenticate** (private topics only) — send `authKeyExpires` with HMAC-SHA384 signature 3. **Subscribe** — send `{"op": "subscribe", "args": ["topic1", "topic2"]}` 4. **Receive** — messages arrive as JSON, except `pong` which is plain text 5. **Keep alive** — send `ping` (plain text) at regular intervals --- ## Heartbeat (Ping / Pong) Send a plain-text `ping` message to keep the connection alive. The server responds with `pong`. | Parameter | Value | |---|---| | **Recommended interval** | Every **15 seconds** | | **Server timeout** | Connections idle for **60 seconds** without a ping are dropped | | **Format** | Plain text `ping` (not JSON) | ``` → ping ← pong ``` If you miss the pong response, your connection may be stale — close and reconnect. --- ## Reconnection Strategy Connections can drop due to network issues, server maintenance, or idle timeouts. Your client should handle disconnects gracefully: ### Recommended approach 1. **Detect disconnect** — listen for WebSocket `close` and `error` events 2. **Wait before reconnecting** — use exponential backoff: - 1st retry: 1 second - 2nd retry: 2 seconds - 3rd retry: 4 seconds - Max: 30 seconds between retries 3. **Re-authenticate** — authentication does not persist across connections 4. **Re-subscribe** — subscriptions do not persist across connections 5. **Resync state** — for orderbook streams, the first message after re-subscribe is always a full `snapshot` ### Orderbook-specific recovery If you detect any of these conditions, **unsubscribe and re-subscribe** to get a fresh snapshot: - **Sequence gap** — `seqNum` != `prevSeqNum + 1` - **Crossed orderbook** — best bid >= best ask - **Stale data** — no update received for an extended period --- ## Connection Limits | Limit | Value | |---|---| | **Max connections per IP** | 50 | | **Max subscriptions per connection** | 100 topics | | **Message buffer** | If the server's outbound buffer to your client fills up, the connection is closed (error code `1007`) | To stay within limits: - Share a single connection for multiple topics when possible - Unsubscribe from topics you no longer need - Use OSS endpoints for orderbook data to keep the main connection lighter --- ## Authentication Private topics (notifications, fills, positions) require authenticating the WebSocket session. Authentication is per-connection and does not expire until the connection closes. **Signature:** `HMAC-SHA384(secret, wsPath + nonce)` Where `wsPath` is the WebSocket path (e.g. `/ws/spot`, `/ws/futures`). ```json { "op": "authKeyExpires", "args": ["", "", ""] } ``` See [Quickstart → Step 4](/quickstart#step-4--subscribe-to-real-time-updates-via-websocket) for complete code examples. --- ## Subscription Format All topics use the same subscribe/unsubscribe format: ```json {"op": "subscribe", "args": ["topic1", "topic2"]} {"op": "unsubscribe", "args": ["topic1"]} ``` The server acknowledges with: ```json {"event": "subscribe", "channel": ["topic1", "topic2"]} ``` --- ## Available Topics ### Spot (wss://ws.btse.com/ws/spot) | Topic | Auth | Description | |---|---|---| | `tradeHistoryApi:` | Public | Real-time trade feed | | `notificationApiV3` | Private | Order status updates | | `fills` | Private | Trade fill notifications | ### Spot OSS (wss://ws.btse.com/ws/oss/spot) | Topic | Auth | Description | |---|---|---| | `snapshotL1:` | Public | Best bid/ask (Level 1) | | `update:_` | Public | Orderbook incremental updates (snapshot + delta) | ### Futures (wss://ws.btse.com/ws/futures) | Topic | Auth | Description | |---|---|---| | `tradeHistoryApiV3:` | Public | Real-time trade feed | | `notificationApiV4` | Private | Order status updates | | `fillsV2` | Private | Trade fill notifications | | `allPositionV4` | Private | All positions snapshot | | `positionsV3` | Private | Position updates | ### Futures OSS (wss://ws.btse.com/ws/oss/futures) | Topic | Auth | Description | |---|---|---| | `snapshotL1:_` | Public | Best bid/ask by grouping | | `update:_` | Public | Orderbook incremental updates | ### OTC (wss://ws.btse.com/ws/otc) | Topic | Auth | Description | |---|---|---| | `quote` | Private | Real-time OTC quote stream | --- ## Error Codes (WebSocket) | Code | Message | Action | |---|---|---| | `1000` | Market pair not supported | Check symbol name | | `1001` | Operation not supported | Check `op` field | | `1002` | Invalid request | Check required fields | | `1005` | Topic does not exist | Check topic name format | | `1007` | Message buffer full | Reduce subscription count or consume faster | | `1008` | Max failed attempts reached | Session closed — reconnect | --- # Order Lifecycle This page describes the states an order passes through from creation to completion, and maps them to the numeric status codes returned in REST responses and WebSocket notifications. --- ## Order States ``` ┌─────────────┐ ┌──────────▸│ INSERTED │──────────────┐ │ │ (status 2) │ │ │ └──────┬──────┘ │ │ │ │ │ ▼ ▼ │ ┌─────────────────┐ ┌──────────────────┐ Place │ │ PARTIALLY │ │ FULLY │ Order │ │ FILLED │ │ TRANSACTED │ │ │ (status 5) │ │ (status 4) │ │ └────────┬───────┘ └──────────────────┘ │ │ │ ▼ │ ┌──────────────────┐ │ │ CANCELLED │ │ │ (status 6) │ │ └──────────────────┘ │ ▼ ┌──────────────┐ ┌──────────────────┐ │ REJECTED │ │ TIMEOUT │ │ (status 15) │ │ (status -1) │ └──────────────┘ └──────────────────┘ ``` --- ## Status Codes | Status | Constant | Description | Terminal? | |--------|----------|-------------|-----------| | `-1` | `TIMEOUT` | Request timed out — the order may or may not have been placed. **Query the order to confirm.** | No | | `2` | `ORDER_INSERTED` | Order accepted and resting on the orderbook | No | | `4` | `ORDER_FULLY_TRANSACTED` | Order completely filled | Yes | | `5` | `ORDER_PARTIALLY_TRANSACTED` | Order partially filled — remaining size still resting | No | | `6` | `ORDER_CANCELLED` | Order cancelled (by user, system, or expiry) | Yes | | `7` | `ORDER_REFUNDED` | Order refunded | Yes | | `8` | `INSUFFICIENT_BALANCE` | Rejected — not enough funds | Yes | | `9` | `TRIGGER_INSERTED` | Trigger/stop order accepted — waiting for trigger condition | No | | `10` | `TRIGGER_ACTIVATED` | Trigger condition met — order converted to limit/market | No | | `15` | `ORDER_REJECTED` | Order rejected (invalid params, risk limits, etc.) | Yes | | `65` | `STATUS_ACTIVE` | Order is active | No | | `85` | `STATUS_PROCESSING` | Order is being processed | No | | `88` | `STATUS_INACTIVE` | Order is inactive | Yes | | `123` | `AMEND_ORDER` | Order amended successfully | No | See [Error Codes](/error-codes#api-status-enum) for the full enum table. --- ## Typical Flows ### Limit order — fully filled ``` Place order → status 2 (INSERTED) → status 4 (FULLY_TRANSACTED) ``` ### Limit order — partial fill then cancel ``` Place order → status 2 (INSERTED) → status 5 (PARTIALLY_TRANSACTED) → Cancel request → status 6 (CANCELLED) ``` ### Market order — immediate fill ``` Place order → status 4 (FULLY_TRANSACTED) ``` Market orders are usually filled immediately. You may not see status 2. ### Stop/trigger order ``` Place order → status 9 (TRIGGER_INSERTED) → Price hits trigger → status 10 (TRIGGER_ACTIVATED) → status 2 (INSERTED) → status 4 or 5 (filled) ``` ### Rejected order ``` Place order → status 15 (REJECTED) or status 8 (INSUFFICIENT_BALANCE) ``` Check the `message` field in the response for the rejection reason. ### Timeout ``` Place order → status -1 (TIMEOUT) ``` The order **may or may not** have been placed. Always query the order by `clOrderID` or `orderID` to confirm the actual state. --- ## Tracking Orders in Real-Time Use the WebSocket `notificationApiV3` topic (Spot) or `notificationApiV4` topic (Futures) to receive status updates as they happen: ```json { "topic": "notificationApiV3", "data": { "symbol": "BTC-USD", "orderID": "990db9b6-...", "status": 5, "filledBaseSize": 0.5, "totalFilledBaseSize": 0.5, "remainingBaseSize": 0.5, "avgFilledPrice": 36000.0 } } ``` **Recommended approach:** 1. Place order via REST — save the `orderID` 2. Listen on `notificationApiV3` for status updates matching your `orderID` 3. When `status` is `4` (fully filled) or `6` (cancelled), the order is terminal See [WebSocket Guide](/websocket-guide) for connection setup and [Quickstart → Step 4](/quickstart#step-4--subscribe-to-real-time-updates-via-websocket) for code examples. --- ## Order Type Codes These numeric type codes appear in `orderType` fields: | Code | Type | |------|------| | `76` | Limit | | `77` | Market | | `80` | Peg / Algo | | `81` | OTC | --- # Client SDKs Auto-generated client libraries for each API product. Download the SDK for your language and get started immediately. ## Python | Product | Download | |---|---| | Markets | [BTSE Markets SDK](pathname:///sdks/python/btse-markets-sdk-python.zip) | | Spot | [BTSE Spot SDK](pathname:///sdks/python/btse-spot-sdk-python.zip) | | Futures | [BTSE Futures SDK](pathname:///sdks/python/btse-futures-sdk-python.zip) | | Wallet | [BTSE Wallet SDK](pathname:///sdks/python/btse-wallet-sdk-python.zip) | | Earn | [BTSE Earn SDK](pathname:///sdks/python/btse-earn-sdk-python.zip) | | OTC | [BTSE OTC SDK](pathname:///sdks/python/btse-otc-sdk-python.zip) | **Install:** ```bash pip install ./ ``` ## JavaScript | Product | Download | |---|---| | Markets | [BTSE Markets SDK](pathname:///sdks/javascript/btse-markets-sdk-javascript.zip) | | Spot | [BTSE Spot SDK](pathname:///sdks/javascript/btse-spot-sdk-javascript.zip) | | Futures | [BTSE Futures SDK](pathname:///sdks/javascript/btse-futures-sdk-javascript.zip) | | Wallet | [BTSE Wallet SDK](pathname:///sdks/javascript/btse-wallet-sdk-javascript.zip) | | Earn | [BTSE Earn SDK](pathname:///sdks/javascript/btse-earn-sdk-javascript.zip) | | OTC | [BTSE OTC SDK](pathname:///sdks/javascript/btse-otc-sdk-javascript.zip) | **Install:** ```bash npm install ./ ``` ## Java | Product | Download | |---|---| | Markets | [BTSE Markets SDK](pathname:///sdks/java/btse-markets-sdk-java.zip) | | Spot | [BTSE Spot SDK](pathname:///sdks/java/btse-spot-sdk-java.zip) | | Futures | [BTSE Futures SDK](pathname:///sdks/java/btse-futures-sdk-java.zip) | | Wallet | [BTSE Wallet SDK](pathname:///sdks/java/btse-wallet-sdk-java.zip) | | Earn | [BTSE Earn SDK](pathname:///sdks/java/btse-earn-sdk-java.zip) | | OTC | [BTSE OTC SDK](pathname:///sdks/java/btse-otc-sdk-java.zip) | **Install:** ```bash mvn install ``` --- These SDKs are auto-generated from the OpenAPI specifications using [OpenAPI Generator](https://openapi-generator.tech/). For manual integration, see the [Authentication](/authentication) page for signing examples in Python, JavaScript, and Go. --- # Markets # Markets API Public market data endpoints unified across spot and futures. All endpoints can be called without authentication; signing requests with a `Read` permission API key returns more accurate, account-aware responses. --- ## Environment | Endpoint | Production | Testnet | |---|---|---| | Markets REST | https://api.btse.com | https://testapi.btse.io | --- ## Rate Limits Public Markets endpoints share a single per-IP bucket. The same limit applies to every endpoint badge in this section. | Bucket | Limit | |--------|-------| | Per IP | 50 requests per 2 seconds | Exceeding the limit returns HTTP `429`. See [Rate Limits](/authentication#rate-limits) for the full mechanism. --- ## Endpoint Index | Endpoint | Description | | --- | --- | | [Exchange Info](../rest/get-exchange-info) | Exchange rule and symbol information | | [Markets Information](../rest/get-markets) | Supported markets summary | | [Order Book](../rest/get-orderbook) | Level 2 order book snapshot | | [Recent Trades](../rest/get-recent-trades) | Recent transaction list | | [Klines (OHLCV)](../rest/get-klines) | Candlestick data | | [24hr Ticker](../rest/get-24-hr-ticker) | 24-hour price change statistics | | [Price Ticker](../rest/get-price-ticker) | Latest price for symbols | | [Indices Ticker](../rest/get-indices-ticker) | Index and mark price | | [Level 1 Ticker](../rest/get-level-1-ticker) | Best bid/ask snapshot | | [Funding Rate History](../rest/get-funding-rate-history) | Futures funding rate history | | [Market Risk Limits](../rest/get-market-risk-limits) | Per-tier risk limits for futures | ## Markets REST API REST base: `https://api.btse.com` ### Exchange Rule and Symbol Information `GET /public-api/market/v1/exchangeInfo` - Auth: public - Rate limit: marketsIp Retrieve exchange information for supported markets and rate limit setting. This API can be publicly accessed (without any security headers). If you access with `Read` permission authentication, the result will be much more accurate by account setting. The `symbol`, `symbols`, and `types` parameters can't be used in combination with each other. You can only use one parameter at a time. | Options | Example | | --- | --- | | No parameter | `/exchangeInfo` | | symbol | `/exchangeInfo?symbol=BTC-USDT` | | symbols | `/exchangeInfo?symbols=["BTC-USDT","ETH-USDT","BTC-PERP-USDT"]` | | types | `/exchangeInfo?types=["FuturesPerpetual","FuturesTimeBased"]` | #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | symbol | String | No | One symbol | | symbols | String | No | Multiple symbols | | types | String | No | Multiple types | ```json // Response { "success": true, "code": 1, "msg": "Success", "time": 1624989977940, "data": { "rateLimits": [ { "type": "RAW_REQUEST", "interval": "SECOND", "intervalNum": 2, "limit": 50 } ], "symbols": [ { "symbol": "BTC-USDT", "type": "Spot", "category": "CRYPTO", "tradeCurrency": "BTC", "baseCurrency": "BTC", "quoteCurrency": "USDT", "active": true, "minOrderPrice": "0.5", "minPriceIncrement": "0.5", "pricePrecision": 1, "minOrderSize": "0.00001", "maxOrderSize": "2000", "minSizeIncrement": "0.00001", "sizePrecision": 5 }, { "symbol": "BTC-PERP-USDT", "type": "FuturesPerpetual", "category": "CRYPTO", "tradeCurrency": "BTC-PERP", "baseCurrency": "BTC", "quoteCurrency": "USDT", "active": true, "minOrderPrice": "0.5", "minPriceIncrement": "0.5", "pricePrecision": 1, "minOrderSize": "1", "maxOrderSize": "500000", "minSizeIncrement": "1", "sizePrecision": 0, "contractSize": "0.001", "availableSettlement": [ "USD", "BTC", "USDT", "USDC", "USDP" ] } ] } } ``` ### Markets Information `GET /public-api/market/v1/markets` - Auth: public - Rate limit: marketsIp Retrieve supported markets. This API can be publicly accessed (without any security headers). If you access with `Read` permission authentication, the result will be much more accurate by account setting. The `symbol`, `symbols`, and `types` parameters can't be used in combination with each other. | Options | Example | | --- | --- | | No parameter | `/markets` | | symbol | `/markets?symbol=BTC-USDT` | | symbols | `/markets?symbols=["BTC-USDT","ETH-USDT","BTC-PERP-USDT"]` | | types | `/markets?types=["FuturesPerpetual","FuturesTimeBased"]` | #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | symbol | String | No | Symbol | | symbols | String | No | Multiple symbols | | types | String | No | Multiple types | ```json // Response { "success": true, "code": 1, "msg": "Success", "time": 1624989977940, "data": { "symbols": [ { "symbol": "BTC-USDT", "type": "Spot", "category": "CRYPTO", "tradeCurrency": "BTC", "baseCurrency": "BTC", "quoteCurrency": "USDT", "active": true, "minOrderPrice": "0.5", "minPriceIncrement": "0.5", "pricePrecision": 1, "minOrderSize": "0.00001", "maxOrderSize": "2000", "minSizeIncrement": "0.00001", "sizePrecision": 5 } ] } } ``` ### Order Book `GET /public-api/market/v1/orderbook` - Auth: public - Rate limit: marketsIp Retrieve the level 2 order book for a symbol. This API can be publicly accessed (without any security headers). If you access with `Read` permission authentication, the result will be much more accurate by account setting. #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | symbol | String | Yes | Symbol | | depth | Integer | No | The depth of order book | ```json // Response { "success": true, "code": 1, "msg": "Success", "time": 1624989977940, "data": { "timestamp": 1624989977940, "bids": [ [ "20334.2", "7.67500" ] ], "asks": [ [ "20339.0", "0.60200" ] ] } } ``` ### Recent Trades List `GET /public-api/market/v1/trades` - Auth: public - Rate limit: marketsIp Retrieve recent transaction data for a symbol. Only data from the last 3 days is supported. If the count of transactions for the specified symbol in the last 3 days is less than the requested `limit`, only the available count will be returned. #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | symbol | String | Yes | Symbol | | limit | Integer | No | The count of trade list | ```json // Response { "success": true, "code": 1, "msg": "Success", "time": 1624989977940, "data": [ { "id": 85997837, "timestamp": 1624990000940, "price": "20334", "size": "0.0021", "quoteSize": "42.7014", "side": "BUY" }, { "id": 85997835, "timestamp": 1624989977940, "price": "20335", "size": "0.0002", "quoteSize": "4.067", "side": "SELL" } ] } ``` ### Klines (OHLCV) `GET /public-api/market/v1/klines` - Auth: public - Rate limit: marketsIp Retrieve kline (OHLCV) data for a symbol. For the `start` and `end` parameter combinations: | start | end | Behavior | | --- | --- | --- | | not set | not set | Return recent `limit` data points (count backward). | | set | not set | Return `limit` data points from `start` (count forward). | | not set | set | Return `limit` data points from `end` (count backward). | | set | set | If `start`/`end` and resolution would yield more than `limit` data points, the request is rejected. Otherwise return up to `limit` points from `end` to `start` (count backward). | #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | symbol | String | Yes | Symbol | | resolution | String | Yes | Candle size in minutes: `1` (1m), `5` (5m), `15` (15m), `30` (30m), `60` (1h), `240` (4h), `360` (6h), `1440` (1d), `10080` (1w), `43200` (1mo). | | start | Long | No | Start timestamp (seconds) | | end | Long | No | End timestamp (seconds) | | limit | Integer | No | The count of klines | ```json // Response { "success": true, "code": 1, "msg": "Success", "time": 1624989977940, "data": [ [ "1667191500", "20554.9", "20581.2", "20542.2", "20565.9", "2824854.568855" ], [ "1667192400", "20565.4", "20594.5", "20466.6", "20585.9", "3283055.501333" ] ] } ``` ### 24hr Ticker Price Change `GET /public-api/market/v1/ticker/24hr` - Auth: public - Rate limit: marketsIp Retrieve 24hr market statistics. Time is measured in hours; price changes are calculated over a window of approximately 24 to 25 hours. Data refreshes every minute. The `symbol`, `symbols`, and `types` parameters are mutually exclusive. The `isFull` parameter controls whether the full or simple response shape is returned. | Options | Example | | --- | --- | | No parameter | `/ticker/24hr` | | symbol | `/ticker/24hr?symbol=BTC-USDT` | | symbols | `/ticker/24hr?symbols=["BTC-USDT","ETH-USDT","BTC-PERP-USDT"]` | | types | `/ticker/24hr?types=["Spot"]` | | isFull | `/ticker/24hr?isFull=false` | #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | symbol | String | No | Symbol | | symbols | String | No | Multiple symbols | | types | String | No | Multiple types | | isFull | Boolean | No | When `true`, return full response (`24HrTickerPriceChangeFullResponse`). When `false`, return simple response (`24HrTickerPriceChangeResponse`). | ```json // Response { "success": true, "code": 1, "msg": "Success", "time": 1624989977940, "data": [ { "symbol": "BTC-USDT", "lastPrice": "20875.31000000", "openPrice": "21323.78000000", "highPrice": "21346.44000000", "lowPrice": "20838.59000000", "amount": "4.12750830", "volume": "99151.67492000", "openTime": 1667696772705, "closeTime": 1667783172705, "priceChange": "-448.47000000", "priceChangePercent": "-2.103", "prevClosePrice": "21324.77000000", "bidPrice": "20875.00000000", "bidQty": "0.03478000", "askPrice": "20875.77000000", "askQty": "0.01129000" } ] } ``` ### Price Ticker `GET /public-api/market/v1/ticker/price` - Auth: public - Rate limit: marketsIp Retrieve the latest price for one or more symbols. The `symbol`, `symbols`, and `types` parameters are mutually exclusive. | Options | Example | | --- | --- | | No parameter | `/ticker/price` | | symbol | `/ticker/price?symbol=BTC-USDT` | | symbols | `/ticker/price?symbols=["BTC-USDT","ETH-USDT","BTC-PERP-USDT"]` | | types | `/ticker/price?types=["Spot"]` | #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | symbol | String | No | Symbol | | symbols | String | No | Multiple symbols | | types | String | No | Multiple types | ```json // Response { "success": true, "code": 1, "msg": "Success", "time": 1624989977940, "data": [ { "symbol": "BTC-USDT", "price": "20875.31000000", "time": 1624989976195 }, { "symbol": "ETH-BTC", "price": "0.07530600", "time": 1624989975876 } ] } ``` ### Ticker Indices `GET /public-api/market/v1/ticker/indices` - Auth: public - Rate limit: marketsIp Retrieve the index price and mark price for one or more symbols. Mark price is only returned for futures symbols. | Options | Example | | --- | --- | | No parameter | `/ticker/indices` | | symbol | `/ticker/indices?symbol=BTC-PERP-USDT` | | symbols | `/ticker/indices?symbols=["BTC-PERP-USDT","ETH-PERP-USDT"]` | | types | `/ticker/indices?types=["Spot","FuturesTimeBased"]` | #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | symbol | String | No | Symbol | | symbols | String | No | Multiple symbols | | types | String | No | Multiple types | ```json // Response { "success": true, "code": 1, "msg": "Success", "time": 1624989977940, "data": [ { "symbol": "BTC-USD", "indexPrice": "100000.7100000" }, { "symbol": "BTC-PERP-USDT", "indexPrice": "29647.7100000", "markPrice": "29649.9000000" } ] } ``` ### Ticker Order Book Level 1 `GET /public-api/market/v1/ticker/l1` - Auth: public - Rate limit: marketsIp Retrieve the level 1 order book (best bid/ask) for one or more symbols. The `symbol` and `symbols` parameters are mutually exclusive. | Options | Example | | --- | --- | | No parameter | `/ticker/l1` | | symbol | `/ticker/l1?symbol=BTC-USDT` | | symbols | `/ticker/l1?symbols=["BTC-USDT","BTC-PERP-USDT"]` | #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | symbol | String | No | Symbol | | symbols | String | No | Multiple symbols | ```json // Response { "success": true, "code": 1, "msg": "Success", "time": 1624989977940, "data": [ { "symbol": "BTC-USDT", "bidPrice": "20875.00000000", "bidQty": "0.03478000", "askPrice": "20875.77000000", "askQty": "0.01129000", "time": 1673325783227 } ] } ``` ### Funding Rate History `GET /public-api/market/v1/recentFundingHistory` - Auth: public - Rate limit: marketsIp Retrieve funding rate history for a futures symbol over a specified period. Historical rates are sorted in ascending order by `timestamp`. #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | symbol | String | Yes | Futures symbol | | period | String | Yes | Funding rate history period: `7D` (7 days) · `2W` (2 weeks) · `1M` (1 month) | ```json // Response { "success": true, "code": 1, "msg": "Success", "time": 1624989977940, "data": [ { "timestamp": 1715500800000, "rate": "0.0000153752" }, { "timestamp": 1715529600000, "rate": "0.0000130831" } ] } ``` ### Market Risk Limits `GET /public-api/market/v1/riskLimits` - Auth: public - Rate limit: marketsIp Retrieve symbol risk limit information for each tier of the risk limit for futures markets. #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | symbol | String | No | Futures symbol. Omit to return all symbols | ```json // Response { "success": true, "code": 1, "msg": "Success", "time": 1624989977940, "data": { "symbol": "BTC-PERP-USDT", "riskLimits": [ { "level": 1, "value": 3000000 }, { "level": 2, "value": 6000000 }, { "level": 3, "value": 12000000 } ] } } ``` --- # Spot # Spot API > **New here? Set up authentication first.:** All private endpoints require a signed request. Read the [Authentication Guide](/authentication) before making your first call. Public endpoints (market data, orderbook) need no credentials. --- ## Environment | Endpoint | Production | Testnet | |---|---|---| | Spot REST | https://api.btse.com/spot | https://testapi.btse.io/spot | | Spot WS | wss://ws.btse.com/ws/spot | wss://testws.btse.io/ws/spot | > The OSS (Order Stream Service) endpoint is used exclusively for [Orderbook Incremental Updates](../websocket/orderbook-incremental-updates). > > Public market data (orderbook, ticker, OHLCV, etc.) is now served by the [Markets API](/markets/overview). --- ## Rate Limits | Category | Per API | Per User | | --- | --- | --- | | **Query** | 15 req/s | 30 req/s | | **Orders** | 75 req/s | 75 req/s | Exceeding limits triggers a tiered block: **1 second → 5 minutes → 15 minutes**. A `Retry-After` header is included in all `429` responses. See [Rate Limits](/authentication#rate-limits) for the full mechanism. --- ## Discovering Tradeable Pairs Call [Markets Information](/markets/rest/get-markets) (no auth required) to get all available markets: ```bash curl https://api.btse.com/public-api/market/v1/markets ``` Each entry includes the symbol name and trading constraints: | Field | Use | |---|---| | `symbol` | The market identifier to use in all API calls (e.g. `BTC-USDT`) | | `active` | `true` if the market is currently open for trading | | `baseCurrency` / `quoteCurrency` | The two currencies in the pair | | `minOrderSize` / `maxOrderSize` | Size limits for orders | | `minPriceIncrement` | Tick size — your price must be a multiple of this | | `minSizeIncrement` | Lot size — your quantity must be a multiple of this | ### Symbol naming conventions | Product | Format | Example | |---|---|---| | Spot | `BASE-QUOTE` | `BTC-USD`, `ETH-USDT` | | Futures (v2.3) | `BASE-PERP` | `BTC-PERP`, `ETH-PERP` | | Futures (legacy) | Concatenated | `BTCPFC` (use `ApplyNewSymbolName: Y` in FIX to get new names) | For crypto assets with very small prices, market data uses a K/M/B prefix (e.g. `K_PEIPEI`), but wallet APIs always use the original name without prefix (matching the `base` field). --- ## API Status Codes See the shared [Error Codes](/error-codes) reference for HTTP status codes and internal API enum values. --- ## API Enum Order status codes, order type codes, and other numeric enums are documented in [Error Codes → API Status Enum](/error-codes#api-status-enum). ## Spot REST API REST base: `https://api.btse.com/spot` ### Create New Order `POST /api/v3.3/order` - Auth: trading - Rate limit: orders Creates a new order. Index Orders only support USD quotes. > **Tip:** Use `clOrderID` to track orders with your own identifier. This ID is returned in all order-related responses and WebSocket notifications. Supports order types: **LIMIT**, **MARKET**, **OCO**, **PEG** (algo). For trigger orders, set `txType` to `STOP` or `TRIGGER` and provide `triggerPrice`. ### Price by Order Type {#price-by-order-type} | Order Type | Side | `price` meaning | | --- | --- | --- | | LIMIT | BUY | Max price (quote currency) willing to pay per unit | | LIMIT | SELL | Min price (quote currency) willing to accept per unit | | MARKET | BUY | Max total amount (quote currency) willing to spend | | MARKET | SELL | Not applicable | | TRIGGER LIMIT | BUY | Max price willing to pay | | TRIGGER LIMIT | SELL | Min price willing to accept | | TRIGGER MARKET | BUY | Max total amount willing to spend | | TRAILING STOP MARKET | BUY | Max total amount willing to spend | #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | symbol | String | Yes | Market symbol | | price | Double | No | Required for LIMIT orders. For MARKET BUY, this is the max total amount (quote currency) willing to spend. Not applicable for MARKET SELL | | size | Double | Yes | Order size | | side | String | Yes | Order side — `BUY` · `SELL` | | time_in_force | String | No | Time in force: `GTC` (Good Till Cancelled) · `IOC` (Immediate Or Cancel) · `FOK` (Fill Or Kill) · `HALFMIN` (30 seconds) · `FIVEMIN` (5 minutes) · `HOUR` (1 hour) · `TWELVEHOUR` (12 hours) · `DAY` (1 day) · `WEEK` (1 week) · `MONTH` (1 month) | | type | String | Yes | Order type: `LIMIT` · `MARKET` · `OCO` (One-Cancels-the-Other) · `PEG` (algo order that tracks market price) | | txType | String | No | Transaction type: `LIMIT` (default) · `STOP` (requires `triggerPrice`) · `TRIGGER` (requires `triggerPrice`) | | stopPrice | Double | No | Mandatory for OCO orders | | triggerPrice | Double | No | Mandatory for Stop, Trigger, and OCO orders | | trailValue | Double | No | Trail value. TP/SL not supported when set | | postOnly | Boolean | No | Post-only order — charged maker fees | | clOrderID | String | No | Custom order ID | | stealth | Double | No | PEG orders: percentage visible on orderbook | | deviation | Double | No | PEG orders: deviation from index price (-10 to 10) | | triggerPriceType | String | No | Trigger price reference — `INDEX_PRICE` · `LAST_PRICE` | #### Response Content | Name | Type | Description | | --- | --- | --- | | symbol | String | Market symbol | | orderID | String | Internal order ID | | clOrderID | String | Custom order ID | | side | String | BUY or SELL | | price | Double | Order price | | orderType | Integer | 76: Limit · 77: Market · 80: Peg/Algo | | status | Integer | Order status code: `2` (ORDER_INSERTED) · `4` (FULLY_MATCHED) · `5` (PARTIAL_FILL) · `6` (CANCELLED) · `9` (TRIGGER_INSERTED) · `10` (TRIGGER_ACTIVATED) · `15` (REJECTED) | | averageFillPrice | Double | Average fill price | | originalOrderBaseSize | Double | Original order quantity expressed in base currency | | originalOrderQuoteSize | Double | Original order quantity expressed in quote currency | | currentOrderBaseSize | Double | Latest order quantity in base currency (after amendments / partial fills) | | currentOrderQuoteSize | Double | Latest order quantity in quote currency | | remainingOrderBaseSize | Double | Unfilled quantity in base currency | | remainingOrderQuoteSize | Double | Unfilled quantity in quote currency | | filledBaseSize | Double | Most recent fill quantity in base currency | | totalFilledBaseSize | Double | Cumulative filled quantity in base currency | | orderCurrency | String | Currency the order size is denominated in: `base` or `quote` | | size | Double | **Deprecated in v3.3.** Use `originalOrderBaseSize` / `originalOrderQuoteSize` | | fillSize | Double | **Deprecated in v3.3.** Use `filledBaseSize` / `totalFilledBaseSize` | | remainingSize | Double | **Deprecated in v3.3.** Use `remainingOrderBaseSize` / `remainingOrderQuoteSize` | | originalSize | Double | **Deprecated in v3.3.** Use `originalOrderBaseSize` / `originalOrderQuoteSize` | | triggerPrice | Double | Trigger price (`0` if not a trigger order) | | stopPrice | Double | Stop price | | time_in_force | String | Time in force: `GTC` · `IOC` · `FOK` · `HALFMIN` · `FIVEMIN` · `HOUR` · `TWELVEHOUR` · `DAY` · `WEEK` · `MONTH` | | timestamp | Long | Order timestamp | | trigger | Boolean | Whether the order is a trigger order | | postOnly | Boolean | Whether post-only | | stealth | Double | Stealth value | | deviation | Double | Deviation value | ```json // Request (MARKET order) { "symbol": "BTC-USD", "size": 1, "side": "BUY", "type": "MARKET" } ``` ```json // Request (LIMIT order) { "symbol": "BTC-USD", "size": 1, "price": 34000, "side": "BUY", "type": "LIMIT" } ``` ```json // Request (OCO order) { "symbol": "BTC-USD", "size": 1, "price": 24000, "side": "BUY", "type": "OCO", "txType": "LIMIT", "stopPrice": 40010, "triggerPrice": 40000 } ``` ```json // Response [ { "status": 2, "symbol": "BTC-USD", "orderType": 76, "price": 34000, "side": "BUY", "orderID": "990db9b6-2ed4-4c68-b46e-827c88cc3884", "timestamp": 1660208800123, "triggerPrice": 0, "stopPrice": null, "trigger": false, "averageFillPrice": 0, "clOrderID": null, "postOnly": false, "time_in_force": "GTC", "orderCurrency": "base", "originalOrderBaseSize": 1, "originalOrderQuoteSize": 34000, "currentOrderBaseSize": 1, "currentOrderQuoteSize": 34000, "remainingOrderBaseSize": 1, "remainingOrderQuoteSize": 34000, "filledBaseSize": 0, "totalFilledBaseSize": 0 } ] ``` ### Query Order `GET /api/v3.3/order` - Auth: trading - Rate limit: query Query order detail by `orderID` or `clOrderID`. Only returns open orders and orders cancelled within the last 30 minutes. #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | orderID | String | No | Mandatory when `clOrderID` is not provided. If both are provided, `clOrderID` is ignored | | clOrderID | String | No | Mandatory when `orderID` is not provided | #### Response Content | Name | Type | Description | | --- | --- | --- | | orderID | String | Order ID | | symbol | String | Market symbol | | quote | String | Quote currency | | side | String | BUY or SELL | | orderType | Integer | Order type code | | price | Double | Order price | | orderValue | Double | Total order value | | averageFillPrice | Double | Average fill price | | status | Integer | Order status code: `2` (ORDER_INSERTED) · `4` (FULLY_MATCHED) · `5` (PARTIAL_FILL) · `6` (CANCELLED) · `9` (TRIGGER_INSERTED) · `10` (TRIGGER_ACTIVATED) · `15` (REJECTED) | | originalOrderBaseSize | Double | Original order quantity expressed in base currency | | originalOrderQuoteSize | Double | Original order quantity expressed in quote currency | | currentOrderBaseSize | Double | Latest order quantity in base currency | | currentOrderQuoteSize | Double | Latest order quantity in quote currency | | remainingOrderBaseSize | Double | Unfilled quantity in base currency | | remainingOrderQuoteSize | Double | Unfilled quantity in quote currency | | totalFilledBaseSize | Double | Cumulative filled quantity in base currency | | orderCurrency | String | Currency the order size is denominated in: `base` or `quote` | | size | Double | **Deprecated in v3.3.** Use `originalOrderBaseSize` / `originalOrderQuoteSize` | | filledSize | Double | **Deprecated in v3.3.** Use `totalFilledBaseSize` | | remainingSize | Double | **Deprecated in v3.3.** Use `remainingOrderBaseSize` / `remainingOrderQuoteSize` | | timeInForce | String | Time in force: `GTC` · `IOC` · `FOK` · `HALFMIN` · `FIVEMIN` · `HOUR` · `TWELVEHOUR` · `DAY` · `WEEK` · `MONTH` | | timestamp | Long | Order timestamp | | clOrderID | String | Custom order ID | | triggerOrder | Boolean | Whether this is a trigger order | | triggerPrice | Double | Trigger price | | triggered | Boolean | Whether the order has been triggered | | trailValue | Double | Trail value | ```json // Response { "orderID": "", "symbol": "BTC-USDT", "quote": "USDT", "status": 6, "orderType": 76, "price": 30000, "side": "SELL", "averageFillPrice": 0, "clOrderID": "", "timeInForce": "GTC", "timestamp": 1697766317422, "triggerOrder": false, "triggerPrice": 0, "triggered": false, "orderCurrency": "base", "originalOrderBaseSize": 0.00001, "originalOrderQuoteSize": 0.3, "currentOrderBaseSize": 0.00001, "currentOrderQuoteSize": 0.3, "remainingOrderBaseSize": 0, "remainingOrderQuoteSize": 0, "totalFilledBaseSize": 0 } ``` ### Amend Order `PUT /api/v3.3/order` - Auth: trading - Rate limit: orders Amend the price, size, or trigger price of a pending order. Does **not** apply to algo orders. For trigger orders, the trigger price cannot be amended after the order has been triggered. #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | symbol | String | Yes | Market symbol | | orderID | String | No | Internal order ID. Required if `clOrderID` is not provided. Takes precedence over `clOrderID` if both are provided | | clOrderID | String | No | Custom order ID. Required if `orderID` is not provided | | type | String | Yes | Amendment type: `PRICE` (amend price, use `value`) · `SIZE` (amend size, use `value`) · `TRIGGERPRICE` (amend trigger price, use `value`) · `ALL` (amend multiple fields, use `orderPrice`/`orderSize`/`triggerPrice`) | | value | Double | No | New value. Required for `PRICE`, `SIZE`, or `TRIGGERPRICE` amendment types | | orderPrice | Double | No | New order price. Used when `type` is `ALL` | | orderSize | Double | No | New order size. Used when `type` is `ALL` | | triggerPrice | Double | No | New trigger price (trigger orders only). Used when `type` is `ALL` | #### Response Content | Name | Type | Description | | --- | --- | --- | | symbol | String | Market symbol | | orderID | String | Internal order ID | | clOrderID | String | Custom order ID | | side | String | BUY or SELL | | price | Double | Order price | | orderType | Integer | 76: Limit · 77: Market · 80: Peg/Algo | | status | Integer | Order status code: `2` (ORDER_INSERTED) · `4` (FULLY_MATCHED) · `5` (PARTIAL_FILL) · `6` (CANCELLED) · `9` (TRIGGER_INSERTED) · `10` (TRIGGER_ACTIVATED) · `15` (REJECTED) | | averageFillPrice | Double | Average fill price | | originalOrderBaseSize | Double | Original order quantity expressed in base currency | | originalOrderQuoteSize | Double | Original order quantity expressed in quote currency | | currentOrderBaseSize | Double | Latest order quantity in base currency (after amendments / partial fills) | | currentOrderQuoteSize | Double | Latest order quantity in quote currency | | remainingOrderBaseSize | Double | Unfilled quantity in base currency | | remainingOrderQuoteSize | Double | Unfilled quantity in quote currency | | filledBaseSize | Double | Most recent fill quantity in base currency | | totalFilledBaseSize | Double | Cumulative filled quantity in base currency | | orderCurrency | String | Currency the order size is denominated in: `base` or `quote` | | size | Double | **Deprecated in v3.3.** Use `originalOrderBaseSize` / `originalOrderQuoteSize` | | fillSize | Double | **Deprecated in v3.3.** Use `filledBaseSize` / `totalFilledBaseSize` | | remainingSize | Double | **Deprecated in v3.3.** Use `remainingOrderBaseSize` / `remainingOrderQuoteSize` | | originalSize | Double | **Deprecated in v3.3.** Use `originalOrderBaseSize` / `originalOrderQuoteSize` | | triggerPrice | Double | Trigger price (`0` if not a trigger order) | | stopPrice | Double | Stop price | | time_in_force | String | Time in force: `GTC` · `IOC` · `FOK` · `HALFMIN` · `FIVEMIN` · `HOUR` · `TWELVEHOUR` · `DAY` · `WEEK` · `MONTH` | | timestamp | Long | Order timestamp | | trigger | Boolean | Whether the order is a trigger order | | postOnly | Boolean | Whether post-only | | stealth | Double | Stealth value | | deviation | Double | Deviation value | ```json // Request (Amend price) { "symbol": "BTC-USD", "orderID": "25248336-66d8-41ff-99fd-83489c4e6029", "type": "PRICE", "value": 35000 } ``` ```json // Request (Amend size) { "symbol": "BTC-USD", "orderID": "689bf733-4879-4e32-8d1f-cb81f63d24d4", "type": "SIZE", "value": 1.05 } ``` ```json // Request (Amend multiple fields) { "symbol": "BTC-USD", "orderID": "cb2785b0-558e-4b30-bf1f-8a8c56174d0c", "type": "ALL", "orderPrice": 40010, "orderSize": 1.05, "triggerPrice": 40000 } ``` ### Cancel Order `DELETE /api/v3.3/order` - Auth: trading - Rate limit: orders Cancel a pending order. If neither `orderID` nor `clOrderID` is provided, **all orders** in the specified market are cancelled. When cancelling by `clOrderID`, all orders with the same ID are cancelled. #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | symbol | String | Yes | Market symbol | | orderID | String | No | Order ID to cancel | | clOrderID | String | No | Custom order ID to cancel | #### Response Content | Name | Type | Description | | --- | --- | --- | | symbol | String | Market symbol | | orderID | String | Internal order ID | | clOrderID | String | Custom order ID | | side | String | BUY or SELL | | price | Double | Order price | | orderType | Integer | 76: Limit · 77: Market · 80: Peg/Algo | | status | Integer | Order status code: `2` (ORDER_INSERTED) · `4` (FULLY_MATCHED) · `5` (PARTIAL_FILL) · `6` (CANCELLED) · `9` (TRIGGER_INSERTED) · `10` (TRIGGER_ACTIVATED) · `15` (REJECTED) | | averageFillPrice | Double | Average fill price | | originalOrderBaseSize | Double | Original order quantity expressed in base currency | | originalOrderQuoteSize | Double | Original order quantity expressed in quote currency | | currentOrderBaseSize | Double | Latest order quantity in base currency (after amendments / partial fills) | | currentOrderQuoteSize | Double | Latest order quantity in quote currency | | remainingOrderBaseSize | Double | Unfilled quantity in base currency | | remainingOrderQuoteSize | Double | Unfilled quantity in quote currency | | filledBaseSize | Double | Most recent fill quantity in base currency | | totalFilledBaseSize | Double | Cumulative filled quantity in base currency | | orderCurrency | String | Currency the order size is denominated in: `base` or `quote` | | size | Double | **Deprecated in v3.3.** Use `originalOrderBaseSize` / `originalOrderQuoteSize` | | fillSize | Double | **Deprecated in v3.3.** Use `filledBaseSize` / `totalFilledBaseSize` | | remainingSize | Double | **Deprecated in v3.3.** Use `remainingOrderBaseSize` / `remainingOrderQuoteSize` | | originalSize | Double | **Deprecated in v3.3.** Use `originalOrderBaseSize` / `originalOrderQuoteSize` | | triggerPrice | Double | Trigger price (`0` if not a trigger order) | | stopPrice | Double | Stop price | | time_in_force | String | Time in force: `GTC` · `IOC` · `FOK` · `HALFMIN` · `FIVEMIN` · `HOUR` · `TWELVEHOUR` · `DAY` · `WEEK` · `MONTH` | | timestamp | Long | Order timestamp | | trigger | Boolean | Whether the order is a trigger order | | postOnly | Boolean | Whether post-only | | stealth | Double | Stealth value | | deviation | Double | Deviation value | ### Dead Man's Switch (Cancel All After) `POST /api/v3.3/order/cancelAllAfter` - Auth: trading - Rate limit: orders A dead-man's switch that cancels all open orders if the server does not receive a renewal request within the specified timeout. Used to protect against connection loss. Send another `cancelAllAfter` request before the timeout expires to extend it. #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | timeout | Integer | Yes | TTL in milliseconds. When reached, all open orders are cancelled | ```json // Request { "timeout": 60000 } ``` ### Query Open Orders `GET /api/v3.3/user/open_orders` - Auth: trading - Rate limit: query Retrieve open orders that have not yet been matched. #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | symbol | String | No | Market symbol. If omitted, returns open orders for all markets | #### Response Content | Name | Type | Description | | --- | --- | --- | | symbol | String | Market symbol | | orderID | String | Internal order ID | | clOrderID | String | Custom order ID | | side | String | BUY or SELL | | price | Double | Order price | | orderType | Integer | 76: Limit · 77: Market · 80: Peg/Algo | | status | Integer | Order status code: `2` (ORDER_INSERTED) · `4` (FULLY_MATCHED) · `5` (PARTIAL_FILL) · `6` (CANCELLED) · `9` (TRIGGER_INSERTED) · `10` (TRIGGER_ACTIVATED) · `15` (REJECTED) | | averageFillPrice | Double | Average fill price | | originalOrderBaseSize | Double | Original order quantity expressed in base currency | | originalOrderQuoteSize | Double | Original order quantity expressed in quote currency | | currentOrderBaseSize | Double | Latest order quantity in base currency (after amendments / partial fills) | | currentOrderQuoteSize | Double | Latest order quantity in quote currency | | remainingOrderBaseSize | Double | Unfilled quantity in base currency | | remainingOrderQuoteSize | Double | Unfilled quantity in quote currency | | filledBaseSize | Double | Most recent fill quantity in base currency | | totalFilledBaseSize | Double | Cumulative filled quantity in base currency | | orderCurrency | String | Currency the order size is denominated in: `base` or `quote` | | size | Double | **Deprecated in v3.3.** Use `originalOrderBaseSize` / `originalOrderQuoteSize` | | fillSize | Double | **Deprecated in v3.3.** Use `filledBaseSize` / `totalFilledBaseSize` | | remainingSize | Double | **Deprecated in v3.3.** Use `remainingOrderBaseSize` / `remainingOrderQuoteSize` | | originalSize | Double | **Deprecated in v3.3.** Use `originalOrderBaseSize` / `originalOrderQuoteSize` | | triggerPrice | Double | Trigger price (`0` if not a trigger order) | | stopPrice | Double | Stop price | | time_in_force | String | Time in force: `GTC` · `IOC` · `FOK` · `HALFMIN` · `FIVEMIN` · `HOUR` · `TWELVEHOUR` · `DAY` · `WEEK` · `MONTH` | | timestamp | Long | Order timestamp | | trigger | Boolean | Whether the order is a trigger order | | postOnly | Boolean | Whether post-only | | stealth | Double | Stealth value | | deviation | Double | Deviation value | ```json // Response [] ``` ### Query User Trades Fills `GET /api/v3.3/user/trade_history` - Auth: read - Rate limit: query Returns the authenticated user's trade history. **Time range constraints:** - `startTime` + `endTime`: max 7 days - `startTime` only: 7 days after start - `endTime` only: 7 days before end - Neither: 7 days before current time #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | symbol | String | No | Market symbol. Optional — if omitted, returns trades across all symbols. | | startTime | Long | No | Starting time in milliseconds | | endTime | Long | No | Ending time in milliseconds | | count | Integer | No | Number of records to return | | clOrderID | String | No | Filter by custom order ID | | orderID | String | No | Filter by order ID | | isMatchSymbol | Boolean | No | If `true`, exact match on `symbol` only | #### Response Content | Name | Type | Description | | --- | --- | --- | | symbol | String | Market symbol | | side | String | BUY or SELL | | price | Double | Fill price | | size | Double | Fill size | | serialId | Long | Serial ID | | timestamp | Long | Fill timestamp | | orderId | String | Order ID | | clOrderId | String | Custom order ID | | feeAmount | Double | Fee charged | | feeCurrency | String | Fee currency | ```json // Response [] ``` ### Query Account Fees `GET /api/v3.3/user/fees` - Auth: read - Rate limit: query Returns maker/taker fees for the account. #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | symbol | String | No | Market symbol. If omitted, returns fees for all markets | #### Response Content | Name | Type | Description | | --- | --- | --- | | symbol | String | Market symbol | | makerFee | Double | Maker fee rate | | takerFee | Double | Taker fee rate | ```json // Response [ { "symbol": "BTC-USD", "makerFee": 0.002, "takerFee": 0.002 } ] ``` ## Spot WebSocket API WebSocket: `wss://ws.btse.com/ws/spot` ### Ping / Pong Endpoint: `wss://ws.btse.com/ws/spot` · Testnet: `wss://testws.btse.io/ws/spot` Send a plain text `ping` to keep the connection alive. The server responds with `pong`. ### Subscription Endpoint: `wss://ws.btse.com/ws/spot` · Testnet: `wss://testws.btse.io/ws/spot` Subscribe or unsubscribe from any topic. #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | op | String | Yes | Operation — `subscribe` · `unsubscribe` | | args | String[] | Yes | Array of topic strings | ```json // Request { "op": "subscribe", "args": [ "tradeHistoryApi:BTC-USD" ] } ``` #### Response Content | Name | Type | Required | Description | | --- | --- | --- | --- | | event | String | No | Echo of the event type | | channel | String[] | No | Successfully subscribed topics | ```json // Response { "event": "subscribe", "channel": [ "tradeHistoryApi:BTC-USD" ] } ``` ### WebSocket Authentication Endpoint: `wss://ws.btse.com/ws/spot` · Testnet: `wss://testws.btse.io/ws/spot` Authenticate the session to access private topics (Notifications, User Trade Fills). Signature: `HMAC-SHA384(secret, "/ws/spot" + nonce)` #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | op | String | Yes | Operation — fixed value `authKeyExpires` | | args | String[] | Yes | [API_KEY, NONCE, HMAC-SHA384_SIGNATURE] | ```json // Request { "op": "authKeyExpires", "args": [ "", "", "" ] } ``` ### OSS L1 Snapshot Endpoint: `wss://ws.btse.com/ws/oss/spot` · Testnet: `wss://testws.btse.io/ws/oss/spot` Subscribe to the best bid / best ask (Level 1) for a market. **Topic format:** `snapshotL1:` (e.g. `snapshotL1:BTC-USD`) #### Response Content | Name | Type | Required | Description | | --- | --- | --- | --- | | topic | String | Yes | WebSocket topic | | data | Object | Yes | Data object | **Data Object:** | Name | Type | Required | Description | | --- | --- | --- | --- | | bids | String[][] | Yes | Best bid [price, size] | | asks | String[][] | Yes | Best ask [price, size] | | symbol | String | Yes | Market symbol | | type | String | Yes | | | timestamp | Long | Yes | Orderbook timestamp (ms) | ```json // Response { "topic": "snapshotL1:BTC-USD", "data": { "bids": [ [ "28016.7", "1.48063" ] ], "asks": [ [ "28033.6", "1.34133" ] ], "type": "snapshotL1", "symbol": "BTC-USD", "timestamp": 1680750154232 } } ``` ### Orderbook Incremental Updates Endpoint: `wss://ws.btse.com/ws/oss/spot` · Testnet: `wss://testws.btse.io/ws/oss/spot` Subscribe to full orderbook updates with delta streaming. **Topic format:** `update:_` (e.g. `update:BTC-USD_0`) On successful subscribe, the server pushes one `snapshot` message (up to 50 levels) followed by `delta` updates. **The snapshot is delivered without grouping (raw book); only `delta` updates respect the subscribed `` value.** If the market is idle, the snapshot still arrives but no deltas follow until book activity resumes. **Applying deltas:** - `[price, size]` tuple: if `size` is `"0"`, remove the price level; otherwise update it - `seqNum` must equal `prevSeqNum + 1`. On gap, unsubscribe and re-subscribe - On crossed orderbook (best bid >= best ask), unsubscribe and re-subscribe #### Response Content | Name | Type | Required | Description | | --- | --- | --- | --- | | topic | String | Yes | WebSocket topic | | data | Object | Yes | Data object | **Data Object:** | Name | Type | Required | Description | | --- | --- | --- | --- | | bids | String[][] | Yes | Bid updates [price, size] | | asks | String[][] | Yes | Ask updates [price, size] | | seqNum | Integer | Yes | Current sequence number | | prevSeqNum | Integer | Yes | Previous sequence number | | type | String | Yes | snapshot (first) or delta (updates) | | timestamp | Long | Yes | Orderbook timestamp | | symbol | String | Yes | Market symbol | ```json // Response { "topic": "update:BTC-USD_0", "data": { "bids": [ [ "59252.5", "0.06865" ] ], "asks": [ [ "59292.0", "0.50000" ] ], "seqNum": 628282, "prevSeqNum": 628281, "type": "snapshot", "timestamp": 1565135165600, "symbol": "BTC-USD" } } ``` ### Public Trade Fills Endpoint: `wss://ws.btse.com/ws/spot` · Testnet: `wss://testws.btse.io/ws/spot` Real-time public trade feed for a market. **Topic:** `tradeHistoryApi:` — no authentication required. #### Response Content | Name | Type | Required | Description | | --- | --- | --- | --- | | topic | String | Yes | WebSocket topic | | data | Object[] | Yes | Array of data objects | **Data Object:** | Name | Type | Required | Description | | --- | --- | --- | --- | | symbol | String | Yes | Market symbol | | side | String | Yes | Trade side — `BUY` · `SELL` | | size | Double | Yes | Transacted size | | price | Double | Yes | Transacted price | | tradeId | Long | Yes | Trade sequence ID | | timestamp | Long | Yes | Trade timestamp | ```json // Response { "topic": "tradeHistoryApi:BTC-USD", "data": [ { "symbol": "BTC-USD", "side": "SELL", "size": 0.007, "price": 5302.8, "tradeId": 118974855, "timestamp": 1584446020295 } ] } ``` ### Notifications Endpoint: `wss://ws.btse.com/ws/spot` · Testnet: `wss://testws.btse.io/ws/spot` Real-time order status updates. Requires WebSocket authentication. **Topic:** `notificationApiV3` Includes both base and quote currency size fields (`originalOrderBaseSize`, `originalOrderQuoteSize`, `currentOrderBaseSize`, `currentOrderQuoteSize`, `filledBaseSize`, `totalFilledBaseSize`, `remainingBaseSize`, `remainingQuoteSize`) so that quote-denominated market orders are represented faithfully. #### Response Content | Name | Type | Required | Description | | --- | --- | --- | --- | | topic | String | Yes | | | data | Object | Yes | Data object | **Data Object:** | Name | Type | Required | Description | | --- | --- | --- | --- | | symbol | String | Yes | Market symbol | | orderID | String | Yes | Internal order ID | | side | String | Yes | Trade direction (BUY or SELL) | | orderType | Integer | Yes | Order classification — `76`: Limit · `77`: Market · `80`: Peg/Algo | | txType | Integer | Yes | Transaction type — `0`: LIMIT · `1`: STOP · `2`: TRIGGER · `3`: OCO | | price | Double | Yes | Order price or executed transaction price | | triggerPrice | Double | Yes | Activation price for conditional orders | | pegPriceDeviation | Double | Yes | Deviation percentage (Algo orders only) | | stealth | Double | Yes | Order visibility percentage on orderbook (Algo orders only) | | status | Integer | Yes | Current order state — `1` (MARKET_UNAVAILABLE) · `2` (ORDER_INSERTED) · `4` (ORDER_FULLY_TRANSACTED) · `5` (ORDER_PARTIALLY_TRANSACTED) · `6` (ORDER_CANCELLED) · `8` (INSUFFICIENT_BALANCE) · `9` (TRIGGER_INSERTED) · `10` (TRIGGER_ACTIVATED) · `12` (ERROR_UPDATE_RISK_LIMIT) · `15` (ORDER_REJECTED) · `27` (TRANSFER_SUCCESSFUL) · `28` (TRANSFER_UNSUCCESSFUL) · `41` (ERROR_INVALID_RISK_LIMIT) · `64` (STATUS_LIQUIDATION) · `101` (FUTURES_ORDER_PRICE_OUTSIDE_LIQUIDATION_PRICE) · `1003` (ORDER_LIQUIDATION) · `1004` (ORDER_ADL) | | timestamp | Long | Yes | Order or transaction timestamp (ms) | | avgFilledPrice | Double | Yes | Mean execution price across fills | | clOrderID | String | Yes | User-supplied order identifier | | maker | Boolean | Yes | Whether this fill was on the maker side | | postOnly | Boolean | Yes | Resting order without aggressive matching | | orderUserInitiated | Boolean | Yes | Manual (true) vs. automated (false) order origin | | originalOrderBaseSize | Double | Yes | Initial base currency quantity | | originalOrderQuoteSize | Double | Yes | Initial quote currency quantity | | currentOrderBaseSize | Double | Yes | Latest base currency quantity | | currentOrderQuoteSize | Double | Yes | Latest quote currency quantity | | filledBaseSize | Double | Yes | Base currency amount executed on this event | | totalFilledBaseSize | Double | Yes | Cumulative base currency fills | | remainingBaseSize | Double | Yes | Unfilled base currency portion | | remainingQuoteSize | Double | Yes | Unfilled quote currency portion | | orderCurrency | String | Yes | Size denomination of the order (`base` or `quote`) | | time_in_force | String | Yes | Time in force: `GTC` · `IOC` · `FOK` · `HALFMIN` · `FIVEMIN` · `HOUR` · `TWELVEHOUR` · `DAY` · `WEEK` · `MONTH` | ```json // Response { "topic": "notificationApiV3", "data": { "symbol": "BTC-USDT", "orderID": "789b05fa-dd43-43e8-8626-e229ae216ead", "side": "BUY", "orderType": 77, "txType": 0, "price": 111131, "triggerPrice": 0, "pegPriceDeviation": 1, "stealth": 1, "status": 5, "timestamp": 1752147703368, "avgFilledPrice": 111131, "clOrderID": "_W_bjvf1752147703280", "maker": false, "postOnly": false, "orderUserInitiated": true, "originalOrderBaseSize": 0, "originalOrderQuoteSize": 1000, "currentOrderBaseSize": 0, "currentOrderQuoteSize": 1000, "filledBaseSize": 0.00899, "totalFilledBaseSize": 0.00899, "remainingBaseSize": 0, "remainingQuoteSize": 0.93231, "orderCurrency": "quote", "time_in_force": "GTC" } } ``` ### User Trade Fills Endpoint: `wss://ws.btse.com/ws/spot` · Testnet: `wss://testws.btse.io/ws/spot` Notification whenever an order is filled. Requires WebSocket authentication. **Topic:** `fills` #### Response Content | Name | Type | Required | Description | | --- | --- | --- | --- | | topic | String | Yes | | | data | Object[] | Yes | Array of data objects | **Data Object:** | Name | Type | Required | Description | | --- | --- | --- | --- | | symbol | String | Yes | Market symbol | | orderId | String | Yes | Internal order ID | | clOrderId | String | Yes | Custom order ID | | side | String | Yes | BUY or SELL | | price | String | Yes | Fill price | | size | String | Yes | Fill size | | feeAmount | String | Yes | Fee amount charged | | feeCurrency | String | Yes | Fee currency (BUY: base, SELL: quote) | | base | String | Yes | Base currency | | quote | String | Yes | Quote currency | | maker | Boolean | Yes | Whether this was a maker trade | | timestamp | String | Yes | Fill timestamp | | tradeId | String | Yes | Unique trade ID | ```json // Response { "topic": "fills", "data": [ { "orderId": "", "serialId": "", "clOrderId": "", "type": "", "symbol": "BTC-USD", "side": "BUY", "price": "34000.0", "size": "1.0", "feeAmount": "0.01", "feeCurrency": "USD", "base": "BTC", "quote": "USD", "maker": false, "timestamp": "1624985375123", "tradeId": "" } ] } ``` --- # Futures # Futures API > **New here? Set up authentication first.:** All authenticated Futures endpoints require `Read`, `Trading`, or `Transfer` permission. Read the [Authentication Guide](/authentication) before making your first call. --- ## Environment | Endpoint | Production | Testnet | |---|---|---| | Futures REST | https://api.btse.com/futures | https://testapi.btse.io/futures | | Futures WS | wss://ws.btse.com/ws/futures | wss://testws.btse.io/ws/futures | > The OSS (Order Stream Service) endpoint is used exclusively for [Orderbook incremental update streams](../websocket/orderbook-incremental-updates). --- ## Rate Limits | Category | Per API | Per User | | --- | --- | --- | | **Query** | 15 req/s | 30 req/s | | **Orders** | 75 req/s | 75 req/s | Rate limit violations trigger a tiered block: **1 second → 5 minutes → 15 minutes**. A `Retry-After` header is included in `429` responses. See [Authentication → Rate Limits](/authentication#rate-limits). --- ## Spam Order Policy Orders with a notional value below **5 USDT** are marked as spam orders: - Spam orders are hidden and always pay taker fee. - Post-Only API spam orders are rejected. - Accounts placing ≥ 4 resting orders with total size < 20 USDT risk being flagged. - Flagged accounts may face rate limits, position limits, or disabled API access. --- ## Symbol Naming (v2.3) | Format | Example | When used | | --- | --- | --- | | New | `BTC-PERP` | Default in v2.3 REST | | Old | `BTCPFC` | FIX API legacy — use `ApplyNewSymbolName: Y` in Logon to use new names | --- ## Error Codes See the shared [Error Codes](/error-codes) reference for HTTP status codes and API status enums. ## Futures REST API REST base: `https://api.btse.com/futures` ### Create New Order `POST /api/v2.3/order` - Auth: trading - Rate limit: orders Creates a new order. Supports **MARKET**, **LIMIT**, **OCO**, trigger, and TP/SL orders. > **Tip:** Use `clOrderID` to track orders with your own identifier. For trigger orders, set `txType` to `STOP` or `TRIGGER` and provide `triggerPrice`. Attach take-profit and stop-loss using `takeProfitPrice`/`stopLossPrice` parameters. Supports one-way and hedge position modes via `positionMode`. #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | symbol | String | Yes | Market symbol | | size | Long | Yes | Order size in contract units | | side | String | Yes | Order side — `BUY` · `SELL` | | type | String | Yes | Order type: `LIMIT` · `MARKET` · `OCO` (One-Cancels-the-Other) | | price | Double | No | Required for LIMIT orders | | time_in_force | String | No | Time in force: `GTC` (Good Till Cancelled) · `IOC` (Immediate Or Cancel) · `FOK` (Fill Or Kill) · `HALFMIN` (30 seconds) · `FIVEMIN` (5 minutes) · `HOUR` (1 hour) · `TWELVEHOUR` (12 hours) · `DAY` (1 day) · `WEEK` (1 week) · `MONTH` (1 month) | | txType | String | No | Transaction type: `LIMIT` (default) · `STOP` (requires `triggerPrice`) · `TRIGGER` (requires `triggerPrice`) | | stopPrice | Double | No | Required for OCO orders | | triggerPrice | Double | No | Required for STOP, TRIGGER, and OCO orders | | trailValue | Double | No | Trail value. TP/SL not supported when set | | postOnly | Boolean | No | Post-only order — charged maker fee | | reduceOnly | Boolean | No | Reduce-only order | | clOrderID | String | No | Custom order ID | | trigger | String | No | Trigger type for STOP/TRIGGER: `markPrice` (default) or `lastPrice` | | takeProfitPrice | Double | No | Take profit trigger price | | takeProfitTrigger | String | No | Take profit trigger type: `markPrice` (default) or `lastPrice` | | stopLossPrice | Double | No | Stop loss trigger price | | stopLossTrigger | String | No | Stop loss trigger type: `markPrice` (default) or `lastPrice` | | positionMode | String | No | Position mode: `ONE_WAY` (default) · `HEDGE` · `ISOLATED` | #### Response Content | Name | Type | Description | | --- | --- | --- | | symbol | String | Market symbol | | orderID | String | Order ID | | clOrderID | String | Custom order ID | | orderType | Integer | `76`: Limit · `77`: Market · `80`: Algo | | side | String | Trade side | | price | Double | Order price | | status | Integer | Order status code: `2` (ORDER_INSERTED) · `4` (FULLY_MATCHED) · `5` (PARTIAL_FILL) · `6` (CANCELLED) · `9` (TRIGGER_INSERTED) · `10` (TRIGGER_ACTIVATED) · `15` (REJECTED) | | time_in_force | String | Time in force: `GTC` · `IOC` · `FOK` · `HALFMIN` · `FIVEMIN` · `HOUR` · `TWELVEHOUR` · `DAY` · `WEEK` · `MONTH` | | timestamp | Long | Order timestamp | | trigger | Boolean | Whether order is a trigger order | | triggerPrice | Double | Trigger price (`0` if not a trigger order) | | avgFilledPrice | Double | Average filled price | | message | String | Trade message | | stealth | Double | Algo orders only | | deviation | Double | Algo orders only | | remainingSize | Integer | Current order size minus filled size | | originalOrderSize | Integer | Original quantity — unchanged after amendments | | currentOrderSize | Integer | Latest quantity (filled + remaining) | | filledSize | Integer | Quantity filled in this update | | totalFilledSize | Integer | Cumulative filled quantity | | postOnly | Boolean | Whether order is post-only | | positionMode | String | `ONE_WAY` · `HEDGE` · `ISOLATED` | | positionDirection | String | Position direction: `LONG` · `SHORT` (only present in HEDGE/ISOLATED mode) | | positionId | String | Position ID | ```json // Request (MARKET order) { "symbol": "BTC-PERP", "size": 10, "side": "BUY", "type": "MARKET" } ``` ```json // Request (LIMIT order) { "symbol": "BTC-PERP", "size": 10, "price": 21000, "side": "BUY", "type": "LIMIT" } ``` ```json // Request (LIMIT STOP order) { "symbol": "BTC-PERP", "size": 10, "price": 21000, "side": "BUY", "type": "LIMIT", "txType": "STOP", "triggerPrice": 30000 } ``` ```json // Request (OCO order) { "symbol": "BTC-PERP", "size": 10, "price": 21000, "side": "BUY", "type": "OCO", "txType": "LIMIT", "trigger": "markPrice", "stopPrice": 30010, "triggerPrice": 30000 } ``` ```json // Request (LIMIT with TP/SL) { "symbol": "BTC-PERP", "size": 10, "price": 29000, "side": "BUY", "type": "LIMIT", "takeProfitPrice": 31000, "takeProfitTrigger": "markPrice", "stopLossPrice": 27000, "stopLossTrigger": "lastPrice" } ``` ### Query Order `GET /api/v2.3/order` - Auth: trading - Rate limit: query Queries order details. Cancelled orders are only available for 30 minutes after cancellation. #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | orderID | String | No | Internal order ID. Takes precedence over `clOrderID` if both provided | | clOrderID | String | No | Custom order ID. Required if `orderID` not provided | #### Response Content | Name | Type | Description | | --- | --- | --- | | orderID | String | Order ID | | symbol | String | Market symbol | | orderType | Integer | Order type | | side | String | Trade side | | price | Double | Order price | | orderValue | Double | Total order value | | originalOrderSize | Integer | Original quantity | | currentOrderSize | Integer | Latest quantity | | totalFilledSize | Integer | Cumulative filled quantity | | remainingSize | Integer | Remaining quantity | | pegPriceMin | Double | Min peg price | | pegPriceMax | Double | Max peg price | | pegPriceDeviation | Double | Deviation from index price (%) | | timestamp | Long | Order timestamp | | triggerOrder | Boolean | Whether it is a trigger order | | triggerPrice | Double | Trigger price | | triggerOriginalPrice | Double | Original trigger price | | triggerOrderType | Integer | Trigger order type | | triggerTrailingStopDeviation | Double | Trailing stop deviation (%) | | triggerStopPrice | Double | Stop price (Algo only) | | triggered | Boolean | Whether trigger has fired | | trailValue | Double | Trail value | | clOrderID | String | Custom order ID | | avgFilledPrice | Double | Average filled price | | status | Integer | Order status code: `2` (ORDER_INSERTED) · `4` (FULLY_MATCHED) · `5` (PARTIAL_FILL) · `6` (CANCELLED) · `9` (TRIGGER_INSERTED) · `10` (TRIGGER_ACTIVATED) · `15` (REJECTED) | | takeProfitOrder | Object | Take profit order info | | stopLossOrder | Object | Stop loss order info | | closeOrder | Boolean | Whether it closes a position | | timeInForce | String | Order validity | | contractSize | Double | Contract size | ### Amend Order `PUT /api/v2.3/order` - Auth: trading - Rate limit: orders Amends an existing order's price, size, or trigger price. Does not apply to algo orders. Already-triggered trigger orders cannot have their trigger price amended. #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | symbol | String | Yes | Market symbol | | orderID | String | No | Internal order ID. Required if `clOrderID` is not provided. Takes precedence over `clOrderID` if both are provided | | clOrderID | String | No | Custom order ID. Required if `orderID` is not provided | | type | String | Yes | Amendment type: `PRICE` (amend price, use `value`) · `SIZE` (amend size, use `value`) · `TRIGGERPRICE` (amend trigger price, use `value`) · `ALL` (amend multiple fields, use `orderPrice`/`orderSize`/`triggerPrice`) | | value | Double | No | New value. Required for `PRICE`, `SIZE`, or `TRIGGERPRICE` amendment types | | orderPrice | Double | No | New order price. Used when `type` is `ALL` | | orderSize | Integer | No | New order size in contract units. Used when `type` is `ALL` | | triggerPrice | Double | No | New trigger price (trigger orders only). Used when `type` is `ALL` | #### Response Content | Name | Type | Description | | --- | --- | --- | | symbol | String | Market symbol | | orderID | String | Order ID | | clOrderID | String | Custom order ID | | orderType | Integer | `76`: Limit · `77`: Market · `80`: Algo | | side | String | Trade side | | price | Double | Order price | | status | Integer | Order status code: `2` (ORDER_INSERTED) · `4` (FULLY_MATCHED) · `5` (PARTIAL_FILL) · `6` (CANCELLED) · `9` (TRIGGER_INSERTED) · `10` (TRIGGER_ACTIVATED) · `15` (REJECTED) | | time_in_force | String | Time in force: `GTC` · `IOC` · `FOK` · `HALFMIN` · `FIVEMIN` · `HOUR` · `TWELVEHOUR` · `DAY` · `WEEK` · `MONTH` | | timestamp | Long | Order timestamp | | trigger | Boolean | Whether order is a trigger order | | triggerPrice | Double | Trigger price (`0` if not a trigger order) | | avgFilledPrice | Double | Average filled price | | message | String | Trade message | | stealth | Double | Algo orders only | | deviation | Double | Algo orders only | | remainingSize | Integer | Current order size minus filled size | | originalOrderSize | Integer | Original quantity — unchanged after amendments | | currentOrderSize | Integer | Latest quantity (filled + remaining) | | filledSize | Integer | Quantity filled in this update | | totalFilledSize | Integer | Cumulative filled quantity | | postOnly | Boolean | Whether order is post-only | | positionMode | String | `ONE_WAY` · `HEDGE` · `ISOLATED` | | positionDirection | String | Position direction: `LONG` · `SHORT` (only present in HEDGE/ISOLATED mode) | | positionId | String | Position ID | ```json // Request (Amend price) { "symbol": "BTC-PERP", "orderID": "25248336-66d8-41ff-99fd-83489c4e6029", "type": "PRICE", "value": 35000 } ``` ```json // Request (Amend size) { "symbol": "BTC-PERP", "orderID": "689bf733-4879-4e32-8d1f-cb81f63d24d4", "type": "SIZE", "value": 5 } ``` ```json // Request (Amend multiple fields) { "symbol": "BTC-PERP", "orderID": "cb2785b0-558e-4b30-bf1f-8a8c56174d0c", "type": "ALL", "orderPrice": 40010, "orderSize": 5, "triggerPrice": 40000 } ``` ### Cancel Order `DELETE /api/v2.3/order` - Auth: trading - Rate limit: orders Cancels pending orders. If neither `orderID` nor `clOrderID` is provided, all orders in the market are cancelled. When cancelling by `clOrderID`, all orders with that ID are cancelled. #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | symbol | String | Yes | Market symbol | | orderID | String | No | Internal order ID to cancel | | clOrderID | String | No | Custom order ID to cancel | #### Response Content | Name | Type | Description | | --- | --- | --- | | symbol | String | Market symbol | | orderID | String | Order ID | | clOrderID | String | Custom order ID | | orderType | Integer | `76`: Limit · `77`: Market · `80`: Algo | | side | String | Trade side | | price | Double | Order price | | status | Integer | Order status code: `2` (ORDER_INSERTED) · `4` (FULLY_MATCHED) · `5` (PARTIAL_FILL) · `6` (CANCELLED) · `9` (TRIGGER_INSERTED) · `10` (TRIGGER_ACTIVATED) · `15` (REJECTED) | | time_in_force | String | Time in force: `GTC` · `IOC` · `FOK` · `HALFMIN` · `FIVEMIN` · `HOUR` · `TWELVEHOUR` · `DAY` · `WEEK` · `MONTH` | | timestamp | Long | Order timestamp | | trigger | Boolean | Whether order is a trigger order | | triggerPrice | Double | Trigger price (`0` if not a trigger order) | | avgFilledPrice | Double | Average filled price | | message | String | Trade message | | stealth | Double | Algo orders only | | deviation | Double | Algo orders only | | remainingSize | Integer | Current order size minus filled size | | originalOrderSize | Integer | Original quantity — unchanged after amendments | | currentOrderSize | Integer | Latest quantity (filled + remaining) | | filledSize | Integer | Quantity filled in this update | | totalFilledSize | Integer | Cumulative filled quantity | | postOnly | Boolean | Whether order is post-only | | positionMode | String | `ONE_WAY` · `HEDGE` · `ISOLATED` | | positionDirection | String | Position direction: `LONG` · `SHORT` (only present in HEDGE/ISOLATED mode) | | positionId | String | Position ID | ### Create New Algo Order `POST /api/v2.3/order/peg` - Auth: trading - Rate limit: orders Creates an algo (peg) order — price tracks market price within the specified deviation range. #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | symbol | String | Yes | Market symbol | | price | Double | Yes | Min price (sell) or max price (buy) | | size | Long | Yes | Order size | | side | String | Yes | Order side — `BUY` · `SELL` | | clOrderID | String | No | Custom order ID | | deviation | Double | No | Deviation from index price in % (`-10` to `10`) | | stealth | Double | No | Percentage of order to display on orderbook | | positionMode | String | No | Position mode: `ONE_WAY` (default) · `HEDGE` · `ISOLATED` | #### Response Content | Name | Type | Description | | --- | --- | --- | | symbol | String | Market symbol | | orderID | String | Order ID | | clOrderID | String | Custom order ID | | orderType | Integer | `76`: Limit · `77`: Market · `80`: Algo | | side | String | Trade side | | price | Double | Order price | | status | Integer | Order status code: `2` (ORDER_INSERTED) · `4` (FULLY_MATCHED) · `5` (PARTIAL_FILL) · `6` (CANCELLED) · `9` (TRIGGER_INSERTED) · `10` (TRIGGER_ACTIVATED) · `15` (REJECTED) | | time_in_force | String | Time in force: `GTC` · `IOC` · `FOK` · `HALFMIN` · `FIVEMIN` · `HOUR` · `TWELVEHOUR` · `DAY` · `WEEK` · `MONTH` | | timestamp | Long | Order timestamp | | trigger | Boolean | Whether order is a trigger order | | triggerPrice | Double | Trigger price (`0` if not a trigger order) | | avgFilledPrice | Double | Average filled price | | message | String | Trade message | | stealth | Double | Algo orders only | | deviation | Double | Algo orders only | | remainingSize | Integer | Current order size minus filled size | | originalOrderSize | Integer | Original quantity — unchanged after amendments | | currentOrderSize | Integer | Latest quantity (filled + remaining) | | filledSize | Integer | Quantity filled in this update | | totalFilledSize | Integer | Cumulative filled quantity | | postOnly | Boolean | Whether order is post-only | | positionMode | String | `ONE_WAY` · `HEDGE` · `ISOLATED` | | positionDirection | String | Position direction: `LONG` · `SHORT` (only present in HEDGE/ISOLATED mode) | | positionId | String | Position ID | ### Dead Man's Switch (Cancel All After) `POST /api/v2.3/order/cancelAllAfter` - Auth: trading - Rate limit: orders Sets a TTL (time-to-live) for all open orders. If the server does not receive a new `cancelAllAfter` request before the timeout, all orders are cancelled. Extend by sending another request. #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | timeout | Long | Yes | TTL in milliseconds | ```json // Request { "timeout": 60000 } ``` ### Close Position `POST /api/v2.3/order/close_position` - Auth: trading - Rate limit: orders Closes a position at market or limit price. > **Note:** When using `positionMode: HEDGE` or `ISOLATED`, the `positionId` parameter is required. #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | symbol | String | Yes | Market symbol | | type | String | Yes | Close type — `LIMIT` requires `price` | | price | Double | No | Close price. Required for `LIMIT` type | | postOnly | Boolean | No | Post-only order | | positionId | String | No | Required when `positionMode` is `HEDGE` or `ISOLATED` | #### Response Content | Name | Type | Description | | --- | --- | --- | | symbol | String | Market symbol | | orderID | String | Order ID | | clOrderID | String | Custom order ID | | orderType | Integer | `76`: Limit · `77`: Market · `80`: Algo | | side | String | Trade side | | price | Double | Order price | | status | Integer | Order status code: `2` (ORDER_INSERTED) · `4` (FULLY_MATCHED) · `5` (PARTIAL_FILL) · `6` (CANCELLED) · `9` (TRIGGER_INSERTED) · `10` (TRIGGER_ACTIVATED) · `15` (REJECTED) | | time_in_force | String | Time in force: `GTC` · `IOC` · `FOK` · `HALFMIN` · `FIVEMIN` · `HOUR` · `TWELVEHOUR` · `DAY` · `WEEK` · `MONTH` | | timestamp | Long | Order timestamp | | trigger | Boolean | Whether order is a trigger order | | triggerPrice | Double | Trigger price (`0` if not a trigger order) | | avgFilledPrice | Double | Average filled price | | message | String | Trade message | | stealth | Double | Algo orders only | | deviation | Double | Algo orders only | | remainingSize | Integer | Current order size minus filled size | | originalOrderSize | Integer | Original quantity — unchanged after amendments | | currentOrderSize | Integer | Latest quantity (filled + remaining) | | filledSize | Integer | Quantity filled in this update | | totalFilledSize | Integer | Cumulative filled quantity | | postOnly | Boolean | Whether order is post-only | | positionMode | String | `ONE_WAY` · `HEDGE` · `ISOLATED` | | positionDirection | String | Position direction: `LONG` · `SHORT` (only present in HEDGE/ISOLATED mode) | | positionId | String | Position ID | ### Bind TP/SL `POST /api/v2.3/order/bind/tpsl` - Auth: trading - Rate limit: orders Binds a take profit and/or stop loss to an existing position. At least one of `takeProfitPrice` or `stopLossPrice` must be set. #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | symbol | String | Yes | Market symbol | | side | String | Yes | Required in HEDGE mode — closes the specified position side — `BUY` · `SELL` | | takeProfitPrice | Double | No | Take profit trigger price. At least one of `takeProfitPrice` or `stopLossPrice` must be set | | takeProfitTrigger | String | No | `markPrice` (default) · `lastPrice` | | stopLossPrice | Double | No | Stop loss trigger price. At least one of `takeProfitPrice` or `stopLossPrice` must be set | | stopLossTrigger | String | No | `markPrice` (default) · `lastPrice` | | positionMode | String | No | `ONE_WAY` (default) · `HEDGE` · `ISOLATED` | | positionId | String | No | Required when `positionMode` is `ISOLATED` | #### Response Content | Name | Type | Description | | --- | --- | --- | | symbol | String | Market symbol | | orderID | String | Order ID | | clOrderID | String | Custom order ID | | orderType | Integer | `76`: Limit · `77`: Market · `80`: Algo | | side | String | Trade side | | price | Double | Order price | | status | Integer | Order status code: `2` (ORDER_INSERTED) · `4` (FULLY_MATCHED) · `5` (PARTIAL_FILL) · `6` (CANCELLED) · `9` (TRIGGER_INSERTED) · `10` (TRIGGER_ACTIVATED) · `15` (REJECTED) | | time_in_force | String | Time in force: `GTC` · `IOC` · `FOK` · `HALFMIN` · `FIVEMIN` · `HOUR` · `TWELVEHOUR` · `DAY` · `WEEK` · `MONTH` | | timestamp | Long | Order timestamp | | trigger | Boolean | Whether order is a trigger order | | triggerPrice | Double | Trigger price (`0` if not a trigger order) | | avgFilledPrice | Double | Average filled price | | message | String | Trade message | | stealth | Double | Algo orders only | | deviation | Double | Algo orders only | | remainingSize | Integer | Current order size minus filled size | | originalOrderSize | Integer | Original quantity — unchanged after amendments | | currentOrderSize | Integer | Latest quantity (filled + remaining) | | filledSize | Integer | Quantity filled in this update | | totalFilledSize | Integer | Cumulative filled quantity | | postOnly | Boolean | Whether order is post-only | | positionMode | String | `ONE_WAY` · `HEDGE` · `ISOLATED` | | positionDirection | String | Position direction: `LONG` · `SHORT` (only present in HEDGE/ISOLATED mode) | | positionId | String | Position ID | ### Get Risk Limit `GET /api/v2.3/risk_limit` - Auth: trading - Rate limit: query Returns the current risk limit for the specified market. #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | symbol | String | Yes | Market symbol | #### Response Content | Name | Type | Description | | --- | --- | --- | | symbol | String | Market symbol | | riskLimit | Long | Risk limit value in notional | | riskLimitLevel | Integer | Current risk limit tier | ```json // Response { "symbol": "BTC-PERP", "riskLimit": 3000000, "riskLimitLevel": 1 } ``` ### Set Risk Limit `POST /api/v2.3/risk_limit` - Auth: trading - Rate limit: orders Updates the risk limit tier for the specified market. #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | symbol | String | Yes | Market symbol | | riskLimitLevel | Integer | Yes | Risk limit tier to apply | | positionMode | String | No | Required when `positionMode` is `HEDGE` or `ISOLATED` | #### Response Content | Name | Type | Description | | --- | --- | --- | | symbol | String | Market symbol | | status | Integer | `8`: Insufficient balance · `12`: Error · `20`: Success · `41`: Invalid risk limit | | type | Double | `94` — indicates risk limit update | | timestamp | Long | Update timestamp | | message | String | Response message | ### Get Leverage `GET /api/v2.3/leverage` - Auth: trading - Rate limit: query Returns the current leverage setting for the specified market. #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | symbol | String | Yes | Market symbol | #### Response Content | Name | Type | Description | | --- | --- | --- | | symbol | String | Market symbol | | leverage | Double | Current leverage (`0` = cross max leverage) | | marginMode | String | Current margin mode: `CROSS` · `ISOLATED` | | positionDirection | String | Position direction: `LONG` · `SHORT` · `null` (in one-way mode) | ```json // Response [ { "symbol": "BTC-PERP", "leverage": 100, "marginMode": "CROSS", "positionDirection": null } ] ``` ### Set Leverage `POST /api/v2.3/leverage` - Auth: trading - Rate limit: orders Changes the leverage setting for the specified market. > **Warning:** Setting leverage to `0` applies cross-wallet maximum leverage. Reducing leverage on an existing position may trigger liquidation if the margin is insufficient. #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | symbol | String | Yes | Market symbol | | leverage | Double | Yes | Leverage value. `0` = cross max leverage | | marginMode | String | No | `CROSS` · `ISOLATED` (default) | | positionMode | String | No | Required when `positionMode` is `HEDGE` or `ISOLATED` | | positionId | String | No | Required when `positionMode` is `HEDGE` or `ISOLATED` | #### Response Content | Name | Type | Description | | --- | --- | --- | | symbol | String | Market symbol | | status | Integer | `8`: Insufficient · `13`: Invalid leverage · `20`: Success · `64`: Liquidation | | type | Double | `93` — indicates leverage update | | timestamp | Long | Update timestamp | | message | String | Response message | ### Change Contract Settlement Currency `POST /api/v2.3/settle_in` - Auth: trading - Rate limit: orders Changes the settlement currency for the current position. #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | symbol | String | Yes | Market symbol | | currency | String | Yes | Settlement currency | | positionId | String | No | Required when `positionMode` is `HEDGE` or `ISOLATED` | #### Response Content | Name | Type | Description | | --- | --- | --- | | status | Integer | Error status code | | error | String | Error message | | code | Integer | Internal error code | | message | String | Human-readable error description | ### Query Position Mode `GET /api/v2.3/position_mode` - Auth: trading - Rate limit: query Returns the current position mode for each market. #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | symbol | String | No | Market symbol | #### Response Content | Name | Type | Description | | --- | --- | --- | | symbol | String | Market symbol | | positionMode | String | `ONE_WAY` · `HEDGE` · `ISOLATED` | ```json // Response [ { "symbol": "BTC-PERP", "positionMode": "ONE_WAY" } ] ``` ### Change Position Mode `POST /api/v2.3/position_mode` - Auth: trading - Rate limit: orders Changes the position mode for the specified market. #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | symbol | String | Yes | Market symbol | | positionMode | String | Yes | `ONE_WAY` · `HEDGE` · `ISOLATED` | #### Response Content | Name | Type | Description | | --- | --- | --- | | symbol | String | Market symbol | | status | Integer | `20`: Success | | type | String | `129` — Futures config mode change | | timestamp | Long | Update timestamp | | message | String | Response message | ### Query Open Orders `GET /api/v2.3/user/open_orders` - Auth: trading - Rate limit: query Returns open orders that have not been matched or were recently matched. #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | symbol | String | No | Market symbol | | orderID | String | No | Filter by internal order ID | | clOrderID | String | No | Filter by custom order ID (ignored if `orderID` provided) | #### Response Content | Name | Type | Description | | --- | --- | --- | | symbol | String | Market symbol | | orderID | String | Order ID | | clOrderID | String | Custom order ID | | orderType | Integer | `76`: Limit · `77`: Market · `80`: Algo | | side | String | Trade side | | price | Double | Order price | | orderValue | Double | Notional value | | originalOrderSize | Integer | Original quantity | | currentOrderSize | Integer | Latest quantity | | totalFilledSize | Integer | Cumulative filled | | remainingSize | Integer | Remaining quantity | | timestamp | Long | Order timestamp | | timeInForce | String | Order validity | | triggerOrder | Boolean | Whether trigger order | | triggered | Boolean | Whether triggered | | triggerPrice | Double | Trigger price | | reduceOnly | Boolean | Whether reduce-only | | avgFilledPrice | Double | Average filled price | | orderState | String | Order state — `STATUS_ACTIVE` · `STATUS_INACTIVE` | | takeProfitOrder | Object | Take profit order info | | stopLossOrder | Object | Stop loss order info | | closeOrder | Boolean | Whether closes a position | | positionMode | String | `ONE_WAY` · `HEDGE` · `ISOLATED` | | positionDirection | String | Position direction: `LONG` · `SHORT` (only present in HEDGE/ISOLATED mode) | | positionId | String | Position ID | | contractSize | Double | Contract size | ```json // Response [] ``` ### Query Trades Fills `GET /api/v2.3/user/trade_history` - Auth: read - Rate limit: query Returns the authenticated user's trade history. **Time range constraints:** - `startTime` + `endTime`: max 7 days - `startTime` only: 7 days after start - `endTime` only: 7 days before end - Neither: 7 days before current time #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | symbol | String | No | Market symbol | | startTime | Long | No | Start time in milliseconds | | endTime | Long | No | End time in milliseconds | | beforeSerialId | Long | No | Pagination for high-volume markets | | afterSerialId | Long | No | Pagination for high-volume markets | | count | Long | No | Number of records | | includeOld | Boolean | No | Include records older than 7 days | | orderID | String | No | Filter by order ID | | clOrderID | String | No | Filter by custom order ID | #### Response Content | Name | Type | Description | | --- | --- | --- | | symbol | String | Market symbol | | side | String | Trade side — `BUY` · `SELL` | | price | Double | Transacted price | | size | Long | Original order size | | serialId | Long | Running sequence ID | | tradeId | String | Trade identifier | | orderId | String | Order ID | | clOrderID | String | Custom order ID | | timestamp | Long | Trade timestamp | | base | String | Base currency | | quote | String | Quote currency | | wallet | String | `CROSS@` or `ISOLATED@{symbol}-USDT` | | feeCurrency | String | Fee currency | | feeAmount | Double | Fee amount | | filledPrice | Double | Filled price | | avgFilledPrice | Double | Average filled price | | filledSize | Long | Filled size | | orderType | Integer | Order type | | realizedPnL | Double | Realized P&L | | positionId | String | Position ID | | contractSize | Double | Contract size | ```json // Response [] ``` ### Query Position `GET /api/v2.3/user/positions` - Auth: trading - Rate limit: query Returns all open positions. When no symbol is specified, all positions are returned. #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | symbol | String | No | Market symbol | #### Response Content | Name | Type | Description | | --- | --- | --- | | symbol | String | Market symbol | | side | String | Trade side | | size | Long | Position size | | entryPrice | Double | Entry price | | markPrice | Double | Mark price | | marginType | Integer | `91`: Cross · `92`: Isolated | | orderValue | Double | Notional value | | settleWithAsset | String | Settlement currency | | totalMaintenanceMargin | Double | Maintenance margin | | unrealizedProfitLoss | Double | Unrealized P&L | | liquidationPrice | Double | Liquidation price | | isolatedLeverage | Double | Isolated leverage | | adlScoreBucket | Double | ADL probability score | | liquidationInProgress | Boolean | Whether liquidation is in progress | | currentLeverage | Double | Current leverage | | timestamp | Long | Query timestamp | | takeProfitOrder | Object | Take profit order info | | stopLossOrder | Object | Stop loss order info | | positionMode | String | `ONE_WAY` · `HEDGE` · `ISOLATED` | | positionDirection | String | Position direction: `LONG` · `SHORT` (only present in HEDGE/ISOLATED mode) | | positionId | String | Position ID | ### Query Account Fee `GET /api/v2.3/user/fees` - Auth: read - Rate limit: query Returns the user's trading fees for futures. #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | symbol | String | No | Market symbol | #### Response Content | Name | Type | Description | | --- | --- | --- | | symbol | String | Market symbol | | makerFee | Double | Maker fee rate | | takerFee | Double | Taker fee rate | ```json // Response [ { "symbol": "BTC-PERP", "makerFee": 0.0002, "takerFee": 0.00055 } ] ``` ### Query Margin Setting `GET /api/v2.3/user/margin_setting` - Auth: read - Rate limit: query Returns the user's initial and maintenance margin percentages. Returns all markets if no symbol is provided. #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | symbol | String | No | Market symbol | #### Response Content | Name | Type | Description | | --- | --- | --- | | symbol | String | Market symbol | | initialMarginPercentage | Double | Current initial margin percentage | | maintenanceMarginPercentage | Double | Current maintenance margin percentage | ```json // Response [ { "symbol": "BTC-PERP", "initialMarginPercentage": 0.01, "maintenanceMarginPercentage": 0.005 } ] ``` ### Query Wallet Balance `GET /api/v2.3/user/wallet` - Auth: read - Rate limit: query Returns the user's futures wallet balance. #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | wallet | String | No | Wallet identifier — `CROSS@` or `ISOLATED@{symbol}-USDT` (e.g. `ISOLATED@BTC-PERP-USDT`). Optional — if omitted, returns balances across all wallets. | #### Response Content | Name | Type | Description | | --- | --- | --- | | wallet | String | Wallet identifier | | activeWalletName | String | Active wallet name | | queryType | Integer | Query type | | trackingID | Long | Internal tracking ID | | walletTotalValue | Double | Wallet total value | | totalValue | Double | Total value | | marginBalance | Double | Margin balance | | availableBalance | Double | Available balance | | unrealisedProfitLoss | Double | Unrealized P&L | | maintenanceMargin | Double | Maintenance margin | | leverage | Double | Current leverage (CROSS: current, not setting) | | openMargin | Double | Open margin | | assets | Object[] | Available assets | | assetsInUse | Object[] | Assets in use | ```json // Response [ { "wallet": "CROSS@", "activeWalletName": "String", "queryType": 0, "trackingID": 0, "walletTotalValue": 0, "totalValue": 100, "marginBalance": 100, "availableBalance": 100, "unrealisedProfitLoss": 0, "maintenanceMargin": 0, "leverage": 0, "openMargin": 0, "assets": [ { "balance": 0.20183537, "assetPrice": 7158.845, "currency": "BTC" } ], "assetsInUse": [ { "balance": 0.01, "assetPrice": 7158.845, "currency": "BTC" } ] } ] ``` ### Query Wallet History `GET /api/v2.3/user/wallet_history` - Auth: read - Rate limit: query Returns wallet transaction history for futures wallets. #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | wallet | String | No | Wallet identifier (`CROSS@` or `ISOLATED@BTC-PERP-USDT`). Omit to return all wallets | | startTime | Long | No | Start time in milliseconds | | endTime | Long | No | End time in milliseconds | | count | Integer | No | Number of records to return | #### Response Content | Name | Type | Description | | --- | --- | --- | | currency | String | Currency | | amount | Double | Transaction amount | | fees | Double | Fees charged | | orderId | String | Internal wallet order ID | | wallet | String | `CROSS@` or `ISOLATED@` | | description | String | Transaction description | | status | Integer | `1`: PENDING · `2`: PROCESSING · `10`: COMPLETED · `16`: CANCELLED | | type | Integer | `105`: Wallet Transfer · `106`: Liquidation · `108`: Realized PnL · `110`: Funding · `121`: Asset Conversion | ```json // Response [] ``` ### Query Unified Wallet Margin `GET /api/v2.3/user/unifiedWallet/margin` - Auth: read - Rate limit: query Returns margin information for the upgraded unified wallet. Use this endpoint if you have upgraded to the unified wallet. #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | symbol | String | No | Market symbol | | positionId | String | No | Position unique ID | #### Response Content | Name | Type | Description | | --- | --- | --- | | symbol | String | Market symbol | | walletTotalValue | Double | Total wallet value | | walletTotalUnrealizedProfitLoss | Double | Total unrealized P&L | | futuresTotalAvailableBalance | Double | Total available balance | | wallets | Object[] | Per-wallet breakdown | ### Query Wallet Margin `GET /api/v2.3/user/margin` - Auth: read - Rate limit: query Returns margin information for the specified wallet. **Not available for unified wallet users** — use Query Unified Wallet Margin instead. #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | symbol | String | Yes | Market symbol | #### Response Content | Name | Type | Description | | --- | --- | --- | | wallet | String | Wallet identifier | | activeWalletName | String | Active wallet name | | queryType | Integer | Query type | | trackingID | Long | Internal tracking ID | | walletTotalValue | Double | Wallet total value | | totalValue | Double | Total value | | marginBalance | Double | Margin balance | | availableBalance | Double | Available balance | | unrealisedProfitLoss | Double | Unrealized P&L | | maintenanceMargin | Double | Maintenance margin | | leverage | Double | Current leverage (CROSS: current, not setting) | | openMargin | Double | Open margin | | assets | Object[] | Available assets | | assetsInUse | Object[] | Assets in use | ### Transfer Funds Between Futures Wallets `POST /api/v2.3/user/wallet/transfer` - Auth: transfer - Rate limit: orders Transfers funds between user wallets (e.g. Spot → Cross, Cross → Isolated). > **Important:** When transferring to/from an `ISOLATED` wallet, you must specify the full wallet identifier (e.g. `ISOLATED@BTC-PERP-USDT`). > > **Note:** the source wallet must hold balance in every `currency` listed in `apiWallets` before calling this endpoint. #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | walletSrc | String | No | Source wallet identifier. Required when `walletSrcType` is `ISOLATED` | | walletSrcType | String | Yes | Source wallet type — `SPOT` · `CROSS` · `ISOLATED` | | walletDest | String | No | Destination wallet identifier. Required when `walletDestType` is `ISOLATED` | | walletDestType | String | Yes | Destination wallet type — `SPOT` · `CROSS` · `ISOLATED` | | apiWallets | Object[] | Yes | Transfer details | #### Response Content | Name | Type | Description | | --- | --- | --- | | wallet | String | Wallet identifier | | activeWalletName | String | Active wallet name | | queryType | Integer | Query type | | trackingID | Long | Internal tracking ID | | walletTotalValue | Double | Wallet total value | | totalValue | Double | Total value | | marginBalance | Double | Margin balance | | availableBalance | Double | Available balance | | unrealisedProfitLoss | Double | Unrealized P&L | | maintenanceMargin | Double | Maintenance margin | | leverage | Double | Current leverage (CROSS: current, not setting) | | openMargin | Double | Open margin | | assets | Object[] | Available assets | | assetsInUse | Object[] | Assets in use | ```json // Request { "walletSrc": "", "walletSrcType": "SPOT", "walletDest": "", "walletDestType": "CROSS", "apiWallets": [ { "currency": "USD", "allBalance": true }, { "currency": "BTC", "allBalance": true } ] } ``` ### Sub-Account Wallet Transfer `POST /api/v2.3/subaccount/wallet/transfer` - Auth: transfer - Rate limit: orders Transfers funds between the main account and a sub-account wallet. #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | walletSrcType | String | Yes | Source wallet type — `SPOT` · `CROSS` · `ISOLATED` | | walletSrc | String | No | Required when `walletSrcType` is `ISOLATED` | | walletDestType | String | Yes | Destination wallet type — `SPOT` · `CROSS` · `ISOLATED` | | walletDest | String | No | Required when `walletDestType` is `ISOLATED` | | fromUser | String | Yes | Source username | | receiver | String | Yes | Receiver username | | apiWallets | Object[] | Yes | Transfer details | #### Response Content | Name | Type | Description | | --- | --- | --- | | code | Integer | Response code | | msg | String | Response message | | time | Long | Response timestamp | | data | Object | Transfer result data | | success | Boolean | Whether transfer succeeded | ## Futures WebSocket API WebSocket: `wss://ws.btse.com/ws/futures` ### Ping / Pong Endpoint: `wss://ws.btse.com/ws/futures` · Testnet: `wss://testws.btse.io/ws/futures` Send a plain text `ping` to keep the connection alive. The server responds with `pong`. ### Subscription Endpoint: `wss://ws.btse.com/ws/futures` · Testnet: `wss://testws.btse.io/ws/futures` Subscribe or unsubscribe from any topic. #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | op | String | Yes | Operation — `subscribe` · `unsubscribe` | | args | String[] | Yes | Array of topic strings | ```json // Request { "op": "subscribe", "args": [ "tradeHistoryApiV3:BTC-PERP" ] } ``` #### Response Content | Name | Type | Required | Description | | --- | --- | --- | --- | | event | String | No | Echo of the event type | | channel | String[] | No | Successfully subscribed topics | ```json // Response { "event": "subscribe", "channel": [ "tradeHistoryApiV3:BTC-PERP" ] } ``` ### WebSocket Authentication Endpoint: `wss://ws.btse.com/ws/futures` · Testnet: `wss://testws.btse.io/ws/futures` Authenticate the session to access private topics (Notifications, User Trade Fills, Positions). Signature: `HMAC-SHA384(secret, "/ws/futures" + nonce)` #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | op | String | Yes | Operation — fixed value `authKeyExpires` | | args | String[] | Yes | [API_KEY, NONCE, HMAC-SHA384_SIGNATURE] | ```json // Request { "op": "authKeyExpires", "args": [ "", "", "" ] } ``` ### OSS L1 Snapshot (By Grouping) Endpoint: `wss://ws.btse.com/ws/oss/futures` · Testnet: `wss://testws.btse.io/ws/oss/futures` Subscribe to the best bid / best ask (Level 1) for a market at a specific grouping. **Topic format:** `snapshotL1:_` (e.g. `snapshotL1:BTC-PERP_0`) #### Response Content | Name | Type | Required | Description | | --- | --- | --- | --- | | topic | String | Yes | WebSocket topic | | data | Object | Yes | Data object | **Data Object:** | Name | Type | Required | Description | | --- | --- | --- | --- | | bids | String[][] | Yes | Best bid [price, size] | | asks | String[][] | Yes | Best ask [price, size] | | symbol | String | Yes | Market symbol | | type | String | Yes | | | timestamp | Long | Yes | Orderbook timestamp (ms) | ```json // Response { "topic": "snapshotL1:BTC-PERP_0", "data": { "bids": [ [ "28064.9", "1268" ] ], "asks": [ [ "28065.0", "1015" ] ], "type": "snapshotL1", "symbol": "BTC-PERP", "timestamp": 1680751558529 } } ``` ### Orderbook Incremental Updates Endpoint: `wss://ws.btse.com/ws/oss/futures` · Testnet: `wss://testws.btse.io/ws/oss/futures` Subscribe to full orderbook updates with delta streaming. **Topic format:** `update:_` (e.g. `update:BTC-PERP_0`) On successful subscribe, the server pushes one `snapshot` message (up to 50 levels) followed by `delta` updates. **The snapshot is delivered without grouping (raw book); only `delta` updates respect the subscribed `` value.** If the market is idle, the snapshot still arrives but no deltas follow until book activity resumes. **Applying deltas:** - `[price, size]` tuple: if `size` is `"0"`, remove the price level; otherwise update it - `seqNum` must equal `prevSeqNum + 1`. On gap, unsubscribe and re-subscribe - On crossed orderbook (best bid >= best ask), unsubscribe and re-subscribe #### Response Content | Name | Type | Required | Description | | --- | --- | --- | --- | | topic | String | Yes | WebSocket topic | | data | Object | Yes | Data object | **Data Object:** | Name | Type | Required | Description | | --- | --- | --- | --- | | bids | String[][] | Yes | Bid updates [price, size] | | asks | String[][] | Yes | Ask updates [price, size] | | seqNum | Integer | Yes | Current sequence number | | prevSeqNum | Integer | Yes | Previous sequence number | | type | String | Yes | snapshot (first) or delta (updates) | | timestamp | Long | Yes | Orderbook timestamp | | symbol | String | Yes | Market symbol | ```json // Response { "topic": "update:BTC-PERP_0", "data": { "bids": [ [ "122160.1", "69350" ], [ "122159.9", "226150" ] ], "asks": [ [ "122133.3", "1000" ], [ "122124.0", "343410" ] ], "seqNum": 628282, "prevSeqNum": 628281, "type": "snapshot", "timestamp": 1565135165600, "symbol": "BTC-PERP" } } ``` ### Public Trade Fills Endpoint: `wss://ws.btse.com/ws/futures` · Testnet: `wss://testws.btse.io/ws/futures` Real-time public trade feed for a market. **Topic:** `tradeHistoryApiV3:` — no authentication required. #### Response Content | Name | Type | Required | Description | | --- | --- | --- | --- | | topic | String | Yes | WebSocket topic | | data | Object[] | Yes | Array of data objects | **Data Object:** | Name | Type | Required | Description | | --- | --- | --- | --- | | symbol | String | Yes | Market symbol | | side | String | Yes | Trade side — `BUY` · `SELL` | | size | Integer | Yes | Transacted size | | price | Double | Yes | Transacted price | | tradeId | Long | Yes | Trade sequence ID | | timestamp | Long | Yes | Trade timestamp | ```json // Response { "topic": "tradeHistoryApiV3:BTC-PERP", "data": [ { "symbol": "BTC-PERP", "side": "SELL", "size": 152, "price": 111144.8, "tradeId": 927002515, "timestamp": 1752129545326 } ] } ``` ### Notifications Endpoint: `wss://ws.btse.com/ws/futures` · Testnet: `wss://testws.btse.io/ws/futures` Real-time order status updates. Requires WebSocket authentication. **Topic:** `notificationApiV4` Carries the full v2.3 size set (`originalOrderSize`, `currentOrderSize`, `filledSize`, `totalFilledSize`, `remainingSize`) plus `positionId` and `postOnly`, so partial fills and post-only orders are represented faithfully. #### Response Content | Name | Type | Required | Description | | --- | --- | --- | --- | | topic | String | Yes | | | data | Object[] | Yes | Array of data objects | **Data Object:** | Name | Type | Required | Description | | --- | --- | --- | --- | | symbol | String | Yes | Market symbol | | orderID | String | Yes | Internal order ID | | side | String | Yes | BUY or SELL | | type | Integer | Yes | 76: Limit · 77: Market · 80: Algo | | price | Double | Yes | Order price or transacted price | | originalOrderSize | Integer | Yes | Original order quantity | | currentOrderSize | Integer | Yes | Latest order quantity | | filledSize | Integer | Yes | Quantity filled in this update | | totalFilledSize | Integer | Yes | Cumulative filled quantity | | remainingSize | Integer | Yes | Remaining quantity | | avgFilledPrice | Double | Yes | Average filled price | | status | Integer | Yes | Order status code: `2` (ORDER_INSERTED) · `4` (FULLY_MATCHED) · `5` (PARTIAL_FILL) · `6` (CANCELLED) · `9` (TRIGGER_INSERTED) · `10` (TRIGGER_ACTIVATED) · `15` (REJECTED) | | clOrderID | String | Yes | Custom order ID | | maker | Boolean | Yes | Whether this was a maker trade | | time_in_force | String | Yes | Time in force: `GTC` · `IOC` · `FOK` · `HALFMIN` · `FIVEMIN` · `HOUR` · `TWELVEHOUR` · `DAY` · `WEEK` · `MONTH` | | timestamp | Long | Yes | Order/trade timestamp | | txType | String | Yes | STOP · TAKEPROFIT · LIMIT | | triggerPrice | Double | Yes | Trigger price | | stealth | Double | Yes | Algo orders only | | pegPriceDeviation | Double | Yes | Algo orders only | | positionId | String | Yes | Position ID | | postOnly | Boolean | Yes | Whether post-only order | ```json // Response { "topic": "notificationApiV4", "data": [ { "symbol": "BTC-PERP", "orderID": "45e8bb8d-d708-4a90-a428-c61583f90efe", "side": "BUY", "orderType": 77, "type": 0, "price": 111085.1, "triggerPrice": 0, "status": 4, "timestamp": 1752147101805, "avgFilledPrice": 111085.1, "clOrderID": "", "postOnly": false, "maker": false, "positionId": "BTC-PERP-USDT", "originalOrderSize": 900, "currentOrderSize": 900, "filledSize": 900, "totalFilledSize": 900, "remainingSize": 0, "time_in_force": "GTC" } ] } ``` ### User Trade Fills Endpoint: `wss://ws.btse.com/ws/futures` · Testnet: `wss://testws.btse.io/ws/futures` Notification whenever a trade is executed for the authenticated user. Requires WebSocket authentication. **Topic:** `fillsV2` #### Response Content | Name | Type | Required | Description | | --- | --- | --- | --- | | topic | String | Yes | | | data | Object[] | Yes | Array of data objects | **Data Object:** | Name | Type | Required | Description | | --- | --- | --- | --- | | symbol | String | Yes | Market symbol | | orderId | String | Yes | Internal order ID | | clOrderId | String | Yes | Custom order ID | | serialId | Long | Yes | Trade sequence ID | | tradeId | String | Yes | Trade unique identifier | | type | String | Yes | 76: Limit · 77: Market · 80: Algo | | side | String | Yes | BUY or SELL | | price | String | Yes | Transacted price | | size | String | Yes | Transacted size | | feeAmount | String | Yes | Fee amount | | feeCurrency | String | Yes | Fee currency | | base | String | Yes | Base currency | | quote | String | Yes | Quote currency | | maker | Boolean | Yes | Whether maker trade | | timestamp | Long | Yes | Trade timestamp | | contractSize | Double | Yes | Contract size | ```json // Response { "topic": "fillsV2", "data": [ { "orderId": "6aa36da1-0ed8-46c1-9327-c9d6313b3d12", "serialId": 189349157, "clOrderId": "_W_ekxogc1711427518228", "type": "77", "symbol": "BTC-PERP", "side": "BUY", "price": "69704.4", "size": "1.0", "feeAmount": "0.0348522", "feeCurrency": "USDT", "base": "BTC-PERP", "quote": "USDT", "maker": false, "timestamp": 1711427518338, "contractSize": 0.0001, "tradeId": "e094117f-9f84-4c82-b55d-d3d2a54d0dca" } ] } ``` ### All Position Endpoint: `wss://ws.btse.com/ws/futures` · Testnet: `wss://testws.btse.io/ws/futures` Pushed whenever any position changes. Requires WebSocket authentication. **Topic:** `allPositionV4` #### Response Content | Name | Type | Required | Description | | --- | --- | --- | --- | | topic | String | Yes | allPositionV4 or positionsV3 | | data | Object[] | Yes | Array of data objects | **Data Object:** | Name | Type | Required | Description | | --- | --- | --- | --- | | marketName | String | Yes | Market name | | orderType | Integer | Yes | 90: Futures position | | orderMode | Integer | Yes | 66: BUY · 83: SELL | | entryPrice | Double | Yes | Entry price | | liquidationPrice | Double | Yes | Liquidation price | | markPrice | Double | Yes | Mark price | | unrealizedProfitLoss | Double | Yes | Unrealized P&L | | totalMaintenanceMargin | Double | Yes | Maintenance margin | | totalContracts | Double | Yes | Contract size | | marginType | Integer | Yes | 91: CROSS · 92: ISOLATED | | closeOrder | Boolean | Yes | Whether a close order exists | | liquidationInProgress | Boolean | Yes | Whether in liquidation | | currentLeverage | Double | Yes | Current leverage | | adlScoreBucket | Double | Yes | ADL probability score | | takeProfitOrder | Object | Yes | Take profit order info | | stopLossOrder | Object | Yes | Stop loss order info | | positionMode | String | Yes | ONE_WAY · HEDGE · ISOLATED | | positionDirection | String | Yes | Position direction: `LONG` · `SHORT` (only present in HEDGE/ISOLATED mode) | | positionId | String | Yes | Position ID | | contractSize | Double | Yes | The position contract size | ```json // Response { "topic": "allPositionV4", "data": [ { "marketName": "BTC-PERP", "orderType": 90, "orderMode": 66, "entryPrice": 111085.1, "liquidationPrice": 0, "markPrice": 111100, "unrealizedProfitLoss": 0.01, "totalMaintenanceMargin": 5.5, "totalContracts": 1, "marginType": 91, "closeOrder": false, "liquidationInProgress": false, "currentLeverage": 10, "adlScoreBucket": 2, "positionMode": "ONE_WAY", "positionDirection": "LONG", "positionId": "BTC-PERP-USDT", "contractSize": 0.0001 } ] } ``` ### Positions Endpoint: `wss://ws.btse.com/ws/futures` · Testnet: `wss://testws.btse.io/ws/futures` Pushed when a position changes. Includes a zero-value snapshot when a position is closed. Requires WebSocket authentication. **Topic:** `positionsV3` Response structure is the same as All Position with additional raw internal fields. #### Response Content | Name | Type | Required | Description | | --- | --- | --- | --- | | topic | String | Yes | allPositionV4 or positionsV3 | | data | Object[] | Yes | Array of data objects | **Data Object:** | Name | Type | Required | Description | | --- | --- | --- | --- | | marketName | String | Yes | Market name | | orderType | Integer | Yes | 90: Futures position | | orderMode | Integer | Yes | 66: BUY · 83: SELL | | entryPrice | Double | Yes | Entry price | | liquidationPrice | Double | Yes | Liquidation price | | markPrice | Double | Yes | Mark price | | unrealizedProfitLoss | Double | Yes | Unrealized P&L | | totalMaintenanceMargin | Double | Yes | Maintenance margin | | totalContracts | Double | Yes | Contract size | | marginType | Integer | Yes | 91: CROSS · 92: ISOLATED | | closeOrder | Boolean | Yes | Whether a close order exists | | liquidationInProgress | Boolean | Yes | Whether in liquidation | | currentLeverage | Double | Yes | Current leverage | | adlScoreBucket | Double | Yes | ADL probability score | | takeProfitOrder | Object | Yes | Take profit order info | | stopLossOrder | Object | Yes | Stop loss order info | | positionMode | String | Yes | ONE_WAY · HEDGE · ISOLATED | | positionDirection | String | Yes | Position direction: `LONG` · `SHORT` (only present in HEDGE/ISOLATED mode) | | positionId | String | Yes | Position ID | | contractSize | Double | Yes | The position contract size | ```json // Response { "topic": "allPositionV4", "data": [ { "marketName": "BTC-PERP", "orderType": 90, "orderMode": 66, "entryPrice": 111085.1, "liquidationPrice": 0, "markPrice": 111100, "unrealizedProfitLoss": 0.01, "totalMaintenanceMargin": 5.5, "totalContracts": 1, "marginType": 91, "closeOrder": false, "liquidationInProgress": false, "currentLeverage": 10, "adlScoreBucket": 2, "positionMode": "ONE_WAY", "positionDirection": "LONG", "positionId": "BTC-PERP-USDT", "contractSize": 0.0001 } ] } ``` --- # Wallet # Wallet API > **New here? Set up authentication first.:** All wallet endpoints (except public crypto network queries) require authentication. Read the [Authentication Guide](/authentication) before making your first call. --- ## Environment | Endpoint | Production | Testnet | |---|---|---| | Wallet REST | https://api.btse.com | https://testapi.btse.io | --- ## Rate Limits | Category | Per User | | --- | --- | | **Wallet operations** | 5 req/s | --- ## Symbol Naming Note For crypto assets with small prices, [Markets Information](../../markets/rest/get-markets) displays them with K/M/B prefixes (e.g. `K_PEIPEI`). Wallet APIs always use the **original name without prefix** (e.g. `PEIPEI`), matching the `baseCurrency` field. ## Wallet REST API REST base: `https://api.btse.com` ### Assets `GET /public-api/wallet/v1/assets` - Auth: read - Rate limit: query Get available asset list. #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | asset | String | No | Asset | #### Response Content | Name | Type | Description | | --- | --- | --- | | data | Object[] | | ```json // Response { "success": true, "code": 1, "msg": "Success", "time": 1624989977940, "data": [ { "asset": "BTC", "type": "CRYPTO", "name": "Bitcoin", "precision": 8, "tradeScale": 5 }, { "asset": "USD", "type": "FIAT", "name": "US Dollar", "precision": 2, "tradeScale": 2 } ] } ``` ### Crypto List `GET /public-api/wallet/v1/crypto/list` - Auth: public - Rate limit: query Get available crypto token list with corresponding action. Only supports `DEPOSIT` and `WITHDRAW`. #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | action | Object | Yes | Action | #### Response Content | Name | Type | Description | | --- | --- | --- | | data | String[] | | ```json // Response { "success": true, "code": 1, "msg": "Success", "time": 1624989977940, "data": [ "BTC", "ETH" ] } ``` ### Crypto Networks `GET /public-api/wallet/v1/crypto/networks` - Auth: public - Rate limit: query Get crypto network list with corresponding crypto. This API can be publicly accessed without any security headers to get a default network list. If you access with `Read` permission authentication, the result will be much more accurate by account setting. The response of `depositEnable` and `withdrawEnable` is true by default under the publicly accessed environment. - The total deposit fee is `max(depositFeeMin, (amount × depositFeeRate)) + (depositExtFees + depositExtFeeRate × amount)`. - The total withdrawal fee is `max(withdrawFeeMin, (amount × withdrawFeeRate)) + (withdrawExtFees + withdrawExtFeeRate × amount)`. **Note: For crypto with small prices, the** [Markets Information](/markets/rest/markets-information) **API displays them using K/M/B prefixes and process trades with the corresponding quantities. (ex. `K_PEIPEI`) However, Wallet-related APIs display the actual crypto information, so both requests and responses will use the original crypto name. (ex. `PEIPEI`)** #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | crypto | String | Yes | Crypto currency, such as BTC | #### Response Content | Name | Type | Description | | --- | --- | --- | | data | Object[] | | ```json // Response { "success": true, "code": 1, "msg": "Success", "time": 1624989977940, "data": [ { "network": "ERC20", "name": "Ethereum (ERC20)", "depositEnable": true, "withdrawEnable": true, "confirmationTime": 15, "depositAmtMin": "0", "depositFeeMin": "0", "depositFeeRate": "0", "depositExtFees": "0", "depositExtFeeRate": "0", "needAddressExtension": false, "withdrawAmtMin": "36.41", "withdrawFeeMin": "6.41", "withdrawFeeRate": "0", "withdrawExtFees": "0", "withdrawExtFeeRate": "0" }, { "network": "RIPPLE", "name": "Ripple", "depositEnable": true, "withdrawEnable": true, "confirmationTime": 10, "depositAmtMin": "0", "depositFeeMin": "0", "depositFeeRate": "0", "depositExtFees": "0", "depositExtFeeRate": "0", "needAddressExtension": true, "addressExtensionTypeName": "tag", "withdrawAmtMin": "0.25", "withdrawFeeMin": "20", "withdrawFeeRate": "0", "withdrawExtFees": "0", "withdrawExtFeeRate": "0" } ] } ``` ### Get Asset Exchange Rate `GET /public-api/wallet/v1/assets/exchangeRate` - Auth: public - Rate limit: query Retrieve exchange rate about a specific pair. For example, `baseCurrency` and `quoteCurrency`. #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | baseCurrency | String | Yes | Base currency, such as BTC | | amount | String | Yes | The quantity of `baseCurrency`. | | quoteCurrency | String | Yes | Quote currency, such as USDT | #### Response Content | Name | Type | Description | | --- | --- | --- | | data | Object | | ```json // Response { "success": true, "code": 1, "msg": "Success", "time": 1624989977940, "data": { "rate": "19026.12846161" } } ``` ### User Wallet `GET /public-api/wallet/v1/user/assets` - Auth: read - Rate limit: query Get user's assets list and balance. Requires the `Read` permission. #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | asset | String | No | Asset | | action | Object | No | Support action | #### Response Content | Name | Type | Description | | --- | --- | --- | | data | Object[] | | ```json // Response { "success": true, "code": 1, "msg": "Success", "time": 1624989977940, "data": [ { "asset": "BTC", "type": "CRYPTO", "totalAmount": "100.0", "availableAmount": "100.0", "availableActions": [ "CONVERT", "TRANSFER", "WITHDRAW", "DEPOSIT", "SEND_TO" ], "cryptoNetwork": { "depositNetworks": [ "BITCOIN", "LIQUID" ], "withdrawalNetworks": [ "BITCOIN", "LIQUID" ] } }, { "asset": "USDT", "type": "FIAT", "totalAmount": "25000.0", "availableAmount": "19000.0", "availableActions": [ "CONVERT", "TRANSFER", "WITHDRAW", "DEPOSIT", "SEND_TO" ], "cryptoNetwork": { "depositNetworks": [], "withdrawalNetworks": [] } } ] } ``` ### User Wallet History `GET /public-api/wallet/v1/user/walletHistory` - Auth: read - Rate limit: query Query wallet history. Requires the `Read` permission. Query parameter rules: - It's **RECOMMENDED** to include `walletType` in the query. If it's not provided, the default will be to query spot wallet history only. - `walletName` is required if `walletType` is `ISOLATED`. - Only upgraded wallet allow query wallet history by using `walletType=UNIFIED`. - `historyTypes` should be encoded to a json array. - It will default to fetch data from the past 7 days if the `startTime` and the `endTime` are not set. - The `startTime` and `endTime` must be within the range of the past 120 days. - If your futures wallet has already been upgraded, querying future wallet history with `walletType=UNIFIED` for retrieving the post-upgrade history. - Querying wallet history by setting `walletType=CROSS` or `walletType=ISOLATED` will only return wallet history from before the futures wallet upgrade. For example, ``` // query spot wallet history since 7 days ago. .../public-api/wallet/v1/user/walletHistory?walletType=SPOT // query spot deposit and withdraw history since 7 days ago. .../public-api/wallet/v1/user/walletHistory?walletType=SPOT&historyTypes=["DEPOSIT","WITHDRAW"] // query spot deposit and withdraw history from 2023-01-01 to 2023-03-31. .../public-api/wallet/v1/user/walletHistory?walletType=SPOT&historyTypes=["DEPOSIT","WITHDRAW"]&startTime=1672531200000&endTime=1680307200000 // query BTC-PERP isolated wallet history since 7 days ago. .../public-api/wallet/v1/user/walletHistory?walletType=ISOLATED&walletName=ISOLATED@BTC-PERP-USDT ``` The detail which is a transaction on the blockchain will show `cryptoNetwork`, and once the transaction is completed, the `txId` will be displayed. #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | walletType | Object | No | | | walletName | String | No | The name of isolated wallet. Required when `walletType` is `ISOLATED` | | asset | String | No | Asset. Required when `walletType`=`SPOT`. | | historyTypes | Object[] | No | Specified query history types | | startTime | Double | No | Starting time (included). | | endTime | Double | No | Ending time (excluded). | | pageSize | Double | No | Page size for pagination | | currentPage | Double | No | Current page number for pagination | #### Response Content | Name | Type | Description | | --- | --- | --- | | data | Object[] | | ```json // Response (Deposit (crypto)) { "success": true, "code": 1, "msg": "Success", "time": 1624989977940, "data": [ { "transactionTime": 1678445104907, "type": "DEPOSIT", "walletName": "SPOT@", "asset": "ETH", "netAmount": "0.05", "amount": "0.05", "transactionRef": "2023031000000321", "status": "COMPLETED", "description": null, "fee": "0", "cryptoNetwork": "ETHEREUM", "toAddress": "0xF14BAc881e719934CEFccD1AC5dC5f814dfbCbF8", "confirmTimes": "(10/10)", "txId": "0x94ead563799ee341cb136c18dc9a71a231abaec820caa18c096febbe92da8709" } ] } ``` ```json // Response (Withdraw (crypto)) { "success": true, "code": 1, "msg": "Success", "time": 1624989977940, "data": [ { "transactionTime": 1678957494276, "type": "WITHDRAW", "walletName": "SPOT@", "asset": "USDT", "netAmount": "-30", "amount": "-30", "transactionRef": "2023031600000374", "status": "COMPLETED", "description": null, "fee": "6.429", "cryptoNetwork": "ERC20", "toAddress": "0xF14BAc881e719934CEFccD1AC5dC5f814dfbCbF8", "confirmTimes": "(15/15)", "txId": "0x0a8487a85f37d4995716419d7d7d7048c9625fab8cae69c235103a39ea789377" } ] } ``` ```json // Response (Send To) { "success": true, "code": 1, "msg": "Success", "time": 1624989977940, "data": [ { "transactionTime": 1679619604907, "type": "SEND", "walletName": "SPOT@", "asset": "USDT", "netAmount": "-1", "amount": "-1", "transactionRef": "2023024000000034", "status": "COMPLETED", "description": null, "fee": "0", "cryptoNetwork": null, "confirmTimes": null, "txId": null } ] } ``` ```json // Response (Others) { "success": true, "code": 1, "msg": "Success", "time": 1767580139575, "data": [ { "transactionTime": 1767580126422, "type": "TRANSFER_OUT", "walletName": "VIRTUAL|4@BTCPFC-USDT#1", "asset": "USDT", "netAmount": "-1.00950715", "amount": "-1.00950715", "transactionRef": "e6d252e8-eb64-48cd-81f0-e35ab453059d", "status": "COMPLETED", "description": "VIRTUAL|4@BTC-PERP-USD#1->CROSS@;CLOSE", "fee": "0", "cryptoNetwork": null, "toAddress": null, "confirmTimes": null, "txId": null }, { "transactionTime": 1767580126418, "type": "REALIZED_PNL", "walletName": "VIRTUAL|4@BTCPFC-USDT#1", "asset": "USDT", "netAmount": "-0.00019329", "amount": "-0.00019329", "transactionRef": "69491374-0a01-4341-92ed-5212eb36b0ff", "status": "COMPLETED", "description": "VIRTUAL|4@BTC-PERP-USD#1;TRADE", "fee": "0", "cryptoNetwork": null, "toAddress": null, "confirmTimes": null, "txId": null }, { "transactionTime": 1767578949884, "type": "TRANSFER_IN", "walletName": "VIRTUAL|4@BTCPFC-USDT#1", "asset": "USDT", "netAmount": "1", "amount": "1", "transactionRef": "1732d021-75dc-43cc-86b6-ebc767dd0969", "status": "COMPLETED", "description": "CROSS@->VIRTUAL|4@BTC-PERP-USD#1;TRANSFER", "fee": "0", "cryptoNetwork": null, "toAddress": null, "confirmTimes": null, "txId": null } ] } ``` ### Query Address `GET /public-api/wallet/v1/user/crypto/address` - Auth: wallet - Rate limit: query Query latest 10 active addresses. Requires the `Wallet` permission. #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | crypto | String | Yes | Crypto currency | | network | String | Yes | Crypto network | #### Response Content | Name | Type | Description | | --- | --- | --- | | data | Object[] | | ```json // Response (Common) { "success": true, "code": 1, "msg": "Success", "time": 1678700285000, "data": [ { "address": "0xc48fd69d0f4B521B7200345A67641aB4076828f2", "addressExtensionTypeName": null, "addressExtension": null, "createTime": 1678070826000 } ] } ``` ```json // Response (Has Extension) { "success": true, "code": 1, "msg": "Success", "time": 1678700285000, "data": [ { "address": "rHJhVRJGWGDqVX6cX8Ca1nPZMrkARE2VVx", "addressExtensionTypeName": "tag", "addressExtension": "533973779", "createTime": 1678070826000 } ] } ``` ### Create Address `POST /public-api/wallet/v1/user/crypto/address` - Auth: wallet - Rate limit: query Create address. Requires the `Wallet` permission. In the `Query address` API, you can retrieve the latest 10 active addresses that can be used for a specific crypto and network. If the number of addresses reaches 10, and none of these addresses were used for deposits, the service will restrict the creation of new addresses by returning a HTTP status code `400`. If you encounter this situation, use the existing addresses for deposits, then you'll be able to continue creating new addresses after. #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | crypto | String | Yes | Crypto currency | | network | String | Yes | Crypto network | | webhookUrl | String | No | If you want to receive notifications, fill in the 'webhookUrl' parameter. Then, notify us so we can whitelist the 'webhookUrl'. | #### Response Content | Name | Type | Description | | --- | --- | --- | | data | Object | | ```json // Request (USDT-ERC20) { "crypto": "USDT", "network": "ERC20" } ``` ```json // Request (USDT-ERC20-Webhook) { "crypto": "USDT", "network": "ERC20", "webhookUrl": "{URL for webhook}" } ``` ```json // Request (XRP-RIPPLE) { "crypto": "XRP", "network": "RIPPLE" } ``` ```json // Response (Common) { "success": true, "code": 1, "msg": "Success", "time": 1678070826000, "data": { "address": "0xc48fd69d0f4B521B7200345A67641aB4076828f2", "addressExtensionTypeName": null, "addressExtension": null, "createTime": 1678070826000 } } ``` ```json // Response (Has Extension) { "success": true, "code": 1, "msg": "Success", "time": 1678070826000, "data": { "address": "rHJhVRJGWGDqVX6cX8Ca1nPZMrkARE2VVx", "addressExtensionTypeName": "tag", "addressExtension": "533973779", "createTime": 1678070826000 } } ``` ### Delete Address `DELETE /public-api/wallet/v1/user/crypto/address` - Auth: wallet - Rate limit: query Rate Limit: 10 requests per 1 day based on IP. Delete a crypto address. Requires the `Wallet` permission. #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | crypto | String | Yes | Cryptocurrency | | network | String | Yes | Crypto network | | address | String | Yes | Crypto address | #### Response Content | Name | Type | Description | | --- | --- | --- | | data | Object | | ```json // Response { "success": true, "code": 1, "msg": "Success", "time": 1624989977940, "data": { "result": "OK" } } ``` ### Withdraw `POST /public-api/wallet/v1/user/crypto/withdraw` - Auth: withdraw - Rate limit: query Withdraw. Requires the `Withdraw` permission. Please refer to the [Crypto Networks](../query-crypto-networks) endpoint for fee calculation. The parameter `amountIncludeFee` affects how the fee is handled, with the differences as follows: - `amountIncludeFee` = true - `amount` is the amount to be deducted from user's wallet (i.e., including fee). The amount the user receives will be `amount - fee`. - `amountIncludeFee` = false - `amount` is the amount the user wants to receive (i.e., excluding fee). The amount deducted from user's wallet will be `amount + fee`. #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | crypto | String | Yes | Crypto to withdraw | | network | String | Yes | Crypto network to withdraw | | address | String | Yes | Withdrawal address | | addressExtension | String | No | Address extension (e.g. XRP tag, EOS memo). Required for networks where `needAddressExtension` is `true` in the [Crypto Networks](../query-crypto-networks) response | | amount | String | Yes | Withdraw amount | | amountIncludeFee | Boolean | No | Whether the fee is included in the amount or not. The default value is set to `true`. | | webhookUrl | String | No | If you wish to receive notifications, please fill in the 'webhookUrl' parameter. Also, kindly notify us so that we can add the 'webhookUrl' to the whitelist. | #### Response Content | Name | Type | Description | | --- | --- | --- | | data | Object | | ```json // Request (Common) { "crypto": "USDT", "network": "ERC20", "address": "0xc48fd69d0f4B521B7200345A67641aB4076828f2", "amount": "36" } ``` ```json // Request (Has Extension) { "crypto": "XRP", "network": "RIPPLE", "address": "rHJhVRJGWGDqVX6cX8Ca1nPZMrkARE2VVx", "addressExtension": "533973779", "amount": "20" } ``` ```json // Request (Has Webhook URL) { "crypto": "USDT", "network": "ERC20", "address": "0xc48fd69d0f4B521B7200345A67641aB4076828f2", "amount": "36", "webhookUrl": "{URL for webhook}" } ``` ```json // Response { "success": true, "code": 1, "msg": "Success", "time": 1624989977940, "data": { "transactionRef": "2025060500001", "orderId": "cca02a29-e47c-4ee7-9d42-28b850a843ec", "crypto": "USDT", "network": "ERC20", "address": "0xc48fd69d0f4B521B7200345A67641aB4076828f2", "addressExtension": null, "amountToAddress": "30.45", "fees": "5.55" } } ``` ### Send To (transfer to user) `POST /public-api/wallet/v1/user/assets/sendTo` - Auth: transfer - Rate limit: query Transfer to another user. Requires the `Transfer` permission. Invoke the API with either the `receiverUsername` or the `receiverUid` parameter. #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | asset | String | Yes | The asset to be transferred | | amount | String | Yes | The amount of asset to be transferred | | receiverUsername | String | No | Receiver's username. Required if `receiverUid` is not provided | | receiverUid | String | No | Receiver's unique ID. Required if `receiverUsername` is not provided | #### Response Content | Name | Type | Description | | --- | --- | --- | | data | Object | | ```json // Request { "asset": "BTC", "amount": "1", "receiverUsername": "tester", "receiverUid": "1234567890" } ``` ```json // Response { "success": true, "code": 1, "msg": "Success", "time": 1624989977940, "data": { "transactionRef": "2023010100000001", "asset": "BTC", "amount": "1", "receiverUsername": "tester", "receiverUid": "1234567890" } } ``` ### Convert `POST /public-api/wallet/v1/user/assets/convert` - Auth: wallet - Rate limit: query Asset conversion. Requires the `Wallet` permission. #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | fromAsset | String | Yes | From asset | | toAsset | String | Yes | To asset | | amount | String | Yes | The amount to convert from an asset, which should be greater than 0. The maximum integer digits is 14 and the maximum decimal places is 10. | #### Response Content | Name | Type | Description | | --- | --- | --- | | data | Object | | ```json // Request { "fromAsset": "BTC", "toAsset": "USDT", "amount": "1" } ``` ```json // Response { "success": true, "code": 1, "msg": "Success", "time": 1624989977940, "data": { "fromAsset": "BTC", "fromAmount": "1", "fromTransactionRef": "2025051000002", "toAsset": "USDT", "toAmount": "16000.0", "toTransactionRef": "2025051000001", "rate": "16000" } } ``` --- # Earn # Earn API > **New here? Set up authentication first.:** All Earn endpoints require authentication with `Read` or `Trading` permission. Read the [Authentication Guide](/authentication) before making your first call. --- ## Environment | Endpoint | Production | Testnet | |---|---|---| | Spot REST | https://api.btse.com/spot | https://testapi.btse.io/spot | --- ## Rate Limits Same as the Spot API — see [Authentication → Rate Limits](/authentication#rate-limits). ## Earn REST API REST base: `https://api.btse.com/spot` ### Query Investment Products `GET /api/v3.3/invest/products` - Auth: read - Rate limit: query Returns all available investment products. #### Response Content | Name | Type | Description | | --- | --- | --- | | id | String | Product ID | | name | String | Product name | | currency | String | Currency | | type | String | Product type (e.g. Flex) | | startDate | Long | Investment start date (Unix ms) | | interestStartDate | Long | Interest accrual start date (Unix ms) | | rates | Object[] | Interest rate tiers | | compounding | Boolean | Whether the product compounds | | autoRenewSupported | Boolean | Whether auto-renew is supported | | dailyLimit | Double | Daily investment limit | | minSize | Double | Minimum investment size | | incrementalSize | Double | Investment step size | ```json // Response [ { "id": "OPENETH00001", "name": "ETH Flex Savings", "currency": "ETH", "type": "Flex", "startDate": 1610685918000, "interestStartDate": 1610719200000, "rates": [ { "days": 1, "rate": 1.15 } ], "compounding": true, "autoRenewSupported": false, "dailyLimit": 10, "minSize": 1, "incrementalSize": 1 } ] ``` ### Deposit Investment `POST /api/v3.3/invest/deposit` - Auth: trading - Rate limit: query Deposit into an investment product. > **Note:** Check `minSize` and `incrementalSize` from [Query Investment Products](/api-explorer/earn/query-investment-products) before depositing. #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | productId | String | Yes | Product ID | | amount | Double | Yes | Investment amount | | renew | Boolean | Yes | Whether to auto-renew | | day | Integer | Yes | Investment duration in days | #### Response Content | Name | Type | Description | | --- | --- | --- | | success | Boolean | Whether the deposit was processed successfully | ```json // Request { "productId": "OPENETH00001", "amount": 1, "renew": false, "day": 1 } ``` ```json // Response { "success": true } ``` ### Renew Investment `POST /api/v3.3/invest/renew` - Auth: trading - Rate limit: query Update the auto-renew setting for an investment order. #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | orderId | Integer | Yes | Investment order ID | | autoRenew | Boolean | Yes | New auto-renew setting | #### Response Content | Name | Type | Description | | --- | --- | --- | | orderId | Integer | Investment order ID | | autoRenew | Boolean | Updated auto-renew status | ```json // Request { "orderId": 12345, "autoRenew": true } ``` ```json // Response { "orderId": 12345, "autoRenew": true } ``` ### Redeem Investment `POST /api/v3.3/invest/redeem` - Auth: trading - Rate limit: query Redeem (withdraw from) an investment order. > **Note:** Redemption may take time to process. Check `redemptionProcessing` in [Query Investment Orders](/api-explorer/earn/query-investment-orders) for status. #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | orderId | Integer | Yes | Investment order ID | | amount | Double | Yes | Amount to redeem | #### Response Content | Name | Type | Description | | --- | --- | --- | | success | Boolean | Whether the redemption was processed successfully | ```json // Request { "orderId": 12345, "amount": 1 } ``` ```json // Response { "success": true } ``` ### Query Investment Orders `GET /api/v3.3/invest/orders` - Auth: read - Rate limit: query Returns all active investment orders for the authenticated user. #### Response Content | Name | Type | Description | | --- | --- | --- | | id | Integer | Order ID | | name | String | Product name | | currency | String | Currency | | type | String | Product type | | rate | Double | Interest rate | | investAmt | Double | Invested amount | | interestEarned | Double | Interest earned to date | | nextInterestPayoutTime | Long | Next payout timestamp | | startTime | Long | Order start time | | endTime | Long | Order end time | | duration | Long | Duration in milliseconds | | payoutLockTime | Long | Lock time before payout | | autoRenew | Boolean | Auto-renew status | | compounding | Boolean | Whether compounding is active | | autoRenewSupported | Boolean | Whether product supports auto-renew | | redemptionProcessing | Boolean | Whether a redemption is currently processing | ```json // Response [] ``` ### Query Investment History `GET /api/v3.3/invest/history` - Auth: read - Rate limit: query Returns paginated investment transaction history. #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | pageNumber | Integer | No | Page number (1-based, default `1`) | | pageSize | Integer | No | Records per page (default `10`, max `50`) | #### Response Content | Name | Type | Description | | --- | --- | --- | | totalRows | Integer | Total records | | pageNumber | Integer | Current page | | pageSize | Integer | Records per page | | data | Object[] | History entries | ```json // Response { "totalRows": 0, "pageNumber": 1, "pageSize": 10, "data": [] } ``` --- # OTC # OTC API > **New here? Set up authentication first.:** Most OTC endpoints require `Trading` or `Read` permission. Read the [Authentication Guide](/authentication) before making your first call. --- ## Environment | Endpoint | Production | Testnet | |---|---|---| | OTC REST | https://api.btse.com | https://testapi.btse.io | | OTC WS | wss://ws.btse.com/ws/otc | wss://testws.btse.io/ws/otc | --- ## Rate Limits | Category | Per API | Per User | | --- | --- | --- | | **Query** | 15 req/s | 30 req/s | --- ## OTC Workflow The typical OTC transaction flow: 1. Call [Request Quote](../rest/request-quote) to get a price quote. 2. Then exchange responds with a quote. 3. **Accept:** Send the quote ID to [Accept Quote](../rest/accept-quote). Exchange executes the trade. 4. **Query status:** Use [Query Quote](../rest/get-quote) to check the state of any quote. 5. If Exchange rejects the quote (e.g. price moved), a new quote is returned with the rejection reason. --- ## OTC Status Codes | Code | Description | | --- | --- | | 30000 | OTC_ORDER_QUERY | | 30001 | OTC_ORDER_QUOTE | | 30007 | OTC_ORDER_COMPLETE_SUCCESS | | 30008 | OTC_ORDER_REQUOTE | | 8 | Insufficient balance | | 40001 | Service unavailable | | 40003 | Rejected | ## OTC REST API REST base: `https://api.btse.com` ### Get OTC Markets `GET /public-api/otc/v1/markets` - Auth: public - Rate limit: query Get OTC markets. This API can be publicly accessed (without any security headers) to get the result. If you access with `Read` permission authentication, the result will be much more accurate by account setting. When requesting a quote, the request size should be in the associated range: - If using `baseSize`, it should be in the range of [`minBaseSize`, `maxBaseSize`). - If using `quoteSize`, it should be in the range of [`minQuoteSizes[i]`, `maxQuoteSizes[i]`]. ```json // Response { "success": true, "code": 1, "msg": "Success", "time": 1624989977940, "data": [ { "asset": "BTC", "minBaseSize": "1.0", "maxBaseSize": "539236.0", "quoteCurrencies": [ "USDT", "ETH", "DOGE" ], "minQuoteSizes": [ "0.1", "0.000029", "0.69604" ], "maxQuoteSizes": [ "2500000", "287", "6960396" ] } ] } ``` ### Request Quote `POST /public-api/otc/v1/quotes` - Auth: trading - Rate limit: orders Request a quote for an OTC order. Requires the `Trading` permission. Choose either `baseSize` or `quoteSize` to submit — these two parameters cannot be sent at the same time. > **Important:** A new quote expires after **6 seconds**. Accept it before expiry. #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | side | String | Yes | Transaction side — `BUY` · `SELL` | | baseCurrency | String | Yes | Base currency | | quoteCurrency | String | Yes | Quote currency | | baseSize | String | No | Size in `baseCurrency`. Mutually exclusive with `quoteSize`. | | quoteSize | String | No | Size in `quoteCurrency`. Mutually exclusive with `baseSize`. | | clOrderId | String | No | Client order ID assigned by the client | ```json // Request (Buy) { "side": "BUY", "baseCurrency": "BTC", "quoteCurrency": "USDT", "baseSize": "15.0", "clOrderId": "test-0001" } ``` ```json // Request (Buy (with quote size)) { "side": "BUY", "baseCurrency": "BTC", "quoteCurrency": "USDT", "quoteSize": "340000.0", "clOrderId": "test-0002" } ``` ```json // Request (Sell) { "side": "SELL", "baseCurrency": "BTC", "quoteCurrency": "ETH", "baseSize": "15.0", "clOrderId": "test-0003" } ``` ```json // Request (Sell (with quote size)) { "side": "SELL", "baseCurrency": "BTC", "quoteCurrency": "ETH", "quoteSize": "300.0", "clOrderId": "test-0004" } ``` ```json // Response (Buy) { "success": true, "code": 1, "msg": "Success", "time": 1673325777227, "data": { "quoteId": "0001-0001-0001-0001", "clOrderId": "test-0001", "side": "BUY", "baseSize": "15.0", "baseCurrency": "BTC", "quoteSize": "257735.09868", "quoteCurrency": "USDT", "ratio": "17182.33991211", "expireTime": 1673325783227, "ttl": 6, "status": "NEW_QUOTE" } } ``` ```json // Response (Sell (with quote size)) { "success": true, "code": 1, "msg": "Success", "time": 1673325777227, "data": { "quoteId": "0004-0004-0004-0004", "clOrderId": "test-0004", "side": "SELL", "baseSize": "23.0325468695", "baseCurrency": "BTC", "quoteSize": "300.0", "quoteCurrency": "ETH", "ratio": "13.025046761", "expireTime": 1673325783227, "ttl": 6, "status": "NEW_QUOTE" } } ``` ### Query Quote `GET /public-api/otc/v1/quotes` - Auth: read - Rate limit: query Query a quote for an OTC order. Requires the `Read` permission. #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | quoteId | String | Yes | Quote ID | ```json // Response (New Quote) { "success": true, "code": 1, "msg": "Success", "time": 1673325777227, "data": { "quoteId": "0001-0001-0001-0001", "clOrderId": "test-0001", "side": "BUY", "baseSize": "15.0", "baseCurrency": "BTC", "quoteSize": "257735.09868", "quoteCurrency": "USDT", "ratio": "17182.33991211", "expireTime": 1673325783227, "ttl": 6, "status": "NEW_QUOTE" } } ``` ```json // Response (Accepted Quote) { "success": true, "code": 1, "msg": "Success", "time": 1673325778012, "data": { "quoteId": "0001-0001-0001-0001", "clOrderId": "test-0001", "side": "BUY", "baseSize": "15.0", "baseCurrency": "BTC", "quoteSize": "257735.09868", "quoteCurrency": "USDT", "ratio": "17182.33991211", "expireTime": 1673325783227, "status": "COMPLETED" } } ``` ```json // Response (Quote Was Expired) { "success": true, "code": 1, "msg": "Success", "time": 1673325784033, "data": { "quoteId": "0002-0002-0002-0002", "clOrderId": "test-0002", "side": "BUY", "baseSize": "19.787758928", "baseCurrency": "BTC", "quoteSize": "340000.0", "quoteCurrency": "USDT", "ratio": "17182.33991211", "expireTime": 1673325783227, "ttl": 0, "status": "EXPIRED" } } ``` ```json // Response (Requote) { "success": true, "code": 1, "msg": "Success", "time": 1673506479119, "data": { "quoteId": "2000-2000-2000-2000", "clOrderId": "test-0002", "side": "BUY", "baseSize": "18.624803705", "baseCurrency": "BTC", "quoteSize": "340000.0", "quoteCurrency": "USDT", "ratio": "18255.225955", "expireTime": 1673506485119, "ttl": 6, "status": "REQUOTE" } } ``` ### Accept Quote `POST /public-api/otc/v1/quotes/accept` - Auth: trading - Rate limit: orders Accept a quote for an OTC order. Requires the `Trading` permission. After requesting a quote with `baseSize`, you can use `baseSize` to partially accept the quote. Likewise, you can use `quoteSize` to partially accept a quote that was requested with `quoteSize`. In any of the scenarios below, the original `baseSize` and `quoteSize` will be accepted in full without any message: 1. Send neither `baseSize` nor `quoteSize`. 2. Send `baseSize` higher than the quoted amount. 3. Send `quoteSize` higher than the quoted amount. 4. Send `baseSize` when the quote was requested with `quoteSize`. 5. Send `quoteSize` when the quote was requested with `baseSize`. #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | quoteId | String | Yes | Quote ID returned from Request Quote | | baseSize | String | No | Partial `baseCurrency` amount to accept. Omit both `baseSize` and `quoteSize` to accept the full quoted amount. | | quoteSize | String | No | Partial `quoteCurrency` amount to accept. Omit both `baseSize` and `quoteSize` to accept the full quoted amount. | ```json // Request (Common) { "quoteId": "0001-0001-0001-0001" } ``` ```json // Request (Accept with specified baseSize) { "quoteId": "0003-0003-0003-0003", "baseSize": "10.0" } ``` ```json // Request (Accept with specified quoteSize) { "quoteId": "0004-0004-0004-0004", "quoteSize": "200.0" } ``` ```json // Response (Common) { "success": true, "code": 1, "msg": "Success", "time": 1673325778012, "data": { "quoteId": "0001-0001-0001-0001", "clOrderId": "test-0001", "side": "BUY", "baseSize": "15.0", "baseCurrency": "BTC", "quoteSize": "257735.09868", "quoteCurrency": "USDT", "ratio": "17182.33991211", "expireTime": 1673325783227, "status": "COMPLETED" } } ``` ```json // Response (Accept with specified baseSize) { "success": true, "code": 1, "msg": "Success", "time": 1673325777227, "data": { "quoteId": "0003-0003-0003-0003", "clOrderId": "test-0003", "side": "SELL", "baseSize": "10.0", "baseCurrency": "BTC", "quoteSize": "130.25046761", "quoteCurrency": "ETH", "ratio": "13.025046761", "expireTime": 1673325783227, "status": "COMPLETED" } } ``` ```json // Response (Accept with specified quoteSize) { "success": true, "code": 1, "msg": "Success", "time": 1673325777227, "data": { "quoteId": "0004-0004-0004-0004", "clOrderId": "test-0004", "side": "SELL", "baseSize": "15.3550312463", "baseCurrency": "BTC", "quoteSize": "200.0", "quoteCurrency": "ETH", "ratio": "13.025046761", "expireTime": 1673325783227, "status": "COMPLETED" } } ``` ```json // Response (Accept but expired or ratio change) { "success": true, "code": 1, "msg": "Success", "time": 1673506479119, "data": { "quoteId": "2000-2000-2000-2000", "clOrderId": "test-0002", "side": "BUY", "baseSize": "18.624803705", "baseCurrency": "BTC", "quoteSize": "340000.0", "quoteCurrency": "USDT", "ratio": "18255.225955", "expireTime": 1673506485119, "ttl": 6, "status": "REQUOTE" } } ``` ## OTC WebSocket API WebSocket: `wss://ws.btse.com/ws/otc` ### WebSocket Authentication Endpoint: `wss://ws.btse.com/ws/otc` · Testnet: `wss://testws.btse.io/ws/otc` Authenticate the session to access private topics (Quote Stream). Signature: `HMAC-SHA384(secret, "/ws/otc" + nonce)` **Signature example:** ```shell echo -n "/ws/otc1624985375123" | openssl dgst -sha384 -hmac "" ``` Args array: - Index 0: API key - Index 1: Current timestamp (nonce) - Index 2: HMAC-SHA384 hex signature #### Request Parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | op | String | Yes | Operation — fixed value `authKeyExpires` | | args | String[] | Yes | [API_KEY, NONCE, HMAC-SHA384_SIGNATURE] | ```json // Request { "op": "authKeyExpires", "args": [ "", "", "" ] } ``` ### Quote Stream Endpoint: `wss://ws.btse.com/ws/otc` · Testnet: `wss://testws.btse.io/ws/otc` Subscribe to continuous OTC price quotes. Prices are pushed as market conditions change. Requires WebSocket authentication. To **accept** a quote, use the `buyQuoteId` or `sellQuoteId` from the stream with the Accept Quote REST endpoint. **REST vs WebSocket — when to use which:** - **REST `POST /public-api/otc/v1/quotes`** — request a single quote with a 5-second TTL. This is the standard one-shot RFQ workflow. - **WebSocket `quote` stream (this channel)** — subscribe to a continuous price feed for high-frequency consumers. Independent of the REST RFQ. **Subscribe payload differs from spot/futures.** OTC uses `op: quote` with a top-level `symbol` (and optional `side`, `quantity`); it does **not** use the spot/futures `{op: "subscribe", args: [...]}` form. Sending a spot/futures-style payload is silently acked with `{event: "subscribe", channel: []}` and produces no data. ```jsonc // ✅ OTC subscribe { "op": "quote", "symbol": "BTC-USD", "quantity": { "quantity": 1, "currency": "BTC" } } // ❌ Spot/futures style — does NOT work for OTC { "op": "subscribe", "args": ["quote"] } ``` **Operations:** - `quote`: subscribe to quote stream - `unsubscribe-quote`: unsubscribe from a specific quote stream - `unsubscribe-quote-all`: unsubscribe from all quote streams --- # FIX # FIX API FIX (Financial Information eXchange) is a standard electronic messaging protocol for placing orders, receiving order updates, and cancelling orders. BTSE's FIX API is based on the **FIX 4.2** specification. --- ## Connection | Environment | Host | Port | | --- | --- | --- | | Production | tcp+ssl://fix.btse.com | `9876` | | Testnet | tcp+ssl://fix.btse.io | `9876` | Sessions for Spot and Futures are **separated** using `SenderSubID`: | SenderSubID | Market | | --- | --- | | `SPOT` | Spot trading | | `FUTURES` | Futures trading | --- ## Rate Limits | Group | Message Types | Limit | | --- | --- | --- | | Auth | Logon (A), Logout (5) | 2 per second | | General | All other messages | 30 per second | When a rate limit is exceeded, a **Business Message Reject (j)** is returned with `BusinessRejectReason: 4` (application not available). --- ## Common Request Attributes All client messages must include these fields: | Tag | Name | Description | | --- | --- | --- | | 8 | BeginString | Must be `FIX.4.2` | | 9 | BodyLength | Body length in bytes | | 34 | MsgSeqNum | Sequence number — starts at 1, increments per message. Duplicates/out-of-order are rejected. Reset on new connection. | | 35 | MsgType | Message type | | 49 | SenderCompID | Client API key | | 50 | SenderSubID | `SPOT` or `FUTURES` | | 52 | SendingTime | Message send time (e.g. `20220916-07:29:07`) | | 56 | TargetCompID | Must be `BTSE` | | 10 | CheckSum | Message checksum | --- ## Messages ### Logon (A) Initiates a FIX session. Must be the first message sent. Only one session per connection. | Tag | Name | Value | Description | | --- | --- | --- | --- | | 35 | MsgType | A | | | 95 | RawDataLength | 96 | Length of RawData field | | 96 | RawData | (hex) | HMAC-SHA384 signature. Concatenate with FIX field separator (`0x01`): `SendingTime` + `MsgType` + `MsgSeqNum` + `SenderCompID` + `TargetCompID`, then HMAC-SHA384 with API secret | | 98 | EncryptMethod | 0 | Must be `0` (None) | | 108 | HeartBtInt | 30 | Heartbeat interval in seconds. Send heartbeats every N−5 seconds | | 141 | ResetSeqNumFlag | Y | Must be `Y` | | 5001 | ApplyNewSymbolName | Y | Futures only. Enables new symbol names (e.g. `BTC-PERP`). Without this field, only old names (e.g. `BTCPFC`) are accepted | > Signature computation ```java // Concatenate fields with FIX separator (0x01): // SendingTime + 0x01 + MsgType + 0x01 + MsgSeqNum // + 0x01 + SenderCompID + 0x01 + TargetCompID // // Then: HMAC-SHA384(apiSecret, dataRaw) // Hex-encode the result → RawData (tag 96) ``` --- ### Heartbeat (0) Keeps the session alive. Send every N−5 seconds where N is the heartbeat interval set in Logon. | Tag | Name | Value | Description | | --- | --- | --- | --- | | 35 | MsgType | 0 | | | 112 | TestReqID | 123 | If responding to a TestRequest, copy from the TestRequest | --- ### Test Request (1) Requests a heartbeat response. | Tag | Name | Value | Description | | --- | --- | --- | --- | | 35 | MsgType | 1 | | | 112 | TestReqID | 123 | Arbitrary string — echoed back in a Heartbeat | --- ### Logout (5) Terminates the session. The other side responds with another Logout message; connection closes afterwards. | Tag | Name | Value | | --- | --- | --- | | 35 | MsgType | 5 | --- ### New Order Single (D) Submits a new order. Supports Market and Limit orders. | Tag | Name | Value | Description | | --- | --- | --- | --- | | 35 | MsgType | D | | | 21 | HandlInst | 1 | Must be `1` (AutomatedExecutionNoIntervention) | | 11 | ClOrdID | order123 | Unique client-selected order identifier | | 55 | Symbol | BTC-USD | Symbol name | | 40 | OrdType | 2 | `1`: Market · `2`: Limit | | 38 | OrderQty | 1.1 | Order size in base units (required for Limit and Market sell) | | 44 | Price | 18000 | Limit or Market buy price (required for Limit and Market buy) | | 54 | Side | 1 | `1`: Buy · `2`: Sell | | 59 | TimeInForce | 1 | `1`: GTC · `3`: IOC · `4`: FOK · `a`: 30s · `b`: 5min · `c`: 1h · `d`: 12h · `e`: 1 week · `f`: 1 month | | 18 | ExecInst | 6 | Optional: `E`: reduce only · `6`: post only | | 5002 | PositionId | BTC-PERP-USDT | Optional. Position to reduce (Futures) | | 5003 | PositionMode | ONEWAY | Optional. `ONEWAY` (default) · `HEDGE` · `ISOLATED` | On acceptance, an [Execution Report (8)](#execution-report-8) is returned. --- ### Order Cancel Request (F) Requests cancellation of an open order. Provide only one of `OrderID` or `OrigClOrdID`. | Tag | Name | Value | Description | | --- | --- | --- | --- | | 35 | MsgType | F | | | 37 | OrderID | order123 | System-assigned order ID | | 41 | OrigClOrdID | order123 | Client-assigned order ID | | 55 | Symbol | BTC-USD | Symbol name | Returns an [Execution Report (8)](#execution-report-8) on success, or [Order Cancel Reject (9)](#order-cancel-reject-9) on failure. --- ### Order Cancel Reject (9) Sent by the server when an Order Cancel Request fails. | Tag | Name | Value | Description | | --- | --- | --- | --- | | 35 | MsgType | 9 | | | 37 | OrderID | order123 | Copied from cancel request (if provided) | | 41 | OrigClOrdID | order123 | Copied from cancel request (if provided) | | 39 | OrdStatus | 4 | `4`: Already cancelled · `8`: Rejection | | 102 | CxlRejReason | 1 | `1`: Unknown order · `99`: Other | | 434 | CxlRejResponseTo | 1 | Always `1` | --- ### Order Status Request (H) Queries order status. Provide only one of `OrderID` or `OrigClOrdID`. Use `OrderID = *` to request all pending orders. | Tag | Name | Value | Description | | --- | --- | --- | --- | | 35 | MsgType | H | | | 37 | OrderID | order123 | Order to query, or `*` for all open orders | | 41 | OrigClOrdID | order123 | Client order ID | | 54 | Side | 1 | `1`: Buy · `2`: Sell | | 55 | Symbol | BTC-USD | Symbol name | Returns an [Execution Report (8)](#execution-report-8) with `ExecType=I`. When no open orders exist, `Text (58)` returns `"No open orders"`. --- ### Execution Report (8) Sent by the server when: an order is filled, the order status changes, or in response to New Order Single, Order Cancel Request, or Order Status Request. | Tag | Name | Value | Description | | --- | --- | --- | --- | | 11 | ClOrdID | order123 | Client order ID | | 12 | Commission | 0.002 | Trade fee (fill only) | | 13 | CommType | 3 | Always `3` | | 14 | CumQty | 0.4 | Cumulative filled quantity | | 17 | ExecID | d840c8… | Execution ID | | 31 | LastPx | 7999.25 | Fill price (fill only) | | 32 | LastShares | 0.4 | Fill quantity (fill only) | | 35 | MsgType | 8 | | | 37 | OrderID | d840c8… | System order ID | | 38 | OrderQty | 1.2 | Original order quantity | | 39 | OrdStatus | 0 | Current order status (see table below) | | 44 | Price | 8000 | Original order price | | 54 | Side | 1 | `1`: Buy · `2`: Sell | | 55 | Symbol | BTC-USD | Symbol name | | 58 | Text | text | Rejection reason description | | 59 | TimeInForce | 6 | `1`: GTC · `3`: IOC · `4`: FOK · `6`: GTD · `a`–`f`: custom durations | | 60 | TransactTime | 20190525-08:26:38.989 | Order update timestamp | | 103 | OrdRejReason | 11 | `11`: UNSUPPORTED_ORDER_CHARACTERISTIC — see Text(58) for details | | 126 | ExpireTime | 20190525-08:26:38.989 | Required when TimeInForce=GTD and ExpireDate not set | | 150 | ExecType | 1 | Reason for this message (see table below) | | 151 | LeavesQty | 0.8 | Remaining open quantity | | 1057 | AggressorIndicator | Y | `Y`: taker · `N`: maker (fill only) | | 5000 | Liquidation | Y | `Y`: on-market liquidation order | **ExecType values:** | ExecType | Description | | --- | --- | | 0 | New order | | 1 | Partially filled | | 3 | Fully filled | | 4 | Order cancelled | | 5 | Order amended | | 7 | Order refund (self-trade) | | 8 | Rejected (response to New Order Single) | | I | Order status (response to Order Status Request) | **OrdStatus values:** | OrdStatus | Description | | --- | --- | | 0 | New order | | 1 | Partially filled | | 3 | Fully filled | | 4 | Cancelled | | 5 | Amended | | 7 | Refunded | | 8 | Rejected | --- ### Reject (3) Sent by the server in response to an invalid message. | Tag | Name | Value | Description | | --- | --- | --- | --- | | 35 | MsgType | 3 | | | 45 | RefSeqNum | 2 | Sequence number of rejected message | | 371 | RefTagID | 38 | Tag number of the rejected field | | 372 | RefMsgType | D | Message type of the rejected message | | 58 | Text | Missing quantity | Human-readable rejection reason | | 373 | SessionRejectReason | 1 | Code identifying rejection reason | --- ### Business Message Reject (j) Sent when a rate limit is exceeded. | Tag | Name | Example | Description | | --- | --- | --- | --- | | 35 | MsgType | j | | | 45 | RefSeqNum | 11 | Sequence number of rejected message | | 49 | SenderCompID | BTSE | Always `BTSE` | | 56 | TargetCompID | c123...05a | Client API key | | 57 | TargetSubID | SPOT | `SPOT` or `FUTURES` | | 58 | Text | exceeding rate limit | Details | | 372 | RefMsgType | F | Message type of rejected message | | 380 | BusinessRejectReason | 4 | Always `4`: application not available |