Skip to main content

Signature

NOTE

The Webull SDK has already encapsulated the signature process, so if you are using the Webull SDK, no extra handling is needed. This article provides a detailed explanation of how the signature value is generated.

Signature Overview

API request signatures are values generated from the request content and a secret key using a specific signature algorithm. This cryptographic approach ensures the integrity and authenticity of the API request data, prevents tampering during transmission, and verifies the identity of the request originator. To ensure secure communication between both parties, when using the OpenAPI, Webull performs security verification for each API request through the signature. Regardless of whether you submit requests via HTTP or HTTPS, you must include the signature value in the request header as shown:

x-signature: signature_value

Signature Composition

The signature content consists of four parts:

  • HTTP request path

  • HTTP request query params

  • HTTP request body

  • HTTP request headers

caution
  • The contents being signed do not require URL Encoding
  • For HTTP POST requests, Content-Type must be application/json.

The relevant signature headers are:

NameDescription
x-app-keyApp Key
x-signature-algorithmSignature algorithm, default is HMAC-SHA1
x-signature-versionSignature algorithm version, default is 1.0
x-signature-nonceSignature nonce, regenerate for each request
x-timestampRFC-3339 formatted request timestamp, format: YYYY-MM-DDThh:mm:ssZ, e.g. 2023-07-16T19:23:51Z, only supports UTC timezone
hostThe request's Host in host:port format, e.g. api.webull.hk:8080 or api.webull.hk

Signature Rules

Content Participating in the Signature

1. HTTP request path
2. HTTP request query params
3. HTTP request body
4. HTTP request headers: x-app-key、x-signature-algorithm、x-signature-version、x-signature-nonce、x-timestamp、host

Constructing the Signature Content

  1. Sort all parameter names (from both request params and headers) in ascending string order.

  2. Join the parameter names and values in order as name1=value1&name2=value2 to create a new string called str1.

  3. Use MD5 to produce a 128-bit (16-byte) hash value for the body parameters and convert it to uppercase: toUpper(MD5(body)). Call this string str2.

  4. Join path, &, str1, &, str2 in order to create a new string called str3.

  5. URL encode str3 to get encoded_string.

CAUTION
  • There must be no spaces between body parameter keys and values.
  • If the body is empty, then str3 = path + "&" + str1

Constructing the Key

Append the character & to the end of your App Secret to get app_secret.

info

For info on how to obtain your App Secret, please refer to Applying for API.

Generating the Signature

Algorithm:

signature = base64(HMAC-SHA1(app_secret, encoded_string))

Example

Request Contents

Request Path:

/trade/place_order

Request Query Parameters:

NameExample ValueNote
a1webullNo URL Encoding needed
a2123No URL Encoding needed
a3xxxNo URL Encoding needed
q1yyyNo URL Encoding needed

Request Headers:

NameExample ValueNote
x-app-key776da210ab4a452795d74e726ebd74b6No URL Encoding needed
x-timestamp2022-01-04T03:55:31ZNo URL Encoding needed
x-signature-version1.0No URL Encoding needed
x-signature-algorithmHMAC-SHA1No URL Encoding needed
x-signature-nonce48ef5afed43d4d91ae514aaeafbc29baNo URL Encoding needed
hostapi.webull.hkNo URL Encoding needed

Request Body:

{"k1":123,"k2":"this is the api request body","k3":true,"k4":{"foo":[1,2]}}

App Secret:

0f50a2e853334a9aae1a783bee120c1f

Step 1: Constructing The Request Content

  1. Sort all parameter names in ascending string order, resulting in:

    Parameter name: a1, value: webull
    Parameter name: a2, value: 123
    Parameter name: a3, value: xxx
    Header name: host, value: api.webull.hk
    Parameter name: q1, value: yyy
    Header name: x-app-key, value: 776da210ab4a452795d74e726ebd74b6
    Header name: x-signature-algorithm, value: HMAC-SHA1
    Header name: x-signature-nonce, value: 48ef5afed43d4d91ae514aaeafbc29ba
    Header name: x-signature-version, value: 1.0
    Header name: x-timestamp, value: 2022-01-04T03:55:31Z
  2. Join parameter names and values in order to get str1:

    # str1
    str1 = "a1=webull&a2=123&a3=xxx&host=api.webull.hk&q1=yyy&x-app-key=776da210ab4a452795d74e726ebd74b6&x-signature-algorithm=HMAC-SHA1&x-signature-nonce=48ef5afed43d4d91ae514aaeafbc29ba&x-signature-version=1.0&x-timestamp=2022-01-04T03:55:31Z"
  3. Use the MD5 to produce hash value for the Body parameters and convert to uppercase: toUpper(MD5 (body)) to get str2:

    Body Parameter
    {"k1":123,"k2":"this is the api request body","k3":true,"k4":{"foo":[1,2]}}
    # str2
    str2 = "E296C96787E1A309691CEF3692F5EEDD"
  4. Join path, &, str1, &, str2 in order to get str3:

    # str3
    str3 = "/trade/place_order&a1=webull&a2=123&a3=xxx&host=api.webull.hk&q1=yyy&x-app-key=776da210ab4a452795d74e726ebd74b6&x-signature-algorithm=HMAC-SHA1&x-signature-nonce=48ef5afed43d4d91ae514aaeafbc29ba&x-signature-version=1.0&x-timestamp=2022-01-04T03:55:31Z&E296C96787E1A309691CEF3692F5EEDD"
  5. URL encode str3 to get encoded_string:

    # encoded_string
    encoded_string = "%2Ftrade%2Fplace_order%26a1%3Dwebull%26a2%3D123%26a3%3Dxxx%26host%3Dapi.webull.hk%26q1%3Dyyy%26x-app-key%3D776da210ab4a452795d74e726ebd74b6%26x-signature-algorithm%3DHMAC-SHA1%26x-signature-nonce%3D48ef5afed43d4d91ae514aaeafbc29ba%26x-signature-version%3D1.0%26x-timestamp%3D2022-01-04T03%3A55%3A31Z%26E296C96787E1A309691CEF3692F5EEDD"

Step2: Constructing The Key

Append the character & to the end of your App Secret to get app_secret, e.g.:

# app_secret
app_secret = "0f50a2e853334a9aae1a783bee120c1f&"

Step 3: Generating the Signature Value

The algorithm for generating the signature value:

signature = base64(HMAC-SHA1(app_secret, encoded_string))
  1. Use the HMAC-SHA1 encryption algorithm, and encrypt the encoded_string with the key app_secret.

  2. Then, encode the encrypted result using Base64.

  3. The final signature string is: kvlS6opdZDhEBo5jq40nHYXaLvM=.

Additional Notes

info
  1. If there are multiple parameters with the same name in the request, sort all values in ascending order, and then join them together using &. For example:

    url?name1=value1&name1=value2&name1=value3

    After sorting the values in ascending order and combining with &:

    name1 = value1&value2&value3
  2. In Golang, the default for json.Marshal has escapeHtml set to true, which will escape <, >, and &. In this case, you need to replace the escaped content back to the original characters as shown below:

    func trans(data []byte) []byte {
    data = bytes.Replace(data, []byte("\\u0026"), []byte("&"), -1)
    data = bytes.Replace(data, []byte("\\u003c"), []byte("<"), -1)
    data = bytes.Replace(data, []byte("\\u003e"), []byte(">"), -1)
    return data
    }