I keep getting an invalid sign error when trying to authenticate to the OKEx API, but am unable to see why my sign is not going through. Another eye might help?
Here is some context from the OKEx API documentation:
*---Signing Messages---
The OK-ACCESS-SIGN header is generated as follows: create a prehash
string of timestamp + method + requestPath + body (where +
represents String concatenation) prepare the Secret sign the prehash
string with the Secret using the HMAC SHA256 encode the signature in
the Base64 format Example:
sign=CryptoJS.enc.Base64.stringify(CryptoJS.HmacSHA256(timestamp + 'GET' + '/users/self/verify', SecretKey))
The timestamp value is the same as the OK-ACCESS-TIMESTAMP header with nanosecond precision.
The request method should be UPPER CASE, i.e. GET and POST.
The requestPath is the path of requesting an endpoint. Example:
/orders?before=2&limit=30
The body refers to the String of the request body. It can be omitted if there is no request body
(frequently the case for GET requests). Example:
{"product_id":"BTC-USD-0309","order_id":"377454671037440"}
The SecretKey is generated when you create an APIKey. Example:
22582BD0CFF14C41EDBF1AB98506286D*
import hmac
import base64
import requests
import json
from Secrets import okex_key
from Secrets import okex_secret
from Secrets import okex_pass
#get time
def get_time():
urltime= 'https://www.okex.com/api/general/v3/time'
response=requests.get(urltime)
time=response.json()
time=time['iso']
return time
# signature
def signature(timestamp, method, request_path, body,secret_key):
if str(body) == '{}' or str(body) == 'None':
body = ''
message = str(timestamp) + str.upper(method) + request_path + str(body)
mac = hmac.new(bytes(secret_key, encoding='utf8'), bytes(message, encoding='utf-8'), digestmod='sha256')
d = mac.digest()
return base64.b64encode(d)
# set request header
def get_header():
body= {}
request= 'GET'
endpoint= '/api/spot/v3/accounts'
header = dict()
header['CONTENT-TYPE'] = 'application/json'
header['OK-ACCESS-KEY'] = okex_key
header['OK-ACCESS-SIGN'] = signature(get_time(), request, endpoint , body, okex_secret)
header['OK-ACCESS-TIMESTAMP'] = str(get_time())
header['OK-ACCESS-PASSPHRASE'] = okex_pass
return header
url = 'http://www.okex.com/api/spot/v3/accounts'
header = get_header()
response= requests.get(url, headers=header)
response.json()
The problem is that you are giving two different time values when calculating the values for OK-ACCESS-SIGN and OK-ACCESS-TIMESTAMP. Put get_time() into a single variable and use it in both places.
current_time = get_time()
header['OK-ACCESS-SIGN'] = signature(current_time, request, endpoint , body, okex_secret)
header['OK-ACCESS-TIMESTAMP'] = str(current_time)
Also note that currently, in get_time, you ask OKEx's servers what time it is. But if your computer's local time is reasonably correct (which you can check at https://time.is, or use NTP on a server) you can avoid doing that HTTP request and just use the local time.
import datetime
def get_time():
now = datetime.datetime.utcnow()
t = now.isoformat("T", "milliseconds")
return t + "Z"
This is my solution for signing request to OKEx API. Thanks to zoe-fleischer and rok-povsic.
import base64
import datetime as dt
import hmac
import requests
APIKEY = "xxxxx"
APISECRET = "xxxx"
PASS = "xxxx"
BASE_URL = 'https://aws.okex.com'
def send_signed_request(http_method, url_path, payload={}):
'''
See https://stackoverflow.com/questions/66486374/how-to-sign-an-okex-api-request
'''
def get_time():
return dt.datetime.utcnow().isoformat()[:-3]+'Z'
def signature(timestamp, method, request_path, body, secret_key):
if str(body) == '{}' or str(body) == 'None':
body = ''
message = str(timestamp) + str.upper(method) + request_path + str(body)
mac = hmac.new(bytes(secret_key, encoding='utf8'), bytes(message, encoding='utf-8'), digestmod='sha256')
d = mac.digest()
return base64.b64encode(d)
# set request header
def get_header(request='GET', endpoint='', body:dict=dict()):
cur_time = get_time()
header = dict()
header['CONTENT-TYPE'] = 'application/json'
header['OK-ACCESS-KEY'] = APIKEY
header['OK-ACCESS-SIGN'] = signature(cur_time, request, endpoint , body, APISECRET)
header['OK-ACCESS-TIMESTAMP'] = str(cur_time)
header['OK-ACCESS-PASSPHRASE'] = PASS
return header
url = BASE_URL + url_path
header = get_header(http_method, url_path, payload)
print(url)
print(header)
response = requests.get(url, headers=header)
response.json()
return response.json()
send_signed_request("GET", "/api/v5/account/balance", payload={})
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With