I am not able to send POST request to OKEx API. However, with GET method, everything is ok - I can check my account balance. As soon I send POST request I've received that error
{'msg': 'Invalid Sign', 'code': '50113'}
class OkexBot:
def __init__(self, APIKEY: str, APISECRET: str, PASS: str):
self.apikey = APIKEY
self.apisecret = APISECRET
self.password = PASS
self.baseURL = 'https://okex.com'
#staticmethod
def get_time():
return dt.datetime.utcnow().isoformat()[:-3] + 'Z'
#staticmethod
def signature(timestamp, method, request_path, body, secret_key):
if str(body) == '{}' or str(body) == 'None':
body = ''
else:
body = json.dumps(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')
output = mac.digest()
return base64.b64encode(output)
def get_header(self, request='GET', endpoint='', body: dict = dict()):
cur_time = self.get_time()
header = dict()
header['CONTENT-TYPE'] = 'application/json'
header['OK-ACCESS-KEY'] = APIKEY
header['OK-ACCESS-SIGN'] = self.signature(cur_time, request, endpoint, body, APISECRET)
header['OK-ACCESS-TIMESTAMP'] = str(cur_time)
header['OK-ACCESS-PASSPHRASE'] = PASS
return header
def place_market_order(self, pair, side, amount, tdMode='cash'):
endpoint = '/api/v5/trade/order'
url = self.baseURL + endpoint
request = 'POST'
body = {
"instId": pair,
"tdMode": tdMode,
"side": side,
"ordType": "market",
"sz": str(amount)
}
body = json.dumps(body)
header = self.get_header(endpoint=endpoint, request=request, body=body)
response = requests.post(url=url, headers=header, data=body)
return response
I looked into this topic
How to sign an OKEx POST API request?
Unable to send authenticated OKEx API POST requests with body (in python). 401 Signature invalid. Authenticated GET requests work
Unable to send a post requests OKEX Invalid Signature
but nothing was helpful.
This code is vaid.
class OkexBot:
def __init__(self, APIKEY: str, APISECRET: str, PASS: str):
self.apikey = APIKEY
self.apisecret = APISECRET
self.password = PASS
self.baseURL = 'https://www.okex.com'
#staticmethod
def get_time():
return dt.datetime.utcnow().isoformat()[:-3] + 'Z'
#staticmethod
def signature(timestamp, method, request_path, body, secret_key):
message = timestamp + method + request_path + body
mac = hmac.new(bytes(secret_key, encoding='utf8'), bytes(message, encoding='utf-8'), digestmod='sha256')
output = mac.digest()
return base64.b64encode(output)
def get_header(self, request='GET', endpoint='', body=''):
cur_time = self.get_time()
header = dict()
header['CONTENT-TYPE'] = "application/json"
header['OK-ACCESS-KEY'] = APIKEY
header['OK-ACCESS-SIGN'] = self.signature(cur_time, request, endpoint, body, APISECRET)
header['OK-ACCESS-TIMESTAMP'] = cur_time
header['OK-ACCESS-PASSPHRASE'] = PASS
return header
def place_market_order(self, pair, side, amount, tdMode='cash'):
endpoint = '/api/v5/trade/order'
url = self.baseURL + '/api/v5/trade/order'
request = 'POST'
body = {
"instId": pair,
"tdMode": tdMode,
"side": side,
"ordType": "market",
"sz": str(Decimal(str(amount)))
}
body = json.dumps(body)
header = self.get_header(request, endpoint, str(body))
print(header)
response = requests.post(url, headers=header, data=body)
return response
Related
The below is a result of this question How to sign an OKEx API request? and some of the answers:
import hmac
import base64
import requests
import datetime
import json
from config import KEY, SECRET, PASS, ROOT_URL
def get_time():
now = datetime.datetime.utcnow()
t = now.isoformat("T", "milliseconds")
return t + "Z"
def signature(timestamp, request_type, endpoint, body, secret):
if body != '':
body = json.dumps(body)
message = str(timestamp) + str.upper(request_type) + endpoint + body
print(message)
mac = hmac.new(bytes(secret, encoding='utf-8'), bytes(message, encoding='utf-8'), digestmod='sha256')
d = mac.digest()
return base64.b64encode(d)
def get_header(request_type, endpoint, body):
time = get_time()
header = dict()
header['CONTENT-TYPE'] = 'application/json'
header['OK-ACCESS-KEY'] = KEY
header['OK-ACCESS-SIGN'] = signature(time, request_type, endpoint, body, SECRET)
header['OK-ACCESS-TIMESTAMP'] = str(time)
header['OK-ACCESS-PASSPHRASE'] = PASS
return header
def get(endpoint, body=''):
url = ROOT_URL + endpoint
header = get_header('GET', endpoint, body)
return requests.get(url, headers=header)
def post(endpoint, body=''):
url = ROOT_URL + endpoint
header = get_header('POST', endpoint, body)
return requests.post(url, headers=header)
where KEY, SECRET, PASS are the API key, secret key, and pass phrase respectively; The ROOT_URL is 'https://www.okex.com'.
The Problem
GET requests work absolutely fine, so when I run the following, there are no issues:
ENDPOINT = '/api/v5/account/balance'
BODY = ''
response = get(ENDPOINT)
response.json()
However, when I try to place an order via a POST request, like so:
ENDPOINT = '/api/v5/trade/order'
BODY = {"instId":"BTC-USDT",
"tdMode":"cash",
"side":"buy",
"ordType":"market",
"sz":"1"}
response = post(ENDPOINT, body=BODY)
response.json()
I get the following output, i.e. it won't accept the signature:
{'msg': 'Invalid Sign', 'code': '50113'}
Related Questions
In this one Can't figure out how to send a signed POST request to OKEx an answer was provided, but it does not work for me as I was already using the suggested URL. More or less the same question was asked here Unable to send a post requests OKEX Invalid Signature, but no activity likely due to the format, so I thought I would repost and elaborate.
OKEX Docs
The docs simply specify that The API endpoints of Trade require authentication (https://www.okex.com/docs-v5/en/?python#rest-api-authentication-signature). But they make no reference to there being any difference between the two methods. Away from that, I am including all required parameters in the body of the post request as far as I can see.
I would appreciate any input on this.
Many thanks!
I ran into the same POST problem and figured it out. I used new domain name okex.com. Here is my code.
def set_userinfo(self):
position_path = "/api/v5/account/set-position-mode"
try:
self.get_header("POST", position_path, {"posMode":"net_mode"})
resp = requests.post(url=self.base_url+position_path, headers=self.headers, json={"posMode":"long_short_mode"}).json()
except Exception as e:
log.error("OK set_userinfo error={} type={}".format(f'{e}', f'{type(e)}'))
def get_header(self, request_type, endpoint, body=''):
timestamp = self.get_time()
self.headers["OK-ACCESS-TIMESTAMP"] = timestamp
self.headers["OK-ACCESS-SIGN"] = self.signature(timestamp, request_type, endpoint, body)
def signature(self, timestamp, request_type, endpoint, body):
if body != '':
body = json.dumps(body)
message = str(timestamp) + str.upper(request_type) + endpoint + body
mac = hmac.new(bytes(self.secret_key, encoding='utf-8'), bytes(message, encoding='utf-8'), digestmod='sha256').digest()
return base64.b64encode(mac)
I have fix the same problem.
Both of the 'body' in signature() and in get_header() should be json.
So you should add following code:
if str(body) == '{}' or str(body) == 'None':
body = ''
else:
body = json.dumps(body)
I ran into the same problem and solved it using below code snippet, the idea is from https://stackoverflow.com/a/68115787/20497127, but I modified a little by adding POST functionality
APIKEY = "" # input key
APISECRET = "" #input secret
PASS = "" #input passphrase
BASE_URL = 'https://www.okx.com'
def send_signed_request(http_method, url_path, payload={}):
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
# demo trading: need to set x-simulated-trading=1, live trading is 0
header['x-simulated-trading'] = '1'
return header
url = BASE_URL + url_path
header = get_header(http_method, url_path, payload)
print(url)
print(header)
if http_method == 'GET':
response = requests.get(url, headers=header)
elif http_method == 'POST':
response = requests.post(url, headers=header, data=payload)
return response.json()
# this will run get requests
res = send_signed_request("GET", "/api/v5/account/balance", payload={})
# this will run post requests
data = {
"instId": "BTC-USDT",
"tdMode": "cross",
"side": "sell",
"ccy":"USDT",
"ordType": "limit",
"px": "100000",
"sz": "0.01"
}
res = send_signed_request("POST", "/api/v5/trade/order", payload=json.dumps(data))
I built a python client REST API wrapper for an Okta authenticated pricing API. My code runs but I am not getting any response back. I need to be able to get a JSON response. I believe a print statement would work but I hit a wall do not know what argument to pass the print statement in order to receive a response from
"conn = http.client.HTTPSConnection("sso.lukka.tech")"
import http.client
import urllib.request, urllib.parse, urllib.error
import json
import base64
def loadJson(response):
body = response.read()
if body == "" or body is None:
print(("Empty response found with status " + str(response.status)))
return {}
else:
return json.loads(body)
class DataPricingClient:
def __init__(self, host, clientId, clientSecret):
conn = http.client.HTTPSConnection("sso.lukka.tech")
path = "/oauth2/aus1imo2fqcx5Ik4Q0h8/v1/token"
encodedData = base64.b64encode(bytes(f"{clientId}:{clientSecret}", "ISO-8859-1")).decode("ascii")
authHeader = "Basic " + encodedData
headers = {
"Authorization": authHeader,
"Accept": "application/json",
"Content-Type": "application/x-www-form-urlencoded"
}
params = urllib.parse.urlencode({
"grant_type": "client_credentials",
"scope": "pricing"
})
conn.request("POST", path, params, headers)
response = conn.getresponse()
if response.status != 200:
raise ApiErrorException(response.status, "Failed to get access token")
self.host = host
self.accessToken = loadJson(response)["access_token"]
def default_headers(self):
return {
"Content-Type": "application/json",
"Accept": "application/json",
"Authorization": "Bearer " + self.accessToken
}
def _send_request(self,path):
with http.client.HTTPSConnection(self.host) as conn:
headers = self.default_headers()
conn.request("GET", path, None, headers)
response = conn.getresponse()
return response
def get_available_sources(self):
path = "/v1/pricing/sources"
return _send_request(path)
def get_source_details(self, sourceId):
path = f"/v1/pricing/sources/{sourceId}"
return _send_request(path)
def get_latest_prices(self,asOf, sourceId, pairCodes):
path = f"/v1/pricing/sources/{sourceId} {pairCodes}"
return _send_request(path)
def historical_prices(self, sourceId, pairCode, begTs, endTs, fill, limit, variances):
path = f"/v1/pricing/sources/{sourceId} {pairCode} {begTs} {endTs} {fill} {limit} {variances}"
return _send_request(path)
class ApiErrorException(Exception):
def __init__(self, status, msg):
self.msg = "Error " + str(status) + ": " + msg
def __str__(self):
return self.msg
if __name__ == '__main__':
from pricing_api_creds import lukka_pricing
c = DataPricingClient(**lukka_pricing)
The problem you have is because of a redirect. The API is basically telling you you have to go to a different page. You can do that manually (by yourself) or use a library to do that. One way to do it with http.client is like in this answer: https://stackoverflow.com/a/20475712/3352383
But I would suggest you to use requests because it does it automatically and usually it is also easier to use. Good luck!
I created this method after running into issues with my token timing out. The call worked when it was just a function and not part of a class. Here is the api class I created to call the msgraph. It successfully gets the token and stores it. I am just trying to call the method of get_users. The error is
<bound method msGraph.Decorators.refreshToken.<locals>.wrapper of <msgraph.msGraph object at 0x0197F628>>
class msGraph():
token = None
token_expiration = None
tenantid = None
user = None
apiurl = None
def __init__(self, tenantid):
self.tenantid = tenantid
try:
self.token = self.gettoken()
if self.token is None:
raise Exception("Request for access token failed.")
except Exception as e:
print(e)
else:
self.token_expiration = time.time() + 1800
def gettoken(self):
try:
apiurl = "https://login.microsoftonline.com/{}/oauth2/v2.0/token".format(self.tenantid)
headers = {'content-type': 'application/x-www-form-urlencoded'}
data = {'grant_type': 'client_credentials',
'client_id': 'xxx',
'client_secret': "xxx",
'scope': 'https://graph.microsoft.com/.default'}
newdata = urllib.parse.urlencode(data)
token = requests.post(apiurl, data=newdata, headers=headers).json()
except Exception as e:
print(e)
return None
else:
return token['access_token']
class Decorators():
#staticmethod
def refreshToken(decorated):
def wrapper(api, *args, **kwargs):
if time.time() > api.token_expiration:
api.gettoken
return decorated(api, *args, **kwargs)
return wrapper
#Decorators.refreshToken
def get_users(self):
try:
print('trying to get users')
apiurl = "https://graph.microsoft.com/v1.0/users"
authheader = "Bearer {}".format(self.token)
print(authheader)
header = {'Authorization': authheader, 'content-type': 'application/json'}
response = requests.get(apiurl, headers=header).json()
except Exception as e:
print(e)
return None
else:
users = response['value']
userList = []
for user in users:
if "#EXT" not in user['userPrincipalName']:
userList.append(user['userPrincipalName'])
return userList
This is how I am accessing it from the main script
main.py
from msgraph import msGraph
from processemails import userscan
tenant_id = "xxx"
company_Domain = "xxx"
# Press the green button in the gutter to run the script.
if __name__ == '__main__':
api = msGraph(tenant_id)
userList = api.get_users()
for user in userList:
userscan(user, company_Domain, api)
quit()
I think you meant to write
userList = api.get_users()
with the parenthesis. Without the parenthesis, what you're assigning to userList is the function object, instead of the result of calling the function. And a function object is not an iterable, hence your error message.
This is my code so far. It was hard work, but now I can't solve this error by myself. Can someone see why the 'content'-argument is missing ?
This is the curl-command what I've tested.
curl -X POST 127.0.0.1:1234/sort -H "Content-Type: application/json" -d #payload.json
But it throws a TypeError. Do I have to convert the json in a dict ? How can I do that? Thanks in advance.
It works properly with the http_utils-module. And I also import the csv-module for the function to return movie titles and the json-module for the list sort.
"""HTTP server module."""
import csv
import json
from datetime import datetime
from http_utils import TcpSocket
CRLF = "\r\n"
FILE_PATH = "film.csv"
REQUEST_TPL = "method: {}\nresource: {}\nversion: {}\nheaders: {}\nbody: {}\n"
RESPONSE_TPL = "status code: {}\nstatus msg: {}\nheaders: {}\nbody: {}\n"
STATUS_CODES = {
200: "OK",
404: "Not Found",
405: "Not Allowed"
}
HTTP_RESPONSE_TPL = "{} {} {}\r\n{}\r\n\r\n{}\r\n"
def json_content(func):
"""Converts function return value to JSON."""
def wrapper(*args, **kwargs):
content = func(*args, **kwargs)
if content is None:
return (404, STATUS_CODES[404])
return (200, json.dumps(content))
return wrapper
class Request:
"""An HTTP Request."""
def __init__(self, method, resource, http_version, headers, body):
"""Initializes request."""
self.method = method
self.resource = resource
self.http_version = http_version
self.headers = headers
self.body = body
def __repr__(self):
"""Returns request attributes formatted as a string."""
return REQUEST_TPL.format(
self.method,
self.resource,
self.http_version,
self.headers,
self.body
)
class Response:
"""An HTTP Response."""
def __init__(self, http_version, status_code, status_message, headers, body):
"""Initializes request."""
self.http_version = http_version
self.status_code = status_code
self.status_message = STATUS_CODES[status_code]
self.headers = headers
self.body = body
def __repr__(self):
"""Return response attributes formatted as a string."""
return RESPONSE_TPL.format(
self.status_code,
self.status_message,
self.headers,
self.body
)
class HTTPServer:
"""A simple HTTP server."""
def __init__(self, host, port):
"""Initializes server."""
self.host = host
self.port = port
self.socket = None
self.collections = {
"headers": {
"GET": self.get_request_headers
},
"movies": {
"GET": self.search_movie_title
},
"sort": {
"POST": self.sort_numbers
}
}
def __call__(self):
"""Starts server and execute logic."""
with TcpSocket(self.host, self.port) as self.socket:
while True:
raw_request = self.recieve_request()
request = self.process_request(raw_request)
response = self.handle_request(request)
data = self.send_response(response)
def recieve_request(self):
"""Recieves raw data by client."""
return self.socket.recieve_data()
def process_request(self, raw_request):
"""Processes raw request to Request object."""
request_segments = raw_request.decode().split(CRLF)
(method, resource, http_version) = request_segments[0].split(" ")
headers = self._get_headers(request_segments[1:-2])
body = self._get_body(request_segments[-1])
return Request(method, resource, http_version, headers, body)
def handle_request(self, request):
"""Handles request and creates response instance."""
status_code = 200
requested_collection = request.resource.split("/")[1]
http_method = request.method
try:
collection = self.collections[requested_collection]
except KeyError:
status_code = 404
else:
try:
method = collection[http_method]
except KeyError:
status_code = 405
else:
(status_code, content) = method(request)
if status_code != 200:
content = STATUS_CODES[status_code]
status_message = STATUS_CODES[status_code]
headers = self._generate_response_headers(content)
http_version = request.http_version
return Response(http_version, status_code, status_message, headers, content)
def send_response(self, response):
"""Deserializes response object and sends HTTP response to client."""
headers = "\r\n".join(
f"{key}: {value}" for (key, value) in response.headers.items()
)
response_data = HTTP_RESPONSE_TPL.format(
response.http_version,
response.status_code,
response.status_message,
headers,
response.body
).encode("utf-8")
self.socket.send_data(response_data)
return response_data
#staticmethod
def _get_headers(raw_headers):
"""Puts headers inside a dict."""
headers = {}
for header in raw_headers:
(key, value) = header.split(": ")
headers[key] = value
return headers
#staticmethod
def _get_body(raw_body):
"""Put JSON body inside a dict."""
return json.loads(raw_body) if raw_body else None
def _generate_response_headers(self, content):
"""Generates response headers."""
return {
"Content-Type": "application/json",
"Host": f"{self.host}:{self.port}",
"Date": datetime.now().ctime(),
"Content-Length": len(content)
}
#staticmethod
#json_content
def sort_numbers(request):
"""Sorts list of numbers."""
if not request.body:
return []
numbers = request.body["input"]
return sorted(numbers)
#staticmethod
#json_content
def get_request_headers(request):
"""gets headers of request."""
return request.headers
#staticmethod
#json_content
def search_movie_title(request):
"""Searches movie title."""
movie_title = request.resource.split("/")[-1]
with open(FILE_PATH, encoding="latin-1", newline="") as csv_file:
reader = csv.DictReader(csv_file, delimiter=";")
for row in reader:
if row["Title"] == movie_title:
return row
return None
if __name__ == "__main__":
server = HTTPServer("127.0.0.1", 7777)
server()
I've been trying to find a solution here for my problem for a long time but nothing seems to work. I am trying to authenticate with the Coinbase Pro API in Python but I am getting
"Unicode-Objects must be encoded before hashing" error.
Please see the code below for your reference.
import json, hmac, hashlib, time, requests, base64
from requests.auth import AuthBase
# Create custom authentication for Exchange
class CoinbaseExchangeAuth(AuthBase):
def __init__(self, api_key, secret_key, passphrase):
self.api_key = api_key
self.secret_key = secret_key
self.passphrase = passphrase
def __call__(self, request):
timestamp = str(time.time())
message = timestamp + request.method + request.path_url + (request.body or '')
hmac_key = base64.b64decode(self.secret_key)
signature = hmac.new(hmac_key, message, hashlib.sha256)
signature_b64 = signature.digest().encode('base64').rstrip('\n')
request.headers.update({
'CB-ACCESS-SIGN': signature_b64,
'CB-ACCESS-TIMESTAMP': timestamp,
'CB-ACCESS-KEY': self.api_key,
'CB-ACCESS-PASSPHRASE': self.passphrase,
'Content-Type': 'application/json'
})
return request
api_url = 'https://api.pro.coinbase.com/'
auth = CoinbaseExchangeAuth(api_key='XXXX',
secret_key='XXXX',
passphrase='XXXX')
# Get accounts
r = requests.get(api_url + 'accounts', auth=auth)
It would be great if someone can point me to the right direction. I couldn't find the correct solution after searching.
hashlib takes bytes or bytearray. Try this -
...
message = message.encode('UTF-8')
hmac_key = self.secret_key.encode('UTF-8')
signature = hmac.new(hmac_key, message, hashlib.sha256)
After some debugging I found that the following solution worked for me:
def __call__(self, request):
print(request.body)
timestamp = str(time.time())
message = (timestamp + (request.method).upper() + request.path_url + (request.body or ''))
message = message.encode('UTF-8')
hmac_key = base64.b64decode(self.secret_key)
signature = hmac.new(hmac_key, message, hashlib.sha256).digest()
signature_b64 = base64.b64encode(signature)
Here is how I solved it, message.encode('utf-8')
def __call__(self, request):
timestamp = str(time.time())
message = timestamp + request.method + request.path_url + (request.body or '')
hmac_key = base64.b64decode(self.secret_key)
signature = hmac.new(hmac_key, message.encode('utf-8'), hashlib.sha256)
signature_b64 = base64.b64encode(signature.digest())