Accessing Amazon Product Advertising API


Posted in: libraries, php | Save to del.icio.us | Twit This! 20 Aug 2009

In this post we will see how to access the Amazon Product Advertising API from PHP. Amazon has recently changed (from 15th Aug ‘09) the authentication mechanism for accessing their API which must now be signed with your Amazon keys. Unsigned requests will be rejected by Amazon. Note that the code uses the hash_hmac() hash function which is only available for PHP versions 5.1.2 and above, so the code will not work for versions below that.

A small example

Below is an example to access the Amazon Product Advertising API using the provided class.

<?php
 
    /* Example usage of the Amazon Product Advertising API */
    include("amazon_api_class.php");
 
    $obj = new AmazonProductAPI();
 
    try
    {
        /* Returns a SimpleXML object */
         $result = $obj->searchProducts("X-Men Origins",
                                       AmazonProductAPI::DVD,
                                       "TITLE");
    }
    catch(Exception $e)
    {
        echo $e->getMessage();
    }
 
    print_r($result);
 
?>

The API access class

Given below is the implementation of the class to access the Amazon Product Advertising API. Comments have been removed for brevity, but are included in the source download. Note that only a few access operations are implemented in the class: ItemLookup, ItemSearch; there are more available in the API, a complete list can be found here. It’s just a simple matter of changing some parameters to implement others.

<?php
 
require_once 'aws_signed_request.php';
 
class AmazonProductAPI
{
 
    private $public_key     = "YOUR AMAZON ACCESS KEY ID";
    private $private_key    = "YOUR AMAZON SECRET KEY";
 
    const MUSIC = "Music";
    const DVD   = "DVD";
    const GAMES = "VideoGames";
 
    private function verifyXmlResponse($response)
    {
        if ($response === False)
        {
            throw new Exception("Could not connect to Amazon");
        }
        else
        {
            if (isset($response->Items->Item->ItemAttributes->Title))
            {
                return ($response);
            }
            else
            {
                throw new Exception("Invalid xml response.");
            }
        }
    }
 
    private function queryAmazon($parameters)
    {
        return aws_signed_request("com",
                                  $parameters,
                                  $this->public_key,
                                  $this->private_key);
    }
 
    public function searchProducts($search,$category,$searchType="UPC")
    {
        $allowedTypes = array("UPC", "TITLE", "ARTIST", "KEYWORD");
        $allowedCategories = array("Music", "DVD", "VideoGames");
 
        switch($searchType) 
        {
            case "UPC" :
                $parameters = array("Operation"     => "ItemLookup",
                                    "ItemId"        => $search,
                                    "SearchIndex"   => $category,
                                    "IdType"        => "UPC",
                                    "ResponseGroup" => "Medium");
                            break;
 
            case "TITLE" :
                $parameters = array("Operation"     => "ItemSearch",
                                    "Title"         => $search,
                                    "SearchIndex"   => $category,
                                    "ResponseGroup" => "Medium");
                            break;
 
        }
 
        $xml_response = $this->queryAmazon($parameters);
 
        return $this->verifyXmlResponse($xml_response);
 
    }
 
    public function getItemByUpc($upc_code, $product_type)
    {
        $parameters = array("Operation"     => "ItemLookup",
                            "ItemId"        => $upc_code,
                            "SearchIndex"   => $product_type,
                            "IdType"        => "UPC",
                            "ResponseGroup" => "Medium");
 
        $xml_response = $this->queryAmazon($parameters);
 
        return $this->verifyXmlResponse($xml_response);
 
    }
 
    public function getItemByAsin($asin_code)
    {
        $parameters = array("Operation"     => "ItemLookup",
                            "ItemId"        => $asin_code,
                            "ResponseGroup" => "Medium");
 
        $xml_response = $this->queryAmazon($parameters);
 
        return $this->verifyXmlResponse($xml_response);
    }
 
    public function getItemByKeyword($keyword, $product_type)
    {
        $parameters = array("Operation"   => "ItemSearch",
                            "Keywords"    => $keyword,
                            "SearchIndex" => $product_type);
 
        $xml_response = $this->queryAmazon($parameters);
 
        return $this->verifyXmlResponse($xml_response);
    }
 
}
 
?>

Amazon signed request

The above class uses the ‘aws_signed_request’ function to generate the new request signature. Original code is by Ulrich Mierendorff, modified here to use cURL.

<?php
 
function  aws_signed_request($region,$params,$public_key,$private_key)
{
 
    $method = "GET";
    $host = "ecs.amazonaws.".$region;
    $uri = "/onca/xml";
 
 
    $params["Service"]          = "AWSECommerceService";
    $params["AWSAccessKeyId"]   = $public_key;
    $params["Timestamp"]        = gmdate("Y-m-d\TH:i:s\Z");
    $params["Version"]          = "2009-03-31";
 
    /* The params need to be sorted by the key, as Amazon does this at
      their end and then generates the hash of the same. If the params
      are not in order then the generated hash will be different from
      Amazon thus failing the authentication process.
    */
    ksort($params);
 
    $canonicalized_query = array();
 
    foreach ($params as $param=>$value)
    {
        $param = str_replace("%7E", "~", rawurlencode($param));
        $value = str_replace("%7E", "~", rawurlencode($value));
        $canonicalized_query[] = $param."=".$value;
    }
 
    $canonicalized_query = implode("&", $canonicalized_query);
 
    $string_to_sign = $method."\n".$host."\n".$uri."\n".
                            $canonicalized_query;
 
    /* calculate the signature using HMAC, SHA256 and base64-encoding */
    $signature = base64_encode(hash_hmac("sha256", 
                                  $string_to_sign, $private_key, True));
 
    /* encode the signature for the request */
    $signature = str_replace("%7E", "~", rawurlencode($signature));
 
    /* create request */
    $request = "http://".$host.$uri."?".$canonicalized_query."
                &Signature=".$signature;
 
    /* I prefer using CURL */
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL,$request);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_TIMEOUT, 15);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
 
    $xml_response = curl_exec($ch);
 
    if ($xml_response === False)
    {
        return False;
    }
    else
    {
        /* parse XML and return a SimpleXML object, if you would
           rather like raw xml then just return the $xml_response.
         */
        $parsed_xml = @simplexml_load_string($xml_response);
        return ($parsed_xml === False) ? False : $parsed_xml;
    }
}
?>
Download Source
Downloads : 799 / File size : 5.1 kB



Share this post

Share on Facebook
Share on Twitter
Share on StumbleUpon
Share on Delicious
Share on Digg
Share on Technorati
Share on Reddit
Feeds RSS Subscribe to site Feed

Other related posts



29 Responses

1

Steven Klotz

August 22nd, 2009 at 3:59 pm

Thanks, this was just what I needed to get started.

2

Sammie

August 28th, 2009 at 5:13 pm

Thanks for sharing your source. Very helpfull!

3

phplifestyle

September 4th, 2009 at 7:47 am

Thank for share. Great source.

4

Soediarto

September 20th, 2009 at 11:56 pm

Thanks for sharing..it helps me a lot..

5

Leo

September 21st, 2009 at 1:22 pm

Very helpful code! Thanks!

6

cd smith

September 30th, 2009 at 7:00 pm

How do you change the code to show reviews? I been trying to modify to do that, I am missing something.

sameer

September 30th, 2009 at 9:08 pm

You need to change the response group to ‘Large’ or ‘Medium’ in the following lines:

.
.
“ResponseGroup” => “Medium”);

To find out what elements are returned by the various groups, you can go here:

http://docs.amazonwebservices.com/AWSECommerceService/latest/DG/CHAP_ResponseGroupsList.html

8

cd smith

September 30th, 2009 at 9:58 pm

I tried that earlier unsuccessfully.

Here is what I did:

amazon_api_class.php

case “TITLE” : $parameters = array(”Operation” => “ItemSearch”,
“Title” => $search,
“SearchIndex” => $category,
“ResponseGroup” => “Large”);

break;

Example.php

echo “Sales Rank : {$result->Items->Item->SalesRank}”;

echo “ASIN : {$result->Items->Item->ASIN}”;
echo “Sales Detail : {$result->Items->Item->EditorialReviews->EditorialReview}”;

echo “Items->Item->LargeImage->URL . “\” />”;

Where did go wrong? Thanks.

sameer

October 1st, 2009 at 12:42 am

You need to do the following using the ‘Large’ response group:

echo $result->Items->Item->CustomerReviews->Review[0]->Content;

echo $result->Items->Item->EditorialReviews->EditorialReview[0]->Content;

10

cd smith

October 1st, 2009 at 5:10 am

That worked! But I have one last question:

How did you know the proper way to format the echo string to display the data?

I cannot find that information in the documentation.

For instance, there is a “Source” tag in the XML but if I add that in the string, nothing displays.

Ex: $result->Items->Item->EditorialReviews->EditorialReview[0]->Source->Content;

sameer

October 1st, 2009 at 5:32 am

Just print the $result using

print_r($result)

and view the source, this will display the serialized data from which you can get the node names; or in the ‘aws_signed_request.php’ file, after the line
.
.

$xml_response = curl_exec($ch);

add the following lines:

print_r($xml_response);
exit();

This will display the actual XML returned by Amazon.

12

cd smith

October 1st, 2009 at 8:11 am

Thanks again..The print_r statement browser display was too jumbled to read it clearly but when I viewed the “source” of the webpage, everything became very understandable.

[CustomerReviews] => SimpleXMLElement Object
(
[AverageRating] => 4.5
[TotalReviews] => 31
[TotalReviewPages] => 7
[Review] => Array
(
[0] => SimpleXMLElement Object
(
[ASIN] => B000000ZML
[Rating] => 5
[HelpfulVotes] => 0
[CustomerId] => A12GGA1DTETD2H
[Reviewer] => SimpleXMLElement Object

All I need to do now is figure out how to display the “Add To Cart” button.

Thanks again..

13

Helen Hunt

October 7th, 2009 at 4:45 am

Thanks for the awesome piece of code that has made my affiliate programme much better.

I was struggling with the new signature and timestamp requirements until I saw your solution.

Keep it up and thanks for sharing. Lovely blog BTW :)

14

Manuel88

October 12th, 2009 at 1:58 am

Finally a script that work!! Thnx man!!

15

Jim Keller

October 24th, 2009 at 12:32 pm

I was using a foreach loop to generate the query string rather than implode() as above, and noticed that the request was failing with an invalid signature because I had forgotten to rtrim() the last ampersand from the query string.

Thanks for the code - this was a huge help, and things seem to be working now.

16

Jonathan Montgomery

October 26th, 2009 at 3:32 pm

This returns an error message “The request must contain the parameter Signature” every time. Any idea why?

17

Anup Khandelwal

October 26th, 2009 at 10:53 pm

Thanks for sharing your script. I need small help from you. I am working on magazine price comparison site, wherein on comparison page i need to display magazine of amazon. Can i cache amazon records on my db?

sameer

October 26th, 2009 at 10:59 pm

Anup, you are not quite clear on your question, but you can cache any Amazon results in your db for latter access, but I’m not sure how long the cache should remain valid. If Amazon updates their db then your cache can go stale.

19

Vishy

October 29th, 2009 at 12:29 pm

Hay man thanks a lot, very useful code…. thanks again :-)

20

Anup Khandelwal

November 24th, 2009 at 11:37 pm

Hi Sameer,
I am successfully able to retrieve magazines from the class you developed. Thank you so much.

But i facing one problem which is:

I want to retrieve magazine name ‘TIME’. But i am getting output of all magazine result which match Magazine Title ‘TIME’. I need exact match result for Magazine Title. Can you please suggest me solution for same.

Code Snippet:
$obj = new AmazonProductAPI();
$result = $obj->searchProducts(”TIME”, AmazonProductAPI::MAGAZINE, “TITLE”);

Thanks,
Anup

sameer

November 25th, 2009 at 12:12 am

Try adding an extra parameter, like the ‘Publisher’ name in the query. The publisher for ‘Time’ magazine is ‘Time Direct Ventures’, so you can add that to the ‘Title’ parameter. So the code in the class would look like this:


.
.

case "TITLE" :
$parameters = array("Operation" => "ItemSearch",
"Title" => $search,
"Publisher" => "Time Direct Ventures",
"SearchIndex" => $category,
"ResponseGroup" => "Large");
.
.

Parameter which you can use are given here:

http://docs.amazonwebservices.com/AWSECommerceService/latest/DG/USSearchIndexParamForItemsearch.html
#USSearchIndexParamForItemsearch_magazines

22

Rucha

December 15th, 2009 at 5:40 am

Hello Sameer,

I am trying to get categories of Books to show on my page using amazon api. Code that you provide is good. i m getting perticular book search result but i want to show books sub categories. can u please tell me what changes i have to do in same code?

sameer

December 15th, 2009 at 6:35 am

You need to search with the ‘BrowseNode’ parameter. For example in the ‘amazon_api_class.php’ file you can change the following code:

.
.
case "TITLE" : $parameters = array("Operation" => "ItemSearch",
"Title" => $search,
"SearchIndex" => $category,
"ResponseGroup" => "Large");
.
.

to


.
.
case "TITLE" : $parameters = array("Operation" => "ItemSearch",
"BrowseNode" => 283155,
"SearchIndex" => "Books",
"ResponseGroup" => "BrowseNodes");
.
.

This will return all the subcategories of ‘Books’, with the names and their BrowseNodes.
The BrowseNode ‘283155′ is that of Books.

Note: When using BrowseNodes, replace the following line:

return $this->verifyXmlResponse($xml_response);

with


return $xml_response;

Now you can search for a particular category by using its BrowseNode and some other search criteria, like say keyword.


.
.
case "TITLE" : $parameters = array("Operation" => "ItemSearch",
"BrowseNode" => 75,
"Keywords" => $search,
"SearchIndex" => "Books",
"ResponseGroup" => "Large");
.
.

The BrowseNode given above (75) is for the ‘Science’ category.

You can find a list of BrowseNodes here:
http://www.browsenodes.com

24

Chandra Mouli

December 16th, 2009 at 8:51 pm

Hi,
This is Chandra Mouli. Tanikonda.Please let me know how to connect to the Amazon product api from our applicatin.Thanks in advance..

Regards
Chandra Mouli Tanikonda

25

Rucha

December 19th, 2009 at 3:39 am

Thanks a lot Sameer. Now it is working fine

26

XT@z

January 4th, 2010 at 12:50 pm

Thanks a lot for this script !! Finally something understandable and WORKING !!

Do you also have something for creating and managing their distant basket ?

27

Rucha

January 5th, 2010 at 3:40 am

Hello Sameer,

Can you please tell me how i can integrate remote shopping cart provided by Amazon API? I am trying CartCreate operation but it is not working. Can you please tell me details?

Thanks…….

28

Aaron Mc Adam

March 2nd, 2010 at 9:37 pm

Hey Sameer, thanks very much for your class, I’m trying to figure out a good way of caching the results, as only 10 results are returned for each request. I think I have an idea how to do it but I was wondering if you had already fixed this problem?

sameer

March 2nd, 2010 at 9:56 pm

I’ve not yet implemented a cache yet, I’d be enlightened to know about your idea.

Comment Form

Use the html <code> tag to insert small source code snippets

For longer code examples use http://pastie.org/.

Get latest updates by E-mail

About this blog

This site is a digital habitat of Sameer, a freelance web developer working from Pune.More

Recent Comments

  • sameer: Check to see if the 'IDE > options > format' is set to HTML. [...]
  • sameer: Google strips any newline characters form the text. Although it does accept it with the online trans [...]
  • Arjan: Fiddler is a debugging tool for IE (not Microsoft's Fiddler) [...]
  • Susan Martin: while creating a test for site, command icons on IDE greyed out and do not respond when selected. I [...]
  • Saar: Thanks for this example. helped me a lot. I have 1 problem, I am translating chunks of code, but I [...]
  • sameer: You can add extra GET variables in the options array as below: $pager_options = array( 'mode [...]
  • Martin: How can you carry over your own variables into the URL? I am using a form to POST a couple of var [...]
  • nancy: thanks very much ! first tools [...]

  • Users Online

    • 15 Users Online
    • 13 Guests, 2 Bots