Skip to main content

Signature

Every API request to Webull must include a cryptographic signature in the request header. The signature is computed from the request content and your App Secret, ensuring the integrity and authenticity of each request.

x-signature: <signature_value>
SDK Users

The Webull SDK handles signature generation automatically. If you're using the SDK, you can skip this page — it's here for those implementing signature logic manually.

Required Request Headers

Every API request must include the following headers:

HeaderRequiredDescription
x-app-keyYesA unique identifier issued to a developer for accessing the API
x-timestampYesRequest timestamp in ISO 8601 format: YYYY-MM-DDThh:mm:ssZ (UTC only)
x-signatureYesThe computed signature value (output of the algorithm described below)
x-signature-algorithmYesSignature algorithm (e.g. HMAC-SHA256)
x-signature-versionYesSignature algorithm version (e.g. 1.0)
x-signature-nonceYesUnique random string, regenerated for each request
x-versionYesInterface version (accepts v2)
About App Secret

The app_secret is a unique key issued to developers. It is not included in any HTTP request header — it is used solely on the client side for signature generation. See Step 2: Construct the Key for details.

What Gets Signed

The signature is computed from four parts of the HTTP request:

  1. Request path
  2. Query parameters
  3. Request body
  4. Signing headers — the following headers participate in signature computation:
    • x-app-key
    • x-signature-algorithm
    • x-signature-version
    • x-signature-nonce
    • x-timestamp
    • host
note

x-signature and x-version do not participate in signing. x-signature carries the output of the signature itself; x-version is a required request header but is excluded from the signature computation.

Important
  • The content being signed does not require URL Encoding at this stage.
  • For POST requests, Content-Type must be application/json.

Signature Algorithm

Step 1: Construct the Signature String

  1. Merge all query parameters and the signing headers (listed in What Gets Signed) into a single list.
  2. Sort all parameter names in ascending alphabetical order.
  3. Join them as name1=value1&name2=value2&... → this is str1.
  4. If the request has a body, compute its MD5 hash and convert to uppercase: toUpper(MD5(body)) → this is str2.
  5. Concatenate: str3 = path + & + str1 + & + str2
    • If the body is empty: str3 = path + & + str1
  6. URL-encode str3 → this is encoded_string.
caution
  • There must be no extra spaces between body parameter keys and values.
  • If the body is empty, omit str2 entirely.

Step 2: Construct the Key

Append & to the end of your App Secret:

app_secret = "<your_app_secret>&"

Step 3: Generate the Signature

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

Worked Example

Below is a complete example showing each step of the signature generation process.

Request Details

Path: /trade/place_order

Query Parameters:

NameValue
a1webull
a2123
a3xxx
q1yyy

Request Headers:

NameValue
x-app-key776da210ab4a452795d74e726ebd74b6
x-timestamp2022-01-04T03:55:31Z
x-signature-version1.0
x-signature-algorithmHMAC-SHA256
x-signature-nonce48ef5afed43d4d91ae514aaeafbc29ba
hostapi.webull.com.au

Body:

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

App Secret: 0f50a2e853334a9aae1a783bee120c1f

Step 1: Construct the Signature String

  1. Merge query parameters and signing headers into a single list, then sort all parameter names in ascending alphabetical order:

    a1=webull, a2=123, a3=xxx,
    host=api.webull.com.au,
    q1=yyy,
    x-app-key=776da210ab4a452795d74e726ebd74b6,
    x-signature-algorithm=HMAC-SHA256,
    x-signature-nonce=48ef5afed43d4d91ae514aaeafbc29ba,
    x-signature-version=1.0,
    x-timestamp=2022-01-04T03:55:31Z
  2. Join them as key=value pairs with &str1:

    a1=webull&a2=123&a3=xxx&host=api.webull.com.au&q1=yyy&x-app-key=776da210ab4a452795d74e726ebd74b6&x-signature-algorithm=HMAC-SHA256&x-signature-nonce=48ef5afed43d4d91ae514aaeafbc29ba&x-signature-version=1.0&x-timestamp=2022-01-04T03:55:31Z
  3. Compute MD5 of the body and convert to uppercase → str2:

    E296C96787E1A309691CEF3692F5EEDD
  4. Concatenate path + & + str1 + & + str2 → str3:

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

Step 2: Construct the Key

app_secret = "0f50a2e853334a9aae1a783bee120c1f&"

Step 3: Generate the Signature

signature = base64(HMAC-SHA256(app_secret, encoded_string))
Verify Your Implementation

Use the values above to test your signature code.

Edge Cases

Duplicate Parameter Names

If a request contains multiple parameters with the same name, sort all values in ascending order and join them with &, then use the combined value in str1.

JSON Body Serialization

When computing the MD5 hash of the request body, ensure the JSON string has no extra spaces between keys and values (use compact serialization like separators=(',', ':') in Python or equivalent in your language).

Additionally, the JSON body used for MD5 computation must be exactly the same string sent in the HTTP request body.

Language-Specific HTML Escaping

Some languages automatically escape special characters in JSON output. You must reverse these escapes before computing the body MD5.