Attention - Important Notice

In accordance with Section 1.4 of our Terms of Service:
  • Persons that are located in or a resident of the United States of America or Québec (Canada) are prohibited from holding positions or entering into contracts at BitMEX.
  • Persons that are located in or a resident of Cuba, Crimea and Sevastopol, Iran, Syria, North Korea and Sudan, or any other jurisdiction where the services offered by BitMEX are restricted, are prohibited from holding positions or entering into contracts at BitMEX.
  • BitMEX reserves the right to immediately close the accounts and to liquidate the open positions of persons determined to have breached our Terms of Service.
AllXBTUSD3651.0+3.78%XBTH193552.0+2.84%XBTZ183625.0+3.45%XBTM193498.0+1.86%XBT7D_D950.00072-52.00%XBT7D_U1050.00690+13.11%ADAZ180.00000940+1.18%ADAH190.00000940+2.17%BCHZ180.0283+10.98%BCHH190.0278+10.76%EOSZ180.0007075+4.41%EOSH190.0006958+3.84%ETHUSD100.40+5.91%ETHZ180.02742+1.59%ETHH190.02733+1.86%LTCZ180.00815-1.33%LTCH190.00802-1.47%TRXZ180.00000424+4.95%TRXH190.00000414+1.22%XRPZ180.00009655+2.46%XRPH190.00009621+2.78%.BXBT3653.91+3.74%.BETH100.25+5.84%.BVOL24H3.77-10.24%Funding: 03:40:13 @ -0.0747%Time: 12:19:46 AM
API Keys Usage

Below we outline the technical and usage details of an API Key.

If you are logged in, please follow this link to manage your active keys.

API Key Permissions

By default, API Keys can only read basic user data, such as positions, margin, orders, and executions. They cannot submit orders or withdraw.

If you wish to execute orders with your API Key, you must add the "order" permission upon creation.

Withdrawals are also possible with the "withdraw" permission. Withdrawals done via API Key are not confirmed via email. Use this permission with caution.

API Keys cannot be used to create other API Keys or modify user authentication.

Authenticating with an API Key

Authentication is done by sending the following HTTP headers:

api-expires: A UNIX timestamp after which the request is no longer valid. This is to prevent replay attacks.

UNIX timestamps are in seconds. For example, 2018-02-08T04:30:37Z is 1518064237.

api-key: Your public API key. This the id param returned when you create an API Key via the API.

api-signature: A signature of the request you are making. It is calculated as hex(HMAC_SHA256(apiSecret, verb + path + expires + data)). See the example calculations below.

Our reference market maker bot features a working implementation of our API key authentication.

Note: Previous versions of this document descripted an api-nonce value, which is a value that should increase between the bounds of 0 and 253. This scheme, while still supported, has significant problems with multithreaded clients and should not be used. Do not use it for new applications.

The ‘data’ param

The data part of the HMAC construction should be exactly equal to the raw body you send to the server. You can send JSON or form encoding, just ensure you use the exact same body string in the HMAC. Generally you will want to prepare the request in your language of choice, then use the same raw body string for the HMAC construction as in the request body.

Full sample calculation

Use these calculations as test cases in your code.

apiKey = 'LAqUlngMIQkIUjXMUreyu3qn'
apiSecret = 'chNOOS4KvNXR_Xq4k4c9qsfoKWvnDecLATCRlcBwyKDYnWgO'

# Simple GET
verb = 'GET'
# Note url-encoding on querystring - this is '/api/v1/instrument?filter={"symbol": "XBTM15"}'
path = '/api/v1/instrument'
expires = 1518064236 # 2018-02-08T04:30:36Z
data = ''

# HEX(HMAC_SHA256(apiSecret, 'GET/api/v1/instrument1518064236'))
# Result is:
# 'c7682d435d0cfe87c16098df34ef2eb5a549d4c5a3c2b1f0f77b8af73423bf00'
signature = HEX(HMAC_SHA256(apiSecret, verb + path + str(expires) + data))

# GET with complex querystring (value is URL-encoded)
verb = 'GET'
# Note url-encoding on querystring - this is '/api/v1/instrument?filter={"symbol": "XBTM15"}'
# Be sure to HMAC *exactly* what is sent on the wire
path = '/api/v1/instrument?filter=%7B%22symbol%22%3A+%22XBTM15%22%7D'
expires = 1518064237 # 2018-02-08T04:30:37Z
data = ''

# HEX(HMAC_SHA256(apiSecret, 'GET/api/v1/instrument?filter=%7B%22symbol%22%3A+%22XBTM15%22%7D1518064237'))
# Result is:
# 'e2f422547eecb5b3cb29ade2127e21b858b235b386bfa45e1c1756eb3383919f'
signature = HEX(HMAC_SHA256(apiSecret, verb + path + str(expires) + data))

verb = 'POST'
path = '/api/v1/order'
expires = 1518064238 # 2018-02-08T04:30:38Z
data = '{"symbol":"XBTM15","price":219.0,"clOrdID":"mm_bitmex_1a/oemUeQ4CAJZgP3fjHsA","orderQty":98}'

# HEX(HMAC_SHA256(apiSecret, 'POST/api/v1/order1518064238{"symbol":"XBTM15","price":219.0,"clOrdID":"mm_bitmex_1a/oemUeQ4CAJZgP3fjHsA","orderQty":98}'))
# Result is:
# '1749cd2ccae4aa49048ae09f0b95110cee706e0944e6a14ad0b3a8cb45bd336b'
signature = HEX(HMAC_SHA256(apiSecret, verb + path + str(expires) + data))


If you are receiving "Signature Not Valid" messages, check the following:

  • Check that your signatures match the sample signatures above.
  • If there is a request body, make sure your Content-Length and Content-Type are valid.
  • Ensure your request body is being properly sent. Try a few sample requests against httpbin.
  • Ensure you are signing the exact string that is being sent to the server. Certain JSON serializers have unstable key ordering, so serialize to a string first, sign that string, and then send the same string in the request body.

Sample Code

We have created several example connectors that implement the above authentication:

A Python snippet:

import time
import hashlib
import hmac

# Generates an API signature.
# A signature is HMAC_SHA256(secret, verb + path + expires + data), hex encoded.
# Verb must be uppercased, url is relative, nonce must be an increasing 64-bit integer
# and the data, if present, must be JSON without whitespace between keys.
def generate_signature(secret, verb, url, expires, data):
    """Generate a request signature compatible with BitMEX."""
    # Parse the url so we can remove the base and extract just the path.
    parsedURL = urlparse(url)
    path = parsedURL.path
    if parsedURL.query:
        path = path + '?' + parsedURL.query

    if isinstance(data, (bytes, bytearray)):
        data = data.decode('utf8')

    print("Computing HMAC: %s" % verb + path + str(expires) + data)
    message = verb + path + str(expires) + data

    signature =, 'utf8'), bytes(message, 'utf8'), digestmod=hashlib.sha256).hexdigest()
    return signature

expires = 1518064236
# Or you might generate it like so:
# expires = int(round(time.time()) + 5)
print(generate_signature('chNOOS4KvNXR_Xq4k4c9qsfoKWvnDecLATCRlcBwyKDYnWgO', 'GET', '/api/v1/instrument', expires, ''))