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.
By default, API Keys can only read basic data, such as positions, margin, orders, and executions. They cannot submit orders or withdraw.
Permissions may be added to a key to make it more useful. It is good security practice to only add the permissions that you need, and no more. The available permissions are as follows:
/order
and /position
functions are available./order/cancel
, /order/cancelAll
and /order/cancelAllAfter
(and the associated WebSocket operation) are usable.
orderCancel
is mutually exclusive with order
. You may only enable one of the two. orderCancel
is intended for risk/audit
processes, not for trading./user/requestWithdrawal
. Unless the withdrawal is made to a saved address with the Skip Confirm
featured turned on, an email withdrawal will still be required.API Keys cannot be used to create other API Keys, or to modify the user in any way, including preferences, authentication, two-factor, or KYC data.
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
.
This timestamp is compared against our system time before reaching the trading engine. It cannot be used as a mechanism to cancel submission of an order that is waiting in queue to be processed.
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 described an
api-nonce
value, which is a value that should increase between the bounds of 0 and 253. This scheme is no longer supported. It has significant problems with multithreaded clients and should not be used. Do not use it for new applications.
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.
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))
#
# POST
#
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:
Content-Length
and Content-Type
are valid.We have created several example connectors that implement the above authentication:
A Python snippet:
import time
import hashlib
import hmac
import urllib.parse
# Generates an API signature.
# A signature is HMAC_SHA256(secret, verb + path + expires + data), hex encoded.
# Verb must be uppercased, url is relative, expires must be unix timestamp (in seconds)
# 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 = urllib.parse.urlparse(url)
path = parsedURL.path
if parsedURL.query:
path = path + '?' + parsedURL.query
if isinstance(data, (bytes, bytearray)):
data = data.decode('utf8')
message = bytes(verb + path + str(nonce) + data, 'utf-8')
print("Computing HMAC: %s" % message)
signature = hmac.new(bytes(secret, 'utf-8'), message, digestmod=hashlib.sha256).hexdigest()
return signature
#
# Testing
#
expires = 1518064236
# Or you might generate it like so:
# expires = int(round(time.time()) + 5)
# Prints 'c7682d435d0cfe87c16098df34ef2eb5a549d4c5a3c2b1f0f77b8af73423bf00'
print(generate_signature('chNOOS4KvNXR_Xq4k4c9qsfoKWvnDecLATCRlcBwyKDYnWgO', 'GET', '/api/v1/instrument', expires, '')) #