Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to sign an OKEx API request?

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---

  1. 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))

  2. The timestamp value is the same as the OK-ACCESS-TIMESTAMP header with nanosecond precision.

  3. The request method should be UPPER CASE, i.e. GET and POST.

  4. The requestPath is the path of requesting an endpoint. Example: /orders?before=2&limit=30

  5. 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"}

  6. 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()
like image 985
Zoe Fleischer Avatar asked Oct 24 '25 14:10

Zoe Fleischer


2 Answers

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"
like image 135
Rok Povsic Avatar answered Oct 26 '25 05:10

Rok Povsic


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={})
like image 36
valentinmk Avatar answered Oct 26 '25 03:10

valentinmk



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!