Important note: do NOT reveal your 'AccessKey' and 'SecretKey' to anyone. They are as important as your password.
Market data APIs are public, meaning you can make requests without using API keys. However, most of other APIs (e.g. trading) require the clients to authenticate with a pair of API key/secret and sign an API request with a correct signature.
Here's how you create the signature.
Components of a Query Request
Each HTTPS request should contain the following data:
-
Path: api.huobi.us, and the method followed like 'api.huobi.us/v1/order/orders'.
-
Signature: The calculated value that ensures the signature is valid and is not tampered.
For example:
https://api.huobi.us/v1/order/orders?
AccessKeyId=e2xxxxxx-99xxxxxx-84xxxxxx-7xxxx
&SignatureMethod=HmacSHA256
&SignatureVersion=2
&Timestamp=2017-05-11T15%3A19%3A30
&order-id=1234567890
&Signature=calculated-value
How to Generate a Signature
Web service requests are sent across the Internet and are vulnerable to tampering. For security reasons, Huobi US requires a signature as part of every request.
Step 1: Format the Query Request
-
Add mandatory authentication parameters to the query string
- AccessKeyId: The 'AccessKey' distributed by Huobi US when you applied for APIKEY.
- SignatureMethod: The hash-based protocol used to calculate the signature. This should be HmacSHA256.
- SignatureVersion: The version of the signature protocol.This should be 2.
- Timestamp: The time at which you make the request. Include this in the Query request to help prevent third parties from intercepting your request.This should be formatted in UTC time, like '2017-05-11T16:22:06'.
-
Add other path parameters to the query string
- Each action has a set of required and optional query parameters that define the API call. This shows in the API reference. It does not include the request body params in the POST method.
-
Sort all the parameters by name in ascend order and put them into a url encoded string
The following example is the url encoded string
AccessKeyId=e2xxxxxx-99xxxxxx-84xxxxxx-7xxxx
&SignatureMethod=HmacSHA256
&SignatureVersion=2
&Timestamp=2017-05-11T15%3A19%3A30
&order-id=1234567890
Step 2: Struct the signature payload. Connect request method, hostname, path and query with '\n'
The following example is the signature payload
GET\n
api.huobi.us\n
/v1/order/orders\n
AccessKeyId=e2xxxxxx-99xxxxxx-84xxxxxx-7xxxx&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2017-05-11T15%3A19%3A30&order-id=1234567890
Step 3: Calculate the Signature and add it to the query
-
Apply HmacSHA256 hash function with inputs (payload string, secret key) to get the hashed string
-
Encode the hashed string with base-64
-
Add "&Signature=[Your request signature with URL encode]" to your path parameter
The following example is the final request
https://api.huobi.us/v1/order/orders
?AccessKeyId=e2xxxxxx-99xxxxxx-84xxxxxx-7xxxx
&order-id=1234567890
&SignatureMethod=HmacSHA256
&SignatureVersion=2
&Timestamp=2017-05-11T15%3A19%3A30
&Signature=4F65x5A2bLyMWVQj3Aqp%2BB4w%2BivaA7n5Oi2SuYtCJ9o%3D
Here is the code example
import base64
import hmac
import hashlib
import json
import urllib
import datetime
import requests
from urllib.parse import urlparse, urlencode
ACCESS_KEY = ""
SECRET_KEY = ""
def publicReq(url, method, data=None, params=None, headers=None):
if method not in ['GET', 'POST']:
raise Exception('method can only be GET or POST')
# default values
if data is None:
data = {}
if params is None:
params = {}
if headers is None:
headers = {}
# need to parse them to string before calling
params = urlencode(params)
data = json.dumps(data)
response = ""
if method == 'POST':
headers.update({
"Accept": "application/json",
"Content-Type": "application/json",
"Cache-Control": "no-cache"
})
try:
res = requests.post(url, params=params, data=data, headers=headers, timeout=TIMEOUT)
response = res.text
except Exception as e:
return json.dumps({"status":"fail","msg":e})
elif method == 'GET':
headers.update({
"Content-type": "application/x-www-form-urlencoded",
"Cache-Control": "no-cache"
})
try:
res = requests.get(url, params=params, headers=headers, timeout=TIMEOUT)
response = res.text
except Exception as e:
return json.dumps({"status":"fail","msg":e})
return response
def privateReq(url, method, data=None, params=None, ACCESS_KEY=ACCESS_KEY, SECRET_KEY=SECRET_KEY):
if method not in ['GET', 'POST']:
raise Exception('method can only be GET or POST')
if not ACCESS_KEY or not SECRET_KEY:
raise Exception('ACCESS_KEY and SECRET_KEY must not be empty')
# default values
if data is None:
data = {}
if params is None:
params = {}
createSig(params, method, url, ACCESS_KEY, SECRET_KEY)
return publicReq(url, method, data, params)
def createSig(params, method, url, ACCESS_KEY=ACCESS_KEY, SECRET_KEY=SECRET_KEY):
# Add mandatory authentication parameters to the query string
timestamp = datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S')
params.update({'AccessKeyId': ACCESS_KEY,
'SignatureMethod': 'HmacSHA256',
'SignatureVersion': '2',
'Timestamp': timestamp})
# Sort all the parameters by name in ascend order
sorted_params = sorted(params.items(), key=lambda d: d[0], reverse=False)
# put them into a url encoded string
encode_params = urlencode(sorted_params)
# struct the signature payload
parsedURL = urlparse(url)
payload = [method, parsedURL.hostname, parsedURL.path, encode_params]
payload = '\n'.join(payload)
payload = payload.encode(encoding='UTF8')
sec = SECRET_KEY.encode(encoding='UTF8')
# Calculate the Signature and add it to the query
digest = hmac.new(sec, payload, digestmod=hashlib.sha256).digest()
signature = base64.b64encode(digest)
signature = signature.decode()
params['Signature'] = signature
return