Search code examples
pythonpython-3.xpython-requestsurllib

REST API POST request causing AttributeError: 'bytes' object has no attribute 'items'


I'm trying to make a POST request to the Coinigy API in Python 3. This is the code I've been running.

from urllib.request import Request, urlopen
from urllib.parse import urlencode

headers = {
  'Content-Type':'application/json',
  'X-API-KEY':'mykey',
  'X-API-SECRET':'mysecretkey'
}

values = {
"exchange_code": "BINA",
"exchange_market": "BTC/USDT",
"type": "all"
}


values = urlencode(values).encode("utf-8")
headers = urlencode(headers).encode("utf-8")


request = Request('https://api.coinigy.com/api/v1/data', data=values, headers=headers)
response_body = urlopen(request,values).read()
print(response_body)

I'm getting the following error:

 AttributeError                            Traceback (most recent call last)
 <ipython-input-41-504342401726> in <module>()
      19 
      20 
 ---> 21 request = Request('https://api.coinigy.com/api/v1/data', 
 data=values, headers=headers)
      22 response_body = urlopen(request,values).read()
      23 print(response_body)

 ~/anaconda3/lib/python3.6/urllib/request.py in __init__(self, url, 
 data, headers, origin_req_host, unverifiable, method)
     333         self.data = data
     334         self._tunnel_host = None
 --> 335         for key, value in headers.items():
     336             self.add_header(key, value)
     337         if origin_req_host is None:

 AttributeError: 'bytes' object has no attribute 'items'

With a past POST request I wrote to their API for pulling down account activity info, I found I needed to use urlencode(headers).encode("utf-8") to get it in the right format to pass through the API. But now that doesn't seem to work. https://coinigy.docs.apiary.io/#reference/account-data/activity-log/activity

If I comment out the headers encode into utf-8 line, it seems to get through to Coinigy, but then it returns the following error code:

 b'{"err_num":"1057-14-01","err_msg":"Missing or empty parameters:"}'

But the urllib.request says that you must pass headers as a dictionary {} so I can't understand what is wrong, it should be the right data structure.


Solution

  • You should not URL encode your headers. Request expects headers to be a dictionary:

    headers should be a dictionary, and will be treated as if add_header() was called with each key and value as arguments.

    These are HTTP headers and when sent over the wire are in the form (newline-separated, colon space between header name and value):

    User-Agent: Mozilla/5.0 blah blah
    Content-Length: 500
    

    So you should pass your headers dictionary in without doing urlencode on it.

    This github repo might also be helpful to you: https://github.com/coinigy/api/blob/master/coinigy_api_rest.py