How to Create API Signature with application/x-www-form-urlencoded - python

First attempt at connecting to a Bitcoin exchange's private API and I am already stuck in trying to do a Test call with my code.
from urllib2 import Request, urlopen
from urllib import urlencode
import datetime
api_key = "myAPIkey"
api_secret = "mySercetKey"
timestamp = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
values = urlencode({"timestamp" : timestamp})
headers = {"Content-Type": "application/x-www-form-urlencoded", "key": api_key, "sig": api_secret}
request = Request("https://www.fybsg.com/api/SGD/test", data=values, headers=headers)
response_body = urlopen(request).read()
print response_body
Here's what was returned from response_body:
{"error":"An unexpected error occurred, its probably your fault, go read the docs."}
Can a kind soul point out what's wrong with my code? (I've got a feeling it's seriously wrong)
The API documentation for the bitcoin exchange can be found here. (Test function)

You were passing invalid timestamp, in the API documentation they have mentioned that the timestamp have to be Current Unix timestamp which can be achieved like this:-
timestamp = datetime.datetime.now()
timestamp = int(time.mktime(timestamp.timetuple()))
OR just:
import time
timestamp= int(time.time())
So after updating your code
from urllib2 import Request, urlopen
from urllib import urlencode
import datetime
import time
api_key = "myAPIkey"
api_secret = "mySercetKey"
timestamp = datetime.datetime.now() #.strftime('%Y-%m-%d %H:%M:%S')
timestamp = int(time.mktime(timestamp.timetuple()))
print timestamp
values = urlencode({"timestamp" : timestamp})
#sig - HMAC-SHA1 signature of POST Data with Key's Secret
from hashlib import sha1
import hmac
hashed = hmac.new(values, api_secret, sha1)
hashed_value = hashed.digest().encode("base64").rstrip('\n')
headers = {"Content-Type": "application/x-www-form-urlencoded",
"key": api_key, "sig":hashed_value}
request = Request("https://www.fybsg.com/api/SGD/test", data=values, headers=headers)
response_body = urlopen(request).read()
print response_body
I'm getting this response:-
{"error":"Invalid API Key or account number"}
Which I think you can fix by using your valid private key or account number.

Related

Why is my http.client request in Python returning [ValueError: No tables found]

I'm trying to use the http.client library to gain access to EPC data through the use of the API found here;
https://epc.opendatacommunities.org/docs/api/domestic
I cannot get the data in the correct format, I have managed to get a 'bytes' variable, which seems to be html, but cannot get it to a readable dataframe.
from http.client import HTTPSConnection
from base64 import b64encode
import pandas as pd
def basic_auth(username, password):
token = b64encode(f"{username}:{password}".encode('utf-8')).decode("ascii")
return f'Basic {token}'
username = "ethan.mercer#aecom.com"
password = "bc6b6b549004ec082a1b8a3a03bd9d1adde3f02b"
c = HTTPSConnection("epc.opendatacommunities.org")
headers = { 'Authorization' : basic_auth(username, password) }
c.request('GET', '/api/v1/domestic/search', headers=headers)
res = c.getresponse()
data = res.read()
data = pd.read_html(data,skiprows=1)[0]
Output:ValueError: No tables found
Updated code:
import json
import requests
import pandas as pd
from base64 import b64encode
def basic_auth(username, password):
token = b64encode(f"{username}:{password}".encode('utf-8')).decode("ascii")
return f'Basic {token}'
username = "ethan.mercer#aecom.com"
password = "bc6b6b549004ec082a1b8a3a03bd9d1adde3f02b"
headers = {'Accept' : 'application/json', 'Authorization' : basic_auth(username, password) }
EPC_Data = requests.get('https://epc.opendatacommunities.org/api/v1/domestic/search?local-authority=E08000003', headers=headers).text
EPC_Data = json.loads(EPC_Data)
This code now works correctly, and is returning a dictionary of headers.
Because your data is in bytes try decoded it like this
data.decode("utf-8")
this should convert your data to an entire string. This is what you can give to pd.read_html (this only will work if there are <table> tags in it)

Why does Kucoin spot order URL seem to be causing KeyError?

I tried to create a simple trading execution code for one-time spot market buy order in Python (using Pycharm).
The code is provided below. API key, API passphrase and API secret key are correct, as well as a signature encoded in base64. The problem seems to be with the order URL. I use this one (provided by Kucoin API documentation): api/v1/orders
The error I'm getting is:
Traceback (most recent call last):
File "C:\Users\xxxx\PycharmProjects\pythonProject11\main 2.py", line 57, in <module>
response = client.request('POST', url, body=body_str, headers=headers)
File "C:\Users\xxxx\PycharmProjects\pythonProject11\venv\lib\site-packages\ccxt\base\exchange.py", line 2801, in request
return self.fetch2(path, api, method, params, headers, body, config, context)
File "C:\Users\xxxx\PycharmProjects\pythonProject11\venv\lib\site-packages\ccxt\base\exchange.py", line 2797, in fetch2
request = self.sign(path, api, method, params, headers, body)
File "C:\Users\xxxx\PycharmProjects\pythonProject11\venv\lib\site-packages\ccxt\kucoin.py", line 3259, in sign
url = self.urls['api'][api]
KeyError: '/api/v1/orders'
Process finished with exit code 1
Code:
import ccxt
import base64
import hashlib
import hmac
import json
from datetime import datetime
# Replace these with your own API key, secret key, and passphrase
api_key = ''
api_secret = ''
api_passphrase = ''
# Set up the Kucoin client
client = ccxt.kucoin()
client.apiKey = api_key
client.secret = api_secret
client.version = 'v1'
# Set the request parameters -- I know there should be a specific token in place of XXXX
symbol = 'XXXX/USDT'
side = 'buy'
type = 'market'
amount = 1.0
# Generate the timestamp in ISO 8601 format
timestamp = datetime.utcnow().isoformat() + 'Z'
# Construct the request body
body = {
'clientOid': 'YOUR_UNIQUE_ID',
'size': amount,
'side': side,
'symbol': symbol,
'type': type
}
# Encode the request body as a JSON string
body_str = json.dumps(body)
# Set the request URL
url = '/api/v1/orders'
# Construct the signature
message = timestamp + 'POST' + url + body_str
signature = base64.b64encode(hmac.new(bytes(api_secret, 'latin-1'), bytes(message, 'latin-1'), digestmod=hashlib.sha256).digest())
# Set the request headers
headers = {
'KC-API-KEY': api_key,
'KC-API-SIGN': signature,
'KC-API-TIMESTAMP': timestamp,
'KC-API-PASSPHRASE': api_passphrase,
'Content-Type': 'application/json'
}
# Send the POST request to the "POST /api/v1/orders" URL
response = client.request('POST', url, body=body_str, headers=headers)
# Print the response
print(response)
I tried various URLs but it doesn't seem to work. What is causing the problem?

How to construct the hashed token signature for Azure Cosmos DB REST API to list users?

According to the documentation for the Cosmos DB REST API, with every API call, the Authorization header must be set. The value for this is constructed as described here: https://learn.microsoft.com/en-us/rest/api/cosmos-db/access-control-on-cosmosdb-resources
I am implementing this in Python as follows:
def get_authorisation_token(verb, resource_type, resource_id, date, master_key):
key = base64.b64decode(master_key)
text = f"""{verb.lower()}\n{resource_type.lower()}\n{resource_id.lower()}\n{date.lower()}\n\n"""
text_encoded = text.encode('utf-8')
signature_hash = hmac.new(key, text_encoded, digestmod=hashlib.sha256).digest()
signature = base64.b64encode(signature_hash).decode()
key_type = 'master'
version = '1.0'
uri = f'type={key_type}&ver={version}&sig={signature}'
uri_encoded = urllib.parse.quote(uri)
return uri_encoded
Since this is sent with every call, the auth token needs to be re-created to match the request URL. So, for example to get a list of databases, one must provide the resource type to be dbs and the resource link/ID to be an empty string with the URL: https://{databaseaccount}.documents.azure.com/dbs/
The part I cannot figure out, is the correct combination of resource type and resource ID/link to get all users from a particular database. Documentation can be found here: https://learn.microsoft.com/en-us/rest/api/cosmos-db/list-users
I have tried some combinations but nothing returns the users, I just get a 401:
{
"code": "Unauthorized",
"message": "The input authorization token can't serve the request. Please check that the expected payload is built as per the protocol, and check the key being used. Server used the following payload to sign: 'get\nusers\ndbs/<db_name>\nmon, 09 nov 2020 23:37:24 gmt\n\n'\r\nActivityId: 697a4159-f160-4aab-ae90-6cb5eaadb710, Microsoft.Azure.Documents.Common/2.11.0"
}
Regarding the issue, please refer to the following code
from wsgiref.handlers import format_date_time
from datetime import datetime
from time import mktime
import base64
from urllib.parse import quote
import hmac
from hashlib import sha256
import requests
from azure.cosmos.auth import GetAuthorizationHeader
from azure.cosmos.cosmos_client import CosmosClientConnection
master_key = ''
database_name = ''
key = base64.b64decode(master_key)
verb = 'GET'
resource_type = 'users'
resource_id = f'dbs/{database_name}'
now = datetime.now()
stamp = mktime(now.timetuple())
date = format_date_time(stamp)
print(date)
text = "{verb}\n{resource_type}\n{resource_id}\n{date}\n{other}\n".format(
verb=(verb.lower() or ''),
resource_type=(resource_type.lower() or ""),
resource_id=(resource_id or ""),
date=date.lower(),
other="".lower())
body = text.encode("utf-8")
digest = hmac.new(key, body, sha256).digest()
signature = base64.encodebytes(digest).decode("utf-8")
key_type = 'master'
version = '1.0'
uri = f'type={key_type}&ver={version}&sig={signature[:-1]}'
uri_encoded = quote(uri)
url = "https://<>.documents.azure.com:443/dbs/<>/users"
payload = {}
headers = {
'Authorization': uri_encoded,
'x-ms-date': date,
'x-ms-version': '2018-12-31'
}
response = requests.request("GET", url, headers=headers, data=payload)
print(response.text)

Python3: Custom Encrypted Headers URLLIB - KrakenAPI

Alright, so I'm a little outside of my league on this one I think.
I'm attempting to facilitate custom HTTP headers what is noted here:
API-Key = API key
API-Sign = Message signature using HMAC-SHA512 of (URI path + SHA256(nonce + POST data)) and base64 decoded secret API key
from https://www.kraken.com/help/api
I'm trying to work solely out of urllib if at all possible.
Below is one of many attempts to get it encoded like required:
import os
import sys
import time
import datetime
import urllib.request
import urllib.parse
import json
import hashlib
import hmac
import base64
APIKey = 'ThisKey'
secret = 'ThisSecret'
data = {}
data['nonce'] = int(time.time()*1000)
data['asset'] = 'ZUSD'
uripath = '/0/private/TradeBalance'
postdata = urllib.parse.urlencode(data)
encoded = (str(data['nonce']) + postdata).encode()
message = uripath.encode() + hashlib.sha256(encoded).digest()
signature = hmac.new(base64.b64decode(secret),
message, hashlib.sha512)
sigdigest = base64.b64encode(signature.digest())
#this is purely for checking how things are coming along.
print(sigdigest.decode())
headers = {
'API-Key': APIKey,
'API-Sign': sigdigest.decode()
}
The above may be working just fine, where I'm struggling now is appropriately getting it to the site.
This is my most recent attempt:
myBalance = urllib.urlopen('https://api.kraken.com/0/private/TradeBalance', urllib.parse.urlencode({'asset': 'ZUSD'}).encode("utf-8"), headers)
Any help is greatly appreciated.
Thanks!
urlopen doesn't support adding headers, so you need to create a Request object and pass it to urlopen:
url = 'https://api.kraken.com/0/private/TradeBalance'
body = urllib.parse.urlencode({'asset': 'ZUSD'}).encode("utf-8")
headers = {
'API-Key': APIKey,
'API-Sign': sigdigest.decode()
}
request = urllib.request.Request(url, data=body, headers=headers)
response = urllib.request.urlopen(request)

How do I sign a POST request using HMAC-SHA512 and the Python requests library?

I'm trying to use Python to access the trading API at poloniex.com, a cryptocurrency exchange. To do this I must follow this prescription:
All calls to the trading API are sent via HTTP POST to https://poloniex.com/tradingApi and must contain the following headers:
Key - Your API key.
Sign - The query's POST data signed by your key's "secret" according to the HMAC-SHA512 method.
Additionally, all queries must include a "nonce" POST parameter. The nonce parameter is an integer which must always be greater than the previous nonce used.
Here is what I have so far. My current issue is that I do not know how to compile the POST url so that it can be signed without sending the incomplete request first. This obviously doesn't work.
import requests
import hmac
import hashlib
import time
headers = { 'nonce': '',
'Key' : 'myKey',
'Sign': '',}
payload = { 'command': 'returnCompleteBalances',
'account': 'all'}
secret = 'mySecret'
headers['nonce'] = int(time.time())
response = requests.post( 'https://poloniex.com/tradingApi', params= payload, headers= headers )
headers['Sign'] = hmac.new( secret, response.url, hashlib.sha512)
Create a prepared request; you can add headers to that after the body has been created:
import requests
import hmac
import hashlib
request = requests.Request(
'POST', 'https://poloniex.com/tradingApi',
data=payload, headers=headers)
prepped = request.prepare()
signature = hmac.new(secret, prepped.body, digestmod=hashlib.sha512)
prepped.headers['Sign'] = signature.hexdigest()
with requests.Session() as session:
response = session.send(prepped)
I changed your params argument to data; for a POST request it is customary to send the parameters in the body, not the URL.
For the nonce, I'd use a itertools.count() object, seeded from the current time so restarts don't affect it. According to the Poloniex API documentation (which you quoted in your question), the nonce is part of the POST body, not the headers, so put it in the payload dictionary:
from itertools import count
import time
# store as a global variable
NONCE_COUNTER = count(int(time.time() * 1000))
# then every time you create a request
payload['nonce'] = next(NONCE_COUNTER)
Using int(time.time()) would re-use the same number if you created more than one request per second. The example code provided by Poloniex uses int(time.time()*1000) to make it possible to create a request every microsecond instead, but using your own monotonically increasing counter (seeded from time.time()) is far more robust.
You can also encapsulate the digest signing process in a custom authentication object; such an object is passed in the prepared request as the last step in preparation:
import hmac
import hashlib
class BodyDigestSignature(object):
def __init__(self, secret, header='Sign', algorithm=hashlib.sha512):
self.secret = secret
self.header = header
self.algorithm = algorithm
def __call__(self, request):
body = request.body
if not isinstance(body, bytes): # Python 3
body = body.encode('latin1') # standard encoding for HTTP
signature = hmac.new(self.secret, body, digestmod=self.algorithm)
request.headers[self.header] = signature.hexdigest()
return request
Use this with your requests calls:
response = requests.post(
'https://poloniex.com/tradingApi',
data=payload, headers=headers, auth=BodyDigestSignature(secret))
The argument passed in is the secret used in the HMAC digest; you can also pass in a different header name.

Categories