Accessing Amazon’s Product Advertising API with Python 4


I’m working on a little hacky toy for our upcoming hack day at work, and one of the pieces requires that I get some info from the Amazon “REST” API. It’s not REST in the least, but that’s not what I’m here to talk about. I’m here to talk about creating a signed request which works with the “Amazon Signature 2″ format needed for the API. There’s a Perl library to do everything with the API, which is great if that’s what you’re looking for, but it wasn’t. I just wanted to sign a simple static request to get information about books for which I had the ISBN. No Python library exists, but fortunately if you’re using Python 2.5+ you’ve got everything you need with very little code. I’m including it here to save you the effort of pulling together the pieces yourself.

# Create a signed request to use Amazon's API
import base64
import hmac
import urllib, urlparse
import time

from hashlib import sha256 as sha256

AWS_ACCESS_KEY_ID = 'XXX'
AWS_SECRET_ACCESS_KEY = 'YYY'
hmac = hmac.new(AWS_SECRET_ACCESS_KEY, digestmod=sha256)

def getSignedUrl(params):
    action = 'GET'
    server = "webservices.amazon.com"
    path = "/onca/xml"

    params['Version'] = '2009-11-02'
    params['AWSAccessKeyId'] = AWS_ACCESS_KEY_ID
    params['Service'] = 'AWSECommerceService'
    params['Timestamp'] = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())

    # Now sort by keys and make the param string
    key_values = [(urllib.quote(k), urllib.quote(v)) for k,v in params.items()]
    key_values.sort()

    # Combine key value pairs into a string.
    paramstring = '&'.join(['%s=%s' % (k, v) for k, v in key_values])
    urlstring = "http://" + server + path + "?" + \
        ('&'.join(['%s=%s' % (k, v) for k, v in key_values]))

    # Add the method and path (always the same, how RESTy!) and get it ready to sign
    hmac.update(action + "\n" + server + "\n" + path + "\n" + paramstring)

    # Sign it up and make the url string
    urlstring = urlstring + "&Signature="+\
        urllib.quote(base64.encodestring(hmac.digest()).strip())

    return urlstring

if __name__ == "__main__":
    params = {'ResponseGroup':'Small,BrowseNodes,Reviews,EditorialReview,AlternateVersions',
                     'AssociateTag':'xxx-20',
                     'Operation':'ItemLookup',
                     'SearchIndex':'Books', 
                     'IdType':'ISBN',
                     'ItemId':'9780385086950'}
    url = getSignedUrl(params)
    print url
  • Angke Chen

    Thanks a bunch! this really worked for me! Great job

  • http://github.com/yoavaviram/python-amazon-simple-product-api Yoav

    If what you are looking for is a simple, object oriented access to Amazon products (lookup and search), try python-amazon-simple-product-api:

    http://github.com/yoavaviram/python-amazon-simple-product-api

  • Michelle Chen

    Thank you so much for sharing your work. It was very helpful to find an example that doesn’t require boto. What’s strange is that the urls sometimes work and sometimes don’t (and I got the error of SignatureDoesNotMatch). Any idea why? Again, thanks, Michelle

  • synedra

    If the same URL works sometime and doesn’t work sometimes you’ll want to dump out the signatures/headers/etc to see if there’s differences. If, on the other hand, the URL consistently doesn’t work for specific products there’s probably a special character in the signed URL, look for issues with characters there. The getSignedUrl thing may not be gracefully handling specific characters… try to use binary testing to figure out which things are causing the trouble.