# Best Practices Guide

## Product Configuration

You can get the exchange's product configuration via `GET /api/v3/public/instruments`.

## Market Data

You can receive real-time market data updates from WebSocket channels.

**Depth channels**:

- `books` default push frequency: `50ms`
- `books5` default push frequency: `10ms`
- `books50` default push frequency: `20ms`
- `books1` default push frequency: `1ms`

- `books` provides full depth data — the first push is a `snapshot`, subsequent pushes are incremental `update`s.
- `books1` provides level-1 depth data — every push is a `snapshot`.
- `books5` provides level-5 depth data — every push is a `snapshot`.
- `books50` provides level-50 depth data — every push is a `snapshot`.

No new snapshot is sent when the order book has not changed.

Order book data is updated and pushed on order events. In most cases, users receive the same order book data across all WebSocket connections and channels. When there is no order book change for a long time, the system triggers a compensating push via a scheduled task; in this case, sequence numbers may differ slightly across servers due to clock differences.

The system pushes the latest state of the order book. When the depth changes (including multiple changes within a short period, e.g. A→B→A), the system sends an update reflecting the final state.

## Configuring Accounts and Sub-accounts

After creating an API key, you can configure your account via the API or the website before trading.

### Account Configuration

You can check your current account configuration via the following REST API:

`GET  /api/v3/account/settings`

The API returns the account mode, position mode, asset mode, and many other account-related fields.

### Account Mode

The Unified Trading Account system provides three account modes: Spot Mode (coming soon), Basic Mode, and Pro Mode.

| Account Mode | Tradable Products | Margin Support | Eligibility |
|:-------|:-------------------------|:--------------------------------------------------|:-----------------------------------|
| Spot Mode | Spot trading only; no leverage or futures trading | No margin used | Users under regulated entities: applied by default |
| Basic Mode | Spot, USDT-margined futures, USDC futures | Under Basic Mode, USDT- and USDC-denominated pairs share the same margin, and PnL can offset across these products | Users registered under global entities: applied by default. Requires completing a questionnaire to activate |
| Pro Mode | Spot, margin, USDT-margined futures, USDC futures, coin-margined | All asset types support cross-margining | Account equity ≥ 1,000 USD: complete a questionnaire to activate |

Under **Pro Mode**, assets across all product types can be used as shared margin, and PnL can offset each other.

The account mode can be changed via the API or on the website.

### Position Mode

The exchange currently supports two position modes.

| Position Mode | Description |
|:---------|:------|
| One-way Position Mode | Only long or short positions can be held. The exchange automatically opens/closes positions based on the specified position size |
| Hedge Position Mode | Long and short positions can be held simultaneously |

You can set the position mode via the following REST API (all positions must be closed and there must be no open orders beforehand):

`POST /api/v3/account/set-hold-mode`


### Cross/Isolated Margin Mode

The Unified Trading Account system supports both cross margin mode and isolated margin mode.

### Getting Leverage

You can get the leverage via the following REST API:

`GET /api/v3/account/settings`

Currently there is no global leverage setting — leverage must be set separately for each trading pair.

**Product Types**:

| Position Mode | Product Type   | Margin Mode | Level  |
|:---------|:--------|:-----------|:-----|
| One-way | USDT-margined futures  | Cross | Trading pair |
| One-way | USDC futures | Cross | Trading pair |
| One-way | Coin-margined perpetual  | Cross | Trading pair |
| Hedge | USDT-margined futures  | Cross | Trading pair |
| Hedge | USDT perpetual | Cross | Trading pair    |
| Hedge | USDC futures | Cross | Trading pair |

### Setting Leverage

After getting the leverage, you can set it as needed:

`POST /api/v3/account/set-leverage`

You can use the two APIs above to pre-set the leverage for each trading pair before trading.

#### Example

Assume the following setup and requirements:

- Account Mode: Pro Mode
- Position Mode: One-way Position Mode
- Product to set leverage to 3 for:
  - BTCUSDT
- The above product uses cross margin mode

For spot margin, leverage is set at the coin level, so you can set it separately for BTC and USDT.

Example request body to set BTC leverage to 3 (applies to selling BTCUSDT):

```json
{
  "leverage": "3.0",
  "coin": "BTC",
  "productType": "MARGIN"
}
```

The request body for USDT is similar.

Next, set the leverage for the BTCUSDT perpetual futures contract.

Since this is a USDT-margined futures contract, it needs to be set separately:

```json
{
  "leverage": "3",
  "symbol": "BTCUSDT",
  "productType": "usdt-futures"
}
```

After sending the above REST API request, the leverage setting for these products is complete.

## Order Management

### Subscribing to the Order Channel

Before placing an order, you should first subscribe to the order channel via WebSocket so you can monitor order status (e.g. pending, fully filled) and take corresponding actions (e.g. placing a new order after a fill).

The order channel supports subscriptions at various dimensions. To subscribe to BTCUSDT order data, after connecting and logging in to the private WebSocket, send either of the requests below:

| Subscription Dimension | Product Type |
|:---------|:---------|
| **Request** | `{"op": "subscribe", "args": [{"instType": "UTA", "topic": "order"}]}` |
| **Success Response** | `{"event": "subscribe", "arg": {"topic": "order", "instType": "UTA"}}`  |

**Note**: The order channel does not push full data on first subscription — it only pushes updates for an order when its status changes (e.g. from pending to successfully canceled).

In other words, you cannot get current order data at the moment you subscribe to the order channel. To get data for orders that were unfilled before you subscribed, use the following REST API:

`GET /api/v3/trade/unfilled-orders`

### Placing Orders

To make it easier for the system to identify orders, we recommend that you fill in a custom order ID (the `clientOid` field) when placing an order. The custom order ID must match `^[0-9A-Za-z_:#\\-+\\s]{1,32}$`.

The `clientOid` uniqueness check applies only to all open orders, but we still recommend always using a unique `clientOid` to make troubleshooting easier.

In this example, we'll set the `clientOid` field to `testBTC0123`.

After subscribing to the order channel, you can prepare to place a BTCUSDT order.

You can place orders via REST or WebSocket.

#### REST API

You can place an order via the following REST API; the server returns an order ID (`orderId`) after receiving the request.

<div className="table-wrap-text">

| REST API  | `POST /api/v3/trade/place-order` |
|:----------|:----------------------------------|
| **Request Body**   | `{"category":"SPOT","symbol":"BGBUSDT","orderType":"limit","qty":"123","price":"1.11","side":"buy","posSide":"long","timeInForce":"gtc","reduceOnly":"no"}` |
| **Success Response**  | `{"code": "00000", "msg": "success", "requestTime": 1695806875837, "data": {"clientOid": "testBTC0123", "orderId": "1234567890"}}` |

</div>

**Note**: This only means the exchange has successfully received the request and assigned an order ID to the order. The order may not have reached the matching engine yet, so you need to further check the order status to confirm.

#### WebSocket

You can also place orders via WebSocket, which is theoretically more efficient and resource-saving than REST.

Since WebSocket operations are asynchronous, you need to provide a message ID (`id`) to identify the response.

After logging in to the private WebSocket, send the following WebSocket message:

```json
{
  "op": "trade",
  "id": "testBTC0123",
  "category": "spot",
  "topic": "place-order",
  "args": [
    {
      "orderType": "limit",
      "price": "100",
      "qty": "0.1",
      "side": "buy",
      "symbol": "BTCUSDT",
      "timeInForce": "gtc",
      "clientOid": "testBTC0123"
    }
  ]
}
```

After receiving the request, the server returns the result along with the message ID (i.e. `testBTC0123`), together with the order ID assigned by the exchange (`orderId`):

```json
{
  "event": "trade",
  "id": "testBTC0123",
  "category": "spot",
  "topic": "place-order",
  "args": [
    {
      "symbol": "BTCUSDT",
      "orderId": "1234567890",
      "clientOid": "testBTC0123",
      "cTime": "1750034397008"
    }
  ],
  "code": "0",
  "msg": "success",
  "ts": "1750034397076"
}
```

**Note**: This only means the exchange has successfully received the request and assigned an order ID to the order. The order may not have reached the matching engine yet, so you need to further check the order status to confirm.

### Checking Order Status

After placing an order, if it does not return an error (`"code": "0"`) and enters the matching engine, you will receive a push on the WebSocket order channel with the order status `new`.

After the order is fully filled, you will receive a push with the order status changed to `filled`, along with other fill-related fields.

If the order is partially or fully filled, WebSocket will return `state = partially_filled` and `filled` respectively.

For Immediate-or-Cancel (IOC), Fill-or-Kill (FOK), and post-only orders, these orders may be rejected by the matching engine, and you will receive a `canceled` status.

Your order may be canceled by the system for various reasons, such as liquidation or self-trade. You can refer to `cancelSource` to determine the reason the order was canceled.

An order's terminal status is either `canceled` or `filled`.

Each fill of an order is assigned a trade ID (`tradeId`) by the system.

**Possible Order Statuses**:

| Scenario | Status Change                                          |
|:------|:-----------------------------------------------|
| Rejected at entry | `code` is non-zero, no update pushed on the WebSocket order channel |
| Order placed and immediately fully filled | `new` → `filled`                              |
| Order placed and immediately filled via multiple trades | `new` → `partially_filled` → ... → `filled`  |
| Order placed and immediately canceled by the matching engine (e.g. IOC, FOK, post-only) | `canceled` (cancellation reason can be queried via `cancelSource`) |
| IOC order, partially filled then canceled by the system due to insufficient price depth | `partially_filled` → `canceled`      |

### Modifying Orders

The order modification API supports modification for all product types, allowing you to modify an order's price (`price` field) and/or quantity (`qty` field). The API also provides an `autoCancel` parameter to set whether the order is automatically canceled if the modification fails.

**REST**:

`POST /api/v3/trade/modify-order`

**WebSocket business action request parameters**:

```json
{
    "args": [
        {
            "autoCancel": "yes",
            "clientOid": "135423791666666666",
            "orderId": "1354237910666666666",
            "price": "5",
            "qty": "2",
            "symbol":"BTCUSDT"
        }
    ],
    "id": "ae5ea6df-215f-4750-a700-d487d03ac020",
    "op": "trade",
    "category": "usdt-futures",
    "topic": "modify-order"
}
```

Similar to placing an order, you should receive a success response from the corresponding REST/WebSocket call, and then receive an order update push on the WebSocket order channel.

**Note**: An order cannot be modified once it is fully filled or successfully canceled.

A success response only means the exchange has received the request — refer to the WebSocket order channel to confirm.

### Canceling Orders

You can cancel orders via REST or WebSocket in a similar way.

**REST**:

`POST /api/v3/trade/cancel-order`

**WebSocket business action request parameters**:

```json
{
    "args": [
        {
            "orderId": "xxxxxxxxxxxxxxxxxx",
            "clientOid": "xxxxxxxxxxxxxxxxxx"
        }
    ],
    "id": "c8a1999c-1f82-409d-870e-f40ff49c4072",
    "op": "trade",
    "topic": "cancel-order"
}
```

Similarly, you should receive a success response from the corresponding REST/WebSocket call. The order is only considered successfully canceled once you receive a push with status `canceled` on the WebSocket order channel.

**Note**: An order cannot be canceled once it is fully filled or successfully canceled.

A success response only means the exchange has received the request — refer to the WebSocket order channel to confirm.

### Batch Operations

Placing, modifying, and canceling orders all support batch operations, up to 20 orders per batch.

**REST**:

| Operation | API |
|:------|:-----|
| Place | `POST /api/v3/trade/place-batch` |
| Modify | `POST /api/v3/trade/batch-modify-order` |
| Cancel | `POST /api/v3/trade/cancel-batch` |

**WebSocket business action parameters**:

| Operation | Parameter |
|:------|:------|
| Place | `"topic": "batch-place"` |
| Modify | `"topic": "batch-modify"` |
| Cancel | `"topic": "batch-cancel"` |

Batch operations allow partial success. After receiving the response, check the `code` and `msg` fields for each order in the result to determine the outcome.

### Order Timestamps

Order data includes several timestamps for tracking order status and latency.

| Field            | Description                                 |
|:---------------|:------------------------------------|
| `createdTime` | The time the order management system created the order after risk checks |
| `updatedTime` | The last time the order management system updated the order. Updated after order modification, fills, and cancellation |
| `ts`          | The time the WebSocket gateway pushed the message                    |

## Pagination

Bitget provides pagination to help you easily retrieve the data you want from large datasets. The related request parameters are as follows:

| Parameter | Type | Required | Description |
|:--------|:------|:---------|:------|
| `cursor` | String | No | Used for pagination. Omit on the first query. For the second and later pages, use the smallest ID returned by the previous query; the result will return data smaller than that value |
| `startTime` | String | No | Start time, Unix timestamp (milliseconds) |
| `endTime` | String | No | End time, Unix timestamp (milliseconds) |
| `limit` | String | No | Number of results to return, maximum 100, default 100 |


The trading APIs that support pagination are listed below:

- `GET /api/v3/trade/unfilled-orders` - Get current orders
- `GET /api/v3/trade/history-orders` - Get order history
- `GET /api/v3/trade/fills` - Get fill details
- `GET /api/v3/account/financial-records` - Get financial records

## Self-Trade Prevention

The trading system enforces mandatory self-trade prevention (STP) at the master account level — no account under the same master account, including the master account itself and all sub-accounts, can self-trade. The default STP mode for an order is Cancel Maker; you can also specify an order's STP mode via the `stpMode` parameter of the order-placement API.

Bitget supports 4 STP modes (`stpMode`): `none`, `cancel_maker`, `cancel_taker`, and `cancel_both`.

**Note**: Mandatory self-trade prevention applies to all users, all order types, and all order-book trading products.

### Self-Trade Prevention Modes

Bitget provides users with four modes that define how self-trading is prevented. The STP outcome depends on the taker order's STP mode; the STP mode of existing orders in the order book (maker side) is not considered.

#### none mode

The order is not restricted by the STP mechanism — the system does not compare UIDs, and the trade executes normally.

#### cancel_taker mode

Cancels the taker order and keeps the maker order.

#### cancel_maker mode

Cancels the maker order and keeps the taker order.

#### cancel_both mode

Cancels both the taker and maker orders.

## Unified Account and Positions

### Account

#### WebSocket Subscription

We recommend subscribing to the account channel via WebSocket to receive account updates.

This endpoint returns the value of your assets denominated in USD, along with other parameters that are continuously updated due to price changes. Bitget sends update data to you when valuations change.

Example request and response after connecting to and logging in to the private WebSocket:

| Subscription Dimension       | Account                                                                          |
|:------------|:-----------------------------------------------------------------------------|
| **Request**     | `{"op": "subscribe","args": [ { "instType": "UTA","topic": "account" } ] }` |
| **Success Response**   | `{"event": "subscribe","arg": {"instType": "UTA","topic": "account"} }`     |

#### Initial Full Push on Subscription

Unlike the order channel, the account channel pushes full data on the first subscription.

#### Subsequent Pushes

Afterwards, you will receive account data pushes under the following circumstances:

| Push Type     | Description                                             |
|:----------|:------------------------------------------------|
| Event-triggered push   | Unified account spot/margin/futures order fills, funding settlement, balance changes (transfers, airdrops, lending, etc.) |

#### REST API

You can also check your account balance via the REST API:

`GET /api/v3/account/assets`
`GET /api/v3/account/funding-assets`

### Maximum Available Quantity

Under Pro Mode, enabling auto-borrow lets you buy/sell a product in a quantity greater than your coin balance.

In this case, you may want to know the maximum buy/sell quantity for a product. You can poll the following REST API to get the maximum available quantity (including available balance and the exchange's maximum borrowable amount):

`POST /api/v3/account/max-open-available`

Example request and response:

| Request | `{"category":"SPOT","symbol":"BTCUSDT","orderType":"market","side":"sell"}` |
|:------------|:--------------------------------------------------------------------------------------------------------------------------------------------------|
| **Success Response**   | `{"code": "00000", "requestTime": 1741851607871, "data": {"available": "52.008255", "maxOpen": "", "buyOpenCost": "", "sellOpenCost": "", "maxBuyOpen": "", "maxSellOpen": ""}, "msg": "success"}` |

For spot/margin, `available` represents the quote currency quantity when side=buy, and the base currency quantity when side=sell.


### Maximum Transferable Amount

To get the maximum transferable amount for the unified account or a sub-account, use `GET /api/v3/account/max-transferable`.

This endpoint also supports getting the maximum transferable amount including borrowing.


### Positions

You should use WebSocket to get position information updates.

#### WebSocket Subscription

Similar to the order channel, the position channel supports subscriptions at various dimensions.

This endpoint returns the mark price and other continuously changing parameters. Bitget periodically pushes data updates to you.

To subscribe to BTCUSDT position data, after connecting and logging in to the private WebSocket, send either of the requests below:

| Subscription Dimension | Product Type |
|:---------|:---------|
| **Request** | `{"op": "subscribe","args": [{"instType": "UTA","topic": "position"}]}` |
| **Success Response** | `{"event": "subscribe","arg": {"instType": "UTA","topic": "position"} }` |


#### Initial Full Push on Subscription

Like the account channel, the position channel pushes full data on the first subscription, sending information for all positions that are not zero.

#### Subsequent Pushes

Afterwards, you will receive position data pushes under the following circumstances:

| Push Type | Description                                                     |
|:---------|:--------------------------------------------------------|
| Event-triggered push | Unified account futures close-order placement, unified account futures open-order fill, unified account futures close-order fill, unified account futures close-order modification, unified account futures close-order cancellation |

#### REST API

You can also check position information via the REST API:

`GET /api/v3/position/current-position`
