OpenAPI adds stock short selling and options functions. For details, please refer to the update logs.
OpenAPI adds stock short selling and options functions. For details, please refer to the update logs.
OpenAPI adds stock short selling and options functions. For details, please refer to the update logs.
Skip to main content

Authentication

caution

The authentication has already been encapsulated by Webull SDK, and no additional processing is required. Here is just a description of the signature generation process.

Catalog

1> Signature generation instructions
2> The content of the signature
3> Signature construction example
4> URL encoding considerations
5> Other instructions

Signature Generation Instructions

The signature value sig is the signature value generated by the request source param and secret according to a certain signature method, which is used to improve the tamper-proof modification of the parameters of the transmission process.
There are three steps in the generation of the signature value: construct the source param, construct the secret, and generate the signature value.

The Content of the Signature

The signature source param mainly consists of 4 parts:

1> Http Request Uri 
2> QueryParams in Http URLs
3> Http Request Body
4> Http Request Headers
// Note: The http post method is currently not supported to pass k=v format parameters in the body, currently only application/json is supported

The signature-related header conventions are as follows:

NameDescription
x-app-keyAccess secret id
x-signatureSignature value
x-signature-algorithmSignature algorithm, default HMAC-SHA1
x-signature-versionSignature algorithm version, default 1.0
x-signature-nonceSignature Unique Random Number
x-timestampRequested timestamp, ISO8601 time format, UTC time zone
hosthttp request default: host:port, example: api.webull.com:8080

The signature rules are as follows:

1> Content involved in signature calculation
1.1> http request uri
1.2> http request query params (Note: param is the original data without urlencode)
1.3> http request body
1.4> http request headers, including: x-app-key、x-signature-algorithm、x-signature-version、x-signature-nonce、x-timestamp、host
2> How signature content is organized:
2.1> The query params and headers are organized into a map structure according to k, v, and then sorted according to the value of the k string from small to large, and then connected in the way of k1=v1&k2=v2, and finally get s1
2.2> Perform md5 processing on the http request body, method: upcase(md5(body)), get: s2 (Note: body is empty and does not participate in the signature)
2.3> Concatenate: uri + "&" + s1 + "&" + s2, resulting in: s3
Note: when body is empty, s3 = uri + '&' + s1
3> Generate signature: signature = base64(HMAC-SHA1(app_secret + "&", encoded_sign_string))

Signature Construction Example

Request Content

uri: /trade/place_order

QueryParams:

NameExample ValueRemark
a1webullValue is not urlencoded
a2123Value is not urlencoded
a3xxxValue is not urlencoded
q1yyyValue is not urlencoded

Headers:

NameExample ValueRemark
x-app-key776da210ab4a452795d74e726ebd74b6
x-timestamp2022-01-04T03:55:31Z
x-signature-version1.0
x-signature-algorithmHMAC-SHA1
x-signature-nonce48ef5afed43d4d91ae514aaeafbc29ba
hostapi.webull.com

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

Secret: 0f50a2e853334a9aae1a783bee120c1f

Step1: Construct the Source Param

Sort the dictionary from small to large according to the key, and get the following order data:
Key: a1 , Value: webull
Key: a2 , Value: 123
Key: a3 , Value: xxx
Key: host , Value: api.webull.com
Key: q1 , Value: yyy
Key: x-app-key , Value: 776da210ab4a452795d74e726ebd74b6
Key: x-signature-algorithm , Value: HMAC-SHA1
Key: x-signature-nonce , Value: 48ef5afed43d4d91ae514aaeafbc29ba
Key: x-signature-version , Value: 1.0
Key: x-timestamp , Value: 2022-01-04T03:55:31Z

OriginBody: {"k1":123,"k2":"this is the api request body","k3":true,"k4":{"foo":[1,2]}}
Body MD5: ToUpper(MD5(body)) = E296C96787E1A309691CEF3692F5EEDD
Note: The json string generated by some json tools contains spaces between the key and value, which will cause the validation to fail

The concatenated source param is as follows:
/trade/place_order&a1=webull&a2=123&a3=xxx&host=api.webull.com&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

Get encoded_sign_string after encodeURIComponent:
%2Ftrade%2Fplace_order%26a1%3Dwebull%26a2%3D123%26a3%3Dxxx%26host%3Dapi.webull.com%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: Add Key Secret

The way to get the secret: Add a byte of "&" at the end of the app_secret of the application, that is, app_secret&, for example:

0f50a2e853334a9aae1a783bee120c1f&

Step3: Generate Signed Value

How to generate:: 
signature = base64(HMAC-SHA1(app_secret + "&", encoded_sign_string))

1> Use HMAC-SHA1 encryption algorithm, use the key obtained in Step2 to encrypt the source param obtained in Step1
2> Then base64 encode the encrypted string
3> The result of the obtained signature value is as follows: kvlS6opdZDhEBo5jq40nHYXaLvM=

URL Encoding Considerations

During signature verification, all non-alphanumeric characters in the string except "-", "_", "." are required to be replaced with percent sign (%) followed by two hexadecimal digits, hexadecimal Letters in system numbers must be uppercase.

Other Instructions

1> In some special cases, such as when there are multiple keys in the Query parameter, the values need to be sorted in ascending order by value, and then connected with '&'. For example:
url?k1=v1&k1=v2&k1=v3
After the connection is processed: k1 = v1&v2&v3 , otherwise, the signature will not pass
2> Golang json.Marshal defaults escapeHtml being true, which will escape <, >, &. In this case, the escaped content needs to be processed and replaced with the original characters. The general processing is as follows:
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
}