Corkscrew
Corkscrew

Reputation: 131

Kraken API 'EAPI:Invalid key' with Python3

I tried making a simple function which makes a HTTP request to Kraken exchange API. The method is private, I am trying to fetch my account balance.

According to Kraken documentation (https://www.kraken.com/features/api#general-usage):

HTTP header:

API-Key = API key

API-Sign = Message signature using HMAC-SHA512 of (URI path + SHA256(nonce + POST data)) and base64 decoded secret API key

POST data:

nonce = always increasing unsigned 64 bit integer

otp = two-factor password (if two-factor enabled, otherwise not required)

I tried to make my signature generation similar to the "veox" Python library (available at: https://github.com/veox/python3-krakenex/blob/master/krakenex/api.py).

I am using Python 3.6.7 on Ubuntu 18.04.

2FA (otp) is turned on for my account on Kraken exchange, though I am not sure if I need to include in the request.

I searched stack overflow for the solution, but I can't seem to get anything from the posts available. (Please keep in mind I'm fairly new to Python and Stack Overflow)

I get 200 response from the server so I am pretty sure the problem is in generating the signature.

Here is my code (xxx, yyy and zzz variables are purposefully written like that):

Kraken_secret_key = 'xxx' 

Kraken_headers ={

    'Kraken_API_key': 'yyy' 

}

def Kraken_account_balance(Kraken_headers):

    URI_path= '/0/private/Balance'

    URL_path = 'https://api.kraken.com/0/private/Balance'

    Kraken_nonce = str(int(time.time()*1000))

    otp = 'zzz'

    Kraken_POST_data = {

        'nonce': Kraken_nonce,
        'otp': str(otp)

    } 

    encoded = (str(Kraken_nonce)+str(otp)).encode()  

    message = URI_path.encode() + hashlib.sha256(encoded).digest() 

    Kraken_signature = hmac.new(base64.b64decode(Kraken_secret_key), message, digestmod=hashlib.sha512)

    Kraken_signature_digest = base64.b64encode(Kraken_signature.digest())

    Kraken_headers['Kraken_API_Signature'] = Kraken_signature_digest.decode()

    response = requests.post(URL_path,data= Kraken_POST_data, headers = Kraken_headers)

    result = response.json()

    print(result)

Upvotes: 3

Views: 5217

Answers (1)

Corkscrew
Corkscrew

Reputation: 131

So I figured out why my code was not working.

Short answer:

  1. POST data used in the Kraken API call has to be URL encoded. That means that "nonce" and "otp" have to be URL encoded for the API to work properly. I used "urllib.parse.urlencode" method from the "urllib" module to get API to work properly.
  2. Header values have to explicitly be called the same names as in the Kraken API manual.

Long answer:

  1. This may be because I am a beginner in coding API's, but Kraken API manual does not explicitly state that POST data has to be URL encoded. OTP (2 factor authentication) did not affect my code in this case so I got rid of that part of the POST data in the call.

In my case, the only POST data I used was the "nonce" value. So for example, if in the above code nonce was equal to

'nonce': 666999

the same value used in the call, but URL encoded with 'urllib.parse.urlencode' method would be equal to

"nonce=666999"
  1. Again, probably not a problem for more experienced developers, but it was not obvious to me that header values have to explicitly be called the same names as in the Kraken API manual.

So in the above code

Kraken_headers ={

'Kraken_API_key': 'yyy' 

}

and

Kraken_headers['Kraken_API_Signature'] = Kraken_signature_digest.decode() 

should be renamed to

Kraken_headers ={

'API-Key': 'yyy' 

}

and

 Kraken_headers['API-Sign'] = Kraken_signature_digest.decode()

Here is the full working code, just replace the private and public key values with your values:

import requests
import time 
import hmac
import hashlib
import json
import base64
import urllib

Kraken_secret_key = 'xxx' 

Kraken_headers ={

'API-Key': 'yyy' 

}

def Kraken_account_balance(Kraken_headers):

    URI_path= '/0/private/Balance'

    URL_path = 'https://api.kraken.com/0/private/Balance'

    Kraken_nonce = str(int(time.time()*1000))

    Kraken_POST_data = {

        'nonce': Kraken_nonce
    } 

    url_encoded_post_data = urllib.parse.urlencode(Kraken_POST_data) 

    encoded = (str(Kraken_POST_data['nonce'])+url_encoded_post_data).encode()  

    message = URI_path.encode() + hashlib.sha256(encoded).digest() 

    Kraken_signature = hmac.new(base64.b64decode(Kraken_secret_key), message,  
    hashlib.sha512)

    Kraken_signature_digest = base64.b64encode(Kraken_signature.digest())

    Kraken_headers['API-Sign'] = Kraken_signature_digest.decode()

    response = requests.post(URL_path,data= Kraken_POST_data, headers = 
    Kraken_headers)

    result = response.json()

    print(result)

Kraken_account_balance(Kraken_headers)

Upvotes: 9

Related Questions