HTTP/HTTPS Authentication
Developers must sign each request using their API Key and API Secret. The signature is generated by concatenating the request method, request path, timestamp, and request parameters/body with the | symbol, then applying an HMAC-SHA256 hash. The signature and relevant metadata are included in request headers. The server validates the signature, authenticates the user, and authorizes access before processing the request.
📌 Overview of the Authentication Process
Each request must include:
- An API Key
- A timestamp
- A signature computed using your API Secret and request content
The API will verify:
- The API Key is valid and active
- The signature matches the request
- The timestamp is recent (default tolerance: ±5 minutes)
If any of these checks fail, the request will be rejected with an error code, such as:
{"code":10010008,"message":"Signature verification failed"}📤 Required Request Headers
All authenticated API requests must include the following custom headers:
| Header | Example | Description |
|---|---|---|
X-API-Key | A1B2C3D4E5F6... | Your public API Key |
X-API-Signature | a8f9c3e... | HMAC-SHA256 signature |
X-API-Timestamp | 1715100000000 | Current Unix timestamp in milliseconds |
Optional (recommended for traceability):
| Header | Example | Description |
|---|---|---|
X-REQUEST-ID | uuid-string | Client-generated ID for debugging/log correlation |
🔢 Signature Generation Steps
Start with the HTTP request method in uppercase (e.g.,
POST).Append the API path (excluding domain and protocol, Must start with
/! e.g.,/trade/v1/orders) using|.Append the Unix timestamp (milliseconds, matching
X-API-Timestamp) using|.Append the request parameters:
- For GET requests:
- If query parameters exist, concatenate them using
&inkey=valueformat (no need to sort), then append with|. Do not include the leading?. - If no parameters exist, append an empty string. Note: Do not change the order of the request parameters. Ensure that the signature and request parameters are consistent.
- If query parameters exist, concatenate them using
- For non-GET requests (e.g., POST, PUT):
- Use the full raw JSON body string and append with
|. Please ensure that the request body and signature are consistent. Do not change them due to similar formatting, which may cause the request body and signature to be different. - If the request body is empty, append an empty string.
- Use the full raw JSON body string and append with
- For GET requests:
Concatenate all parts to form the signature string in the format:
{request_method}|{request_path}|{timestamp}|{query_string_or_request_body}Use your API Secret to generate an HMAC-SHA256 hash of the signature string.
Base64-encode the resulting hash.
Add the following headers to your request:
X-API-KeyX-API-TimestampX-API-Signature
Example (String Construction Flow)
POST|/trade/v1/orders|1746774142003|{"market":"hkex","product_code":"00700",...}The final signature is:
Base64(HMAC-SHA256(signature_string, API_Secret))If an error occurs during the authentication process, please refer to the
Error Codessection under the HTTP Return Codes document.
Sample Code
import requests
import hmac
import hashlib
import base64
import time
class ApiClient:
def __init__(self, api_key, api_secret, base_url):
self.api_key = api_key
self.api_secret = api_secret
self.base_url = base_url
def send_request(self, method, path, query_string='', body=''):
timestamp = int(time.time() * 1000) # Milliseconds
signature_string = self.build_signature_string(method, path, timestamp, query_string, body)
signature = self.generate_signature(signature_string, self.api_secret)
url = self.base_url + path
if query_string:
url += '?' + query_string
headers = {
'X-API-Key': self.api_key,
'X-API-Timestamp': str(timestamp),
'X-API-Signature': signature
}
if method.upper() == 'GET':
response = requests.get(url, headers=headers)
else:
response = requests.request(method.upper(), url, headers=headers, data=body)
return response.text
def build_signature_string(self, method, path, timestamp, query_string, body):
signature_string = f"{method.upper()}|{path}|{timestamp}"
if method.upper() == 'GET':
signature_string += f"|{query_string}"
else:
signature_string += f"|{body}"
return signature_string
def generate_signature(self, data, secret):
key = secret.encode('utf-8')
message = data.encode('utf-8')
hmac_obj = hmac.new(key, message, hashlib.sha256)
return base64.b64encode(hmac_obj.digest()).decode('utf-8')
api_key = 'your_api_key_here'
api_secret = 'your_api_secret_here'
base_url = 'https://api.example.com'
client = ApiClient(api_key, api_secret, base_url)
# GET request with query parameters
get_response = client.send_request('GET', '/trade/v1/orders', 'symbol=BTCUSDT&page_size=10')
print('GET Response:', get_response)
# POST request with JSON body
post_body = '{"symbol":"BTCUSDT","side":"BUY","type":"LIMIT","price":"50000","quantity":"0.1"}'
post_response = client.send_request('POST', '/trade/v1/orders', body=post_body)
print('POST Response:', post_response)import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Map;
import java.util.stream.Collectors;
public class ApiAuthExample {
// Example usage
public static void main(String[] args) {
String apiKey = "YOUR_API_KEY";
String apiSecret = "YOUR_API_SECRET";
String baseUrl = "https://url_here";
// GET request example
Map<String, String> getParams = Map.of("symbol", "BTCUSDT", "page_size", "10");
sendGetRequest(baseUrl + "/trade/v1/orders", getParams, apiKey, apiSecret);
// POST request example
String postBody = "{\"symbol\":\"BTC\",\"quantity\":1.5}";
sendPostRequest(baseUrl + "/trade/v1/orders", postBody, apiKey, apiSecret);
}
/**
* Sends a GET request with API authentication headers
* @param url Full endpoint URL with protocol and domain
* @param params Query parameters (key-value pairs)
* @param apiKey API key credential
* @param apiSecret API secret credential
*/
public static void sendGetRequest(String url, Map<String, String> params, String apiKey, String apiSecret) {
try {
long timestamp = System.currentTimeMillis();
String queryString = buildQueryString(params);
URL urlObj = new URL(url);
// Build signature components
String path = urlObj.getPath();
String signatureData = String.format("GET|%s|%d|%s", path, timestamp, queryString);
String signature = generateSignature(signatureData, apiSecret);
// Create connection
String fullUrl = queryString.isEmpty() ? url : url + "?" + queryString;
HttpURLConnection conn = (HttpURLConnection) new URL(fullUrl).openConnection();
conn.setRequestMethod("GET");
// Set headers
conn.setRequestProperty("X-API-Key", apiKey);
conn.setRequestProperty("X-API-Timestamp", String.valueOf(timestamp));
conn.setRequestProperty("X-API-Signature", signature);
// Handle response
int responseCode = conn.getResponseCode();
System.out.println("GET Response Code: " + responseCode);
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(conn.getInputStream()))) {
String line;
StringBuilder response = new StringBuilder();
while ((line = reader.readLine()) != null) {
response.append(line);
}
System.out.println("Response Body: " + response);
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Sends a POST request with API authentication headers
* @param url Full endpoint URL with protocol and domain
* @param requestBody JSON-formatted request body
* @param apiKey API key credential
* @param apiSecret API secret credential
*/
public static void sendPostRequest(String url, String requestBody, String apiKey, String apiSecret) {
try {
long timestamp = System.currentTimeMillis();
URL urlObj = new URL(url);
// Build signature components
String path = urlObj.getPath();
String signatureData = String.format("POST|%s|%d|%s", path, timestamp, requestBody);
String signature = generateSignature(signatureData, apiSecret);
// Create connection
HttpURLConnection conn = (HttpURLConnection) urlObj.openConnection();
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/json");
conn.setDoOutput(true);
// Set headers
conn.setRequestProperty("X-API-Key", apiKey);
conn.setRequestProperty("X-API-Timestamp", String.valueOf(timestamp));
conn.setRequestProperty("X-API-Signature", signature);
// Send request body
try (OutputStream os = conn.getOutputStream()) {
byte[] input = requestBody.getBytes(StandardCharsets.UTF_8);
os.write(input, 0, input.length);
}
// Handle response
int responseCode = conn.getResponseCode();
System.out.println("POST Response Code: " + responseCode);
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(conn.getInputStream()))) {
String line;
StringBuilder response = new StringBuilder();
while ((line = reader.readLine()) != null) {
response.append(line);
}
System.out.println("Response Body: " + response);
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Generates HMAC-SHA256 signature for request authentication
* @param data Concatenated signature string
* @param apiSecret API secret credential
* @return Base64-encoded signature
*/
private static String generateSignature(String data, String apiSecret) {
try {
Mac hmac = Mac.getInstance("HmacSHA256");
SecretKeySpec keySpec = new SecretKeySpec(apiSecret.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
hmac.init(keySpec);
byte[] rawSignature = hmac.doFinal(data.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(rawSignature);
} catch (Exception e) {
throw new RuntimeException("Failed to generate signature", e);
}
}
/**
* Builds URL query string from parameters
* @param params Map of query parameters
* @return URL-encoded query string (without leading '?')
*/
private static String buildQueryString(Map<String, String> params) {
if (params == null || params.isEmpty()) return "";
return params.entrySet().stream()
.map(entry -> entry.getKey() + "=" + entry.getValue())
.collect(Collectors.joining("&"));
}
}public class ApiClient
{
private readonly string _apiKey;
private readonly string _apiSecret;
private readonly HttpClient _httpClient;
public ApiClient(string apiKey, string apiSecret, string baseUrl)
{
_apiKey = apiKey;
_apiSecret = apiSecret;
_httpClient = new HttpClient { BaseAddress = new Uri(baseUrl) };
}
public async Task<string> SendRequestAsync(string method, string path, string queryString = "", string body = "")
{
// Generate current Unix timestamp in milliseconds
long timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
// Build the signature string
string signatureString = BuildSignatureString(method, path, timestamp, queryString, body);
// Generate the HMAC-SHA256 signature and encode it to Base64
string signature = GenerateSignature(signatureString, _apiSecret);
// Prepare the HTTP request
var request = new HttpRequestMessage(new HttpMethod(method),
path + (string.IsNullOrEmpty(queryString) ? "" : "?" + queryString));
if (!method.Equals("GET", StringComparison.CurrentCultureIgnoreCase) && !string.IsNullOrEmpty(body))
{
request.Content = new StringContent(body, Encoding.UTF8, "application/json");
}
// Add required headers
request.Headers.Add("X-API-Key", _apiKey);
request.Headers.Add("X-API-Timestamp", timestamp.ToString());
request.Headers.Add("X-API-Signature", signature);
// Send the request and return the response
var response = await _httpClient.SendAsync(request);
// response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
private string BuildSignatureString(string method, string path, long timestamp, string queryString, string body)
{
// Start with method (uppercase) and concatenate path and timestamp
string signatureString = $"{method.ToUpper()}|{path}|{timestamp}";
// Append query string for GET or body for POST
if (method.ToUpper() == "GET")
{
signatureString += "|" + queryString;
}
else
{
signatureString += "|" + (body ?? "");
}
return signatureString;
}
private string GenerateSignature(string data, string secret)
{
using (var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret)))
{
byte[] hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(data));
return Convert.ToBase64String(hash);
}
}
}
class Program
{
static async Task Main(string[] args)
{
string apiKey = "your_api_key_here";
string apiSecret = "your_api_secret_here";
string baseUrl = "https://url_here";
var client = new ApiClient(apiKey, apiSecret, baseUrl);
// Example 1: GET request with query parameters
string getResponse = await client.SendRequestAsync(
"GET",
"/trade/v1/orders",
"symbol=BTCUSDT&page_size=10"
);
Console.WriteLine("GET Response: " + getResponse);
// Example 2: POST request with JSON body
string postBody = "{\"symbol\":\"BTCUSDT\",\"side\":\"BUY\",\"type\":\"LIMIT\",\"price\":\"50000\",\"quantity\":\"0.1\"}";
string postResponse = await client.SendRequestAsync(
"POST",
"/trade/v1/orders",
body: postBody
);
Console.WriteLine("POST Response: " + postResponse);
}
}package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"fmt"
"io/ioutil"
"net/http"
"strings"
"time"
)
type ApiClient struct {
apiKey string
apiSecret string
client *http.Client
baseUrl string
}
func NewApiClient(apiKey, apiSecret, baseUrl string) *ApiClient {
return &ApiClient{
apiKey: apiKey,
apiSecret: apiSecret,
client: &http.Client{},
baseUrl: baseUrl,
}
}
func (c *ApiClient) SendRequest(method, path, queryString, body string) (string, error) {
timestamp := time.Now().UnixNano() / 1e6 // Milliseconds
signatureString := c.buildSignatureString(method, path, timestamp, queryString, body)
signature := c.generateSignature(signatureString, c.apiSecret)
url := c.baseUrl + path
if queryString != "" {
url += "?" + queryString
}
req, err := http.NewRequest(strings.ToUpper(method), url, strings.NewReader(body))
if err != nil {
return "", err
}
req.Header.Add("X-API-Key", c.apiKey)
req.Header.Add("X-API-Timestamp", fmt.Sprintf("%d", timestamp))
req.Header.Add("X-API-Signature", signature)
if method != "GET" && body != "" {
req.Header.Add("Content-Type", "application/json")
}
resp, err := c.client.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
responseBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
return string(responseBody), nil
}
func (c *ApiClient) buildSignatureString(method, path string, timestamp int64, queryString, body string) string {
signatureString := fmt.Sprintf("%s|%s|%d", strings.ToUpper(method), path, timestamp)
if strings.ToUpper(method) == "GET" {
signatureString += "|" + queryString
} else {
signatureString += "|" + body
}
return signatureString
}
func (c *ApiClient) generateSignature(data, secret string) string {
key := []byte(secret)
h := hmac.New(sha256.New, key)
h.Write([]byte(data))
return base64.StdEncoding.EncodeToString(h.Sum(nil))
}
func main() {
apiKey := "your_api_key_here"
apiSecret := "your_api_secret_here"
baseUrl := "https://url_here"
client := NewApiClient(apiKey, apiSecret, baseUrl)
// GET request with query parameters
getResponse, err := client.SendRequest("GET", "/trade/v1/orders", "symbol=BTCUSDT&page_size=10", "")
if err != nil {
fmt.Println("Error:", err)
} else {
fmt.Println("GET Response:", getResponse)
}
// POST request with JSON body
postBody := `{"symbol":"BTCUSDT","side":"BUY","type":"LIMIT","price":"50000","quantity":"0.1"}`
postResponse, err := client.SendRequest("POST", "/trade/v1/orders", "", postBody)
if err != nil {
fmt.Println("Error:", err)
} else {
fmt.Println("POST Response:", postResponse)
}
}const axios = require('axios');
const crypto = require('crypto');
class ApiClient {
constructor(apiKey, apiSecret, baseUrl) {
this.apiKey = apiKey;
this.apiSecret = apiSecret;
this.baseUrl = baseUrl;
}
async sendRequest(method, path, queryString = '', body = '') {
const timestamp = Date.now();
const signatureString = this.buildSignatureString(method, path, timestamp, queryString, body);
const signature = this.generateSignature(signatureString, this.apiSecret);
const url = this.baseUrl + path + (queryString ? `?${queryString}` : '');
const headers = {
'X-API-Key': this.apiKey,
'X-API-Timestamp': timestamp.toString(),
'X-API-Signature': signature,
};
try {
const response = await axios({
method: method.toUpperCase(),
url,
headers,
data: body,
});
return response.data;
} catch (error) {
console.error('Request failed:', error);
throw error;
}
}
buildSignatureString(method, path, timestamp, queryString, body) {
let signatureString = `${method.toUpperCase()}|${path}|${timestamp}`;
if (method.toUpperCase() === 'GET') {
signatureString += `|${queryString}`;
} else {
signatureString += `|${body}`;
}
return signatureString;
}
generateSignature(data, secret) {
const hmac = crypto.createHmac('sha256', secret);
hmac.update(data);
return hmac.digest('base64');
}
}
const apiKey = 'your_api_key_here';
const apiSecret = 'your_api_secret_here';
const baseUrl = 'https://url_here';
const client = new ApiClient(apiKey, apiSecret, baseUrl);
// GET request with query parameters
client
.sendRequest('GET', '/trade/v1/orders', 'symbol=BTCUSDT&page_size=10')
.then(response => console.log('GET Response:', response))
.catch(error => console.error('Error:', error));
// POST request with JSON body
const postBody = JSON.stringify({
symbol: 'BTCUSDT',
side: 'BUY',
type: 'LIMIT',
price: '50000',
quantity: '0.1',
});
client
.sendRequest('POST', '/trade/v1/orders', '', postBody)
.then(response => console.log('POST Response:', response))
.catch(error => console.error('Error:', error));🧷 Security Best Practices
- Never share your Secret; store it securely (e.g., in a key vault or encrypted file)
- Use timestamp validation to prevent replay attacks
- Rotate keys regularly via the API key management panel
- Restrict IPs if possible (IP whitelisting)
- Use UUIDs in
X-REQUEST-IDto trace requests across microservices

