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