# Quick Start

## Access Setup

To use the API, please [log in](https://www.bitget.com/login) to the web page and complete the API key application and permission configuration, and then proceed with development and trading based on the documentation details.

You can click [API Key Management](https://www.bitget.com/account/newapi) to create an API Key.

Each UID can create up to 50 API key sets, with each key supporting two types of permissions: read and write.

### Sub-account API Key

Sub-accounts (virtual sub-accounts and standard sub-accounts) can create and manage their own API Keys independently, without requiring the main account to operate on their behalf.

**Prerequisite**: The main account must enable the **API Key Management** permission for the sub-account. This setting is disabled by default. The main account can configure it under sub-account permission settings.

Once enabled, the sub-account can perform the following operations on its own API Keys:

- Create API Key
- View API Key
- Edit API Key permissions
- Delete API Key

The main account retains full control and can view, edit, or delete any sub-account's API Keys at any time.

The permissions are outlined below:

- Unified account trade, read-only: Enables access to trade information.
- Unified account trade, read and write: In addition to read-only permissions, it allows order placement, cancellation, and more.
- Unified account management, read-only: Enables access to account information.
- Unified account management, read and write: In addition to read-only permissions, it allows account settings, such as leverage adjustment, Holding mode switching, and more.

After creation, please note the following information:

- `APIKey` The identity of the API transaction, generated by a random algorithm.
- `Secretkey` private key, randomly generated by the system, used for signature generation.
- `Passphrase` Password is set by the user. It should be noted that if the Passphrase is forgotten, it cannot be retrieved and the APIKey needs to be re-created.

:::tip{title="Security"}
For security reasons, it is strongly recommended that you bind an IP address when creating the API Key.
:::

:::warning{title="Risk Warning"}
- These three keys are closely related to account security. Please remember `Passphrase` and do not disclose it to others at any time.
The leakage of any one of these three keys may cause the loss of your assets. If you find that the APIKey is leaked, please delete the APIKey as soon as possible
- For security reasons, it is strongly recommended that you bind an IP address when creating the API Key
- Once set, the `Passphrase` cannot be changed. If it is forgotten or lost, you will need to delete and re-create the API Key
:::

## Demo Trading

Demo trading allows you to practice trading and test strategies in a real-market environment using virtual funds, helping you improve your skills and reduce the risk of losses.

#### API Key

To perform demo trading via API, you'll need to create a Demo API Key in the first place. The steps are as follows:<br/>
Log in to your account → Switch to Demo mode → Go to the Personal Center → Go to the API Key Management → Create a Demo API Key → Use the Demo API Key to start trading.

#### REST

Please use the created Demo API Key for API calls, and add `paptrading` in the request header, with the value set to `1`.

#### WebSocket

Please use the created Demo API Key for WebSocket connections and request the demo trading service address:<br/>
Public: wss://wspap.bitget.com/v3/ws/public<br/>
Private: wss://wspap.bitget.com/v3/ws/private

## Domain Name

### Regular Domain Name

| Domain Name               | Domain                            | Description                       |
|---------------------------|-----------------------------------|-----------------------------------|
| REST                      | https://api.bitget.com            | main domain name                  |
| Websocket public channel  | wss://ws.bitget.com/v3/ws/public  | main domain name, public channel  |
| websocket private channel | wss://ws.bitget.com/v3/ws/private | main domain name, private channel |
| websocket SBE domain      | wss://ws.bitget.com/v3/ws/public/sbe | main domain name, SBE          |

### Vip Line Domain Name

:::info{title="Overview"}
- The VIP Line is a high-speed line dedicated to VIP and institutional users. The link bypasses the CDN to reduce request latency. Eligible clients may consult their BD/RM to apply for access
- Usage: simply replace the domain with the VIP Line request domain
- Supports both **Unified Account mode** and **Classic Account mode**
:::

#### Unified Account Domain

| Domain Name               | API                                    | Description                       |
|----------------------------|-----------------------------------------|-----------------------------------|
| REST                      | https://vip-api.bitget.com             | main domain name                  |
| websocket public channel  | wss://vip-ws.bitget.com/v3/ws/public   | main domain name, public channel  |
| websocket private channel | wss://vip-ws.bitget.com/v3/ws/private  | main domain name, private channel |
| websocket SBE domain      | wss://vip-ws.bitget.com/v3/ws/public/sbe | main domain name, SBE          |

#### Classic Account Domain

| Domain Name               | API                                    | Description                       |
|----------------------------|-----------------------------------------|-----------------------------------|
| REST                      | https://vip-api.bitget.com             | main domain name                  |
| websocket public channel  | wss://vip-ws.bitget.com/v2/ws/public   | main domain name, public channel  |
| websocket private channel | wss://vip-ws.bitget.com/v2/ws/private  | main domain name, private channel |

### Lo-La Domain

:::info{title="Overview"}
- Lo-La (Low-Latency) is a high-speed line dedicated to VIP and institutional users, offering lower latency and better stability. Eligible clients may consult their BD/RM to apply for access
- Usage: simply replace the domain with the Lo-La request domain
- Only supports **Unified Account mode**
:::

#### Lo-La High-Speed Domain

:::info{title="Overview"}
Sacrifices link high-availability in exchange for absolute same-zone routing of order requests compared to the VIP Line, further reducing overall link latency
:::

| Domain Name               | API                                             | Description                       |
|----------------------------|--------------------------------------------------|-----------------------------------|
| REST                      | https://vip-api-uta-a.bitget.com/api/v3/*       | Rest main domain name             |
| websocket public channel  | wss://vip-ws-uta-pub-a.bitget.com/v3/ws/public  | main domain name, public channel  |
| websocket private channel | wss://vip-ws-uta-pri-a.bitget.com/v3/ws/private | main domain name, private channel |
| websocket SBE domain      | wss://vip-ws-uta-pub-a.bitget.com/v3/ws/public/sbe | main domain name, SBE          |

#### Lo-La High-Availability Domain

:::info{title="Overview"}
The link has a built-in circuit-breaker protection mechanism to ensure the continuity of clients' trading
:::

| Domain Name               | API                                        | Description                       |
|----------------------------|---------------------------------------------|-----------------------------------|
| REST                      | https://vip-api-uta.bitget.com/api/v3/*    | Rest main domain name             |
| websocket public channel  | wss://vip-ws-uta.bitget.com/v3/ws/public   | main domain name, public channel  |
| websocket private channel | wss://vip-ws-uta.bitget.com/v3/ws/private  | main domain name, private channel |
| websocket SBE domain      | wss://vip-ws-uta.bitget.com/v3/ws/public/sbe | main domain name, SBE          |

## Header

All REST request headers must contain the following keys:

- `ACCESS-KEY`: The API key, represented as a string.
- `ACCESS-SIGN`: Sign using base64 encoding (see messages for details).
- `ACCESS-TIMESTAMP`:  The request's timestamp.
- `ACCESS-PASSPHRASE`: The passphrase you set when creating the API KEY.
- `Content-Type`: Set to application/json by default.
- `locale`: Language. e.g., Chinese (zh-CN), English (en-US)

## Response

- `X-BG-REQUEST-ACCEPT-TIME`: The time the server received the request
- `X-BG-RESPONSE-COMPLETE-TIME`: The time the server completed request processing
- `x-mbx-used-remain-limit`: Remaining rate limit quota for the current endpoint

## SDK

Bitget V3 provides official SDKs for the following languages:

| Language | Link |
|:---------|:-----|
| Java     | [Java](https://github.com/BitgetLimited/v3-bitget-api-sdk/tree/master/bitget-java-sdk-api) |
| Python   | [Python](https://github.com/BitgetLimited/v3-bitget-api-sdk/tree/master/bitget-python-sdk-api) |
| Node.js  | [Node.js](https://github.com/BitgetLimited/v3-bitget-api-sdk/tree/master/bitget-node-sdk-api) |
| Golang   | [Golang](https://github.com/BitgetLimited/v3-bitget-api-sdk/tree/master/bitget-golang-sdk-api) |
| PHP      | [PHP](https://github.com/BitgetLimited/v3-bitget-api-sdk/tree/master/bitget-php-sdk-api) |

## Signature

### Generate Signature

The `ACCESS-SIGN` request header is generated by encrypting the **timestamp + method.toUpperCase() + requestPath + "?" + queryString + body** string (+ means string concatenation) using the **HMAC SHA256** algorithm with **secretKey**, then encoding the result with **BASE64**.

#### Description of each parameter in the signature

- timestamp: Same as `ACCESS-TIMESTAMP` request header. Value equals milliseconds since Epoch.
- method: Request method (POST/GET), all uppercase.
- requestPath: Request interface path.
- queryString: The query string in the request URL (the request parameter after `?`).
- body: The request body as a string. If the request body is empty (usually a GET request), the body can be omitted.

**If the queryString is empty, signature content:**

`timestamp + method.toUpperCase() + requestPath + body`

**If the queryString is not empty, signature content:**

`timestamp + method.toUpperCase() + requestPath + "?" + queryString + body`

#### Sample Code

**GET example** — Get account fee rate (BTCUSDT spot):

- timestamp = 16273667805456
- method = "GET"
- requestPath = "/api/v3/account/fee-rate"
- queryString = "category=SPOT&symbol=BTCUSDT"

Content to be signed:

`16273667805456GET/api/v3/account/fee-rate?category=SPOT&symbol=BTCUSDT`

**POST example** — Place a spot limit order (BTCUSDT):

- timestamp = 16273667805456
- method = "POST"
- requestPath = "/api/v3/trade/place-order"
- body = `{"category":"SPOT","symbol":"BTCUSDT","side":"buy","orderType":"limit","qty":"0.1","price":"30000","timeInForce":"gtc"}`

Content to be signed:

`16273667805456POST/api/v3/trade/place-order{"category":"SPOT","symbol":"BTCUSDT","side":"buy","orderType":"limit","qty":"0.1","price":"30000","timeInForce":"gtc"}`

#### Steps to generate the final signature

**HMAC**

```java title='Step 1. Use the private key secretkey to encrypt the string to be signed with HMAC SHA256'
String payload = hmac_sha256(secretkey, Message);
```

```java title='Step 2. Base64 encoding for Signature'
String signature = base64.encode(payload);
```

**RSA**

Step 1. Use the RSA privateKey **privateKey** to encrypt the string to be signed with SHA-256.

Step 2. Base64 encoding for Signature.

### HMAC Signature Demo Code

<CodeTabs>

```java title="Java"
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.management.RuntimeErrorException;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import org.springframework.util.Base64Utils;

@Slf4j
public class CheckSign {

  private static final String secretKey = "";


  public static void main(String[] args) throws Exception {
    //POST sign example
//        String timestamp = "1684813405151";
//        String body = "{\"symbol\":\"BTCUSDT\",\"productType\":\"usdt-futures\",\"marginMode\":\"crossed\",\"marginCoin\":\"USDT\",\"size\":\"8\",\"side\":\"buy\",\"orderType\":\"limit\",\"price\":\"30000\"}";
//
//        String sign = generate(timestamp,"POST","/api/v3/trade/place-order" ,null,body,secretKey);
//        log.info("sign:{}",sign);


    //GET sign example
    String timestamp = "1684814440729";
    String queryString = "category=SPOT&symbol=BTCUSDT"; // Need to be sorted in ascending alphabetical order by key
    String sign = generate(timestamp,"GET","/api/v3/account/fee-rate" ,queryString,null,secretKey);
    log.info("sign:{}",sign);
  }



  private static Mac MAC;

  static {
    try {
      CheckSign.MAC = Mac.getInstance("HmacSHA256");
    } catch (NoSuchAlgorithmException var1) {
      throw new RuntimeErrorException(new Error("Can't get Mac's instance."));
    }
  }

  public static String generate(String timestamp, String method, String requestPath,
                                String queryString, String body, String secretKey)
          throws CloneNotSupportedException, InvalidKeyException, UnsupportedEncodingException {

    method = method.toUpperCase();
    body = StringUtils.defaultIfBlank(body, StringUtils.EMPTY);
    queryString = StringUtils.isBlank(queryString) ? StringUtils.EMPTY : "?" + queryString;
    String preHash = timestamp + method + requestPath + queryString + body;
    log.info("preHash:{}",preHash);
    byte[] secretKeyBytes = secretKey.getBytes("UTF-8");
    SecretKeySpec secretKeySpec = new SecretKeySpec(secretKeyBytes, "HmacSHA256");
    Mac mac = (Mac) CheckSign.MAC.clone();
    mac.init(secretKeySpec);
    return Base64.getEncoder().encodeToString(mac.doFinal(preHash.getBytes("UTF-8")));
  }

}
```

```python title="Python"
import hmac
import base64
import json
import time



def get_timestamp():
  return int(time.time() * 1000)


def sign(message, secret_key):
  mac = hmac.new(bytes(secret_key, encoding='utf8'), bytes(message, encoding='utf-8'), digestmod='sha256')
  d = mac.digest()
  return base64.b64encode(d)


def pre_hash(timestamp, method, request_path, body):
  return str(timestamp) + str.upper(method) + request_path + body


def parse_params_to_str(params):
    params = [(key, val) for key, val in params.items()]
    params.sort(key=lambda x: x[0])
    url = '?' +toQueryWithNoEncode(params);
    if url == '?':
        return ''
    return url

def toQueryWithNoEncode(params):
    url = ''
    for key, value in params:
        url = url + str(key) + '=' + str(value) + '&'
    return url[0:-1]



if __name__ == '__main__':
  API_SECRET_KEY = ""


  timestamp = "1685013478665" # get_timestamp()
  request_path = "/api/v3/trade/place-order"
  # POST
  params = {"category": "SPOT", "symbol": "BTCUSDT", "side": "buy", "orderType": "limit", "qty": "0.1", "price": "30000", "timeInForce": "gtc"}
  body = json.dumps(params)
  sign = sign(pre_hash(timestamp, "POST", request_path, str(body)), API_SECRET_KEY)
  print(sign)


  # GET
  body = ""
  request_path = "/api/v3/account/fee-rate"
  params = {"category": "SPOT", "symbol": "BTCUSDT"}
  request_path = request_path + parse_params_to_str(params) # Need to be sorted in ascending alphabetical order by key
  sign = sign(pre_hash(timestamp, "GET", request_path, body), API_SECRET_KEY)
  print(sign)
```

</CodeTabs>

### RSA Signature Demo Code

```python
import base64
import json
import time
from Crypto.Hash import SHA256
from Crypto.Signature import PKCS1_v1_5
from Crypto.PublicKey import RSA



def get_timestamp():
    return int(time.time() * 1000)


def rsa_sign(message, private_key):
  pri_key = RSA.importKey(private_key)
  encoded_param = SHA256.new(bytes(message, encoding='utf-8'))
  sign_str = PKCS1_v1_5.new(pri_key).sign(encoded_param)
  return base64.b64encode(sign_str).decode()


def pre_hash(timestamp, method, request_path, body):
    return str(timestamp) + str.upper(method) + request_path + body


def parse_params_to_str(params):
    params = [(key, val) for key, val in params.items()]
    params.sort(key=lambda x: x[0])
    from urllib.parse import urlencode
    url = '?' +urlencode(params);
    if url == '?':
        return ''
    return url


if __name__ == '__main__':
    private_key = '''-----BEGIN PRIVATE KEY-----
YOUR_PRIVATE_KEY_HERE
-----END PRIVATE KEY-----'''


    timestamp = get_timestamp()
    request_path = "/api/v3/trade/place-order"
    # POST
    params = {"category": "SPOT", "symbol": "BTCUSDT", "side": "buy", "orderType": "limit", "qty": "0.1", "price": "30000", "timeInForce": "gtc"}
    body = json.dumps(params)
    sign = rsa_sign(pre_hash(timestamp, "POST", request_path, str(body)), private_key)
    print(sign)


    # GET
    body = ""
    request_path = "/api/v3/account/fee-rate"
    params = {"category": "SPOT", "symbol": "BTCUSDT"}
    request_path = request_path + parse_params_to_str(params) # Need to be sorted in ascending alphabetical order by key
    sign = rsa_sign(pre_hash(timestamp, "GET", request_path, body), private_key)
    print(sign)
```

## Rate Limit

If requests are too frequent, the system will automatically limit the request and return the `429 Too Many Requests` status code.

1. The rate limit for each API endpoint is marked on the respective doc page;
2. Each API endpoint's rate limit is calculated independently;
3. REST and WebSocket share the same rate limit quota;
4. The overall rate limit for the common domain name is 6000 requests/IP/min

## WebSocket

### Overview

WebSocket is a new HTML5 protocol that achieves full-duplex data transmission between the client and server, allowing data to be transferred effectively in both directions. A connection between the client and server can be established with just one handshake. The server will then be able to push data to the client according to preset rules. Its advantages include:

- The WebSocket request header size for data transmission between client and server is only 2 bytes.
- Either the client or server can initiate data transmission.
- There is no need to repeatedly create and delete TCP connections, saving bandwidth and server resources.

It is strongly recommended that developers use the WebSocket API to obtain market information and order book data.

### Connect

**Connection limit**: 300 connection requests/IP/5min, max 100 connections/IP

**Subscription limit**: 240 subscription requests/hour/connection, max 1000 channel subscriptions/connection

To keep the connection stable and effective, it is recommended to:

1. Set a 30-second timer to send the string `"ping"`, and expect the string `"pong"` as a response. If no `"pong"` is received, please reconnect.
2. The WebSocket server will disconnect if no `"ping"` is received for 2 minutes.
3. The WebSocket server accepts up to 10 messages per second per connection. Messages include:
   - String `"ping"`
   - JSON messages, including login, subscribe, and unsubscribe
4. If a user sends more messages than the limit, the connection will be disconnected. IPs that are repeatedly disconnected may be blocked by the server.
5. It is strongly recommended to subscribe to **fewer than 50 channels per connection**. Connections with fewer channel subscriptions are more stable.

### Login

**apiKey**: Unique identifier for invoking the API. Users need to [apply](https://www.bitget.com/account/newapi) for one.

**passphrase**: The APIKey password.

**timestamp**: Unix timestamp in milliseconds. Expires in 30 seconds.

**sign**: Signature string. The signing algorithm is as follows:

The message to be signed is: `timestamp + "GET" + "/user/verify"`

```javascript title="Example of timestamp"
const timestamp = '' + Date.now()
```

```javascript title="Sign example"
sign = CryptoJS.enc.Base64.Stringify(CryptoJS.HmacSHA256(timestamp + 'GET' + '/user/verify', secretKey))
```

**method**: Always `'GET'`.

**requestPath**: Always `'/user/verify'`.

The request will expire 30 seconds after the timestamp. If your server time differs from the API server time, we recommend using the REST API to query the API server time and then synchronize the timestamp.

#### Steps to generate the final signature

**HMAC**

Step 1. Concatenate the content:

```java
Long timestamp = System.currentTimeMillis() / 1000;
String content = timestamp + "GET" + "/user/verify";
```

Step 2. Use the private key secretkey to encrypt the string with HMAC SHA256:

```java
String hash = hmac_sha256(content, secretkey);
```

Step 3. Base64 encode the hash:

```java
String sign = base64.encode(hash);
```

**RSA**

Step 1. Use the RSA privateKey **privateKey** to encrypt the string to be signed with SHA-256.

Step 2. Base64 encoding for Signature.

If login fails, the connection will be automatically disconnected.

```json title="Request Format Description"
{
  "op":"login",
  "args":[
    {
      "apiKey":"<api_key>",
      "passphrase":"<passphrase>",
      "timestamp":"<timestamp>",
      "sign":"<sign>"
    }
  ]
}
```

```json title="Request Example"
{
  "op":"login",
  "args":[
    {
      "apiKey":"xx_xxx",
      "passphrase":"xxx",
      "timestamp":"1538054050",
      "sign":"8RCOqCJAhhEh4PWcZB/96QojLDqMAg4qNynIixFzS3E="
    }
  ]
}
```

```json title="Successful Response Example"
{
  "event":"login",
  "code":"0",
  "msg":""
}
```

```json title="Failure Response Example"
{
  "event":"error",
  "code":"30005",
  "msg":"error"
}
```

## Contact us

- Bitget Telegram official group [Click to join](https://t.me/BitgetENOfficial)
- API Telegram official group [Click to join](https://t.me/bitgetOpenapi)
