<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Jeff&#039;s Notes &#187; Business</title>
	<atom:link href="http://www.jeffreyjason.com/category/business/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.jeffreyjason.com</link>
	<description>Drop me a line with suggested topics: umassthrower@gmail.com.</description>
	<lastBuildDate>Sat, 17 Dec 2011 21:37:58 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.1.4</generator>
		<item>
		<title>Amazon Product Advertising API w/ Ruby</title>
		<link>http://www.jeffreyjason.com/2010/07/12/amazon-product-advertising-api-w-ruby/</link>
		<comments>http://www.jeffreyjason.com/2010/07/12/amazon-product-advertising-api-w-ruby/#comments</comments>
		<pubDate>Mon, 12 Jul 2010 07:03:28 +0000</pubDate>
		<dc:creator>jeff</dc:creator>
				<category><![CDATA[Business]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[Amazon Affiliate]]></category>
		<category><![CDATA[Amazon Ruby API]]></category>
		<category><![CDATA[AWS]]></category>
		<category><![CDATA[Product Advertising API]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Ruby on Rails]]></category>
		<category><![CDATA[SOAP]]></category>
		<category><![CDATA[web services]]></category>

		<guid isPermaLink="false">http://www.jeffreyjason.com/?p=28</guid>
		<description><![CDATA[The Amazon Product Advertising API (formerly AWS or AWS-EC) is a way to programmatically perform many of the actions that a user performs on the Amazon.com website such as searching for an item or adding one to your cart. <a class="more-link" href="http://www.jeffreyjason.com/2010/07/12/amazon-product-advertising-api-w-ruby/">More<span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>The Amazon Product Advertising API (formerly AWS or AWS-EC) is a way to programmatically perform many of the actions a user can perform on the Amazon.com website.  This includes searching for an item, viewing an items details, or adding an item to your cart.  In order to use this API it is required that you register as a registered affiliate in the Amazon Affiliate Marketing program.  Any use that is outside the scope of the AM program is expresely forbidden by the <a href="https://affiliate-program.amazon.com/gp/advertising/api/detail/agreement.html" target="_blank">TOS</a>.  The data obtained through this API is generally used to, for example, provide a set of dynamically generated custom links for advertising amazon products that are relevant to some user generated content in your application or website.</p>
<div style='float:left; padding-right:10px;'>
<iframe src="http://rcm.amazon.com/e/cm?t=jefsnot-20&#038;o=1&#038;p=12&#038;l=ur1&#038;category=kindle&#038;banner=1RR50DN6TK7D02JARP02&#038;f=ifr" width="300" height="250" scrolling="no" border="0" marginwidth="0" style="border:none;" frameborder="0"></iframe>
</div>
<p>This API is not to be confused with the other major Amazon Web Services APIs (which are now also called &#8220;AWS&#8221;) such as AWS-S3 (Simple Storage Service) or AWS-EC2 (Elastic Computing Cloud).  Amazon had formerly named this particular API &#8220;AWS-EC&#8221; for <em>Affiliate Web Services ECommerce </em>API, but that name was deprecated when they decided to call the aforementioned pay-per-unit cloud based APIs &#8220;Amazon Web Services.&#8221;  And to top it off an Amazon Affiliate that wants to use the API formerly known as AWS must signup for an &#8220;AWS&#8221; account since the Product Advertising API is of course an Amazon Web Service.  (Did I mention the reference docs still say AWS?)</p>
<p>OK, now that we have that out of the way you can feel a little less confused when you see &#8220;AWS&#8221; on discussion board posts and people are talking about cost per unit of CPU time.</p>
<h3>Prerequisites</h3>
<div style='float:right;border-top: 40px;'>
<iframe src="http://rcm.amazon.com/e/cm?lt1=_blank&#038;bc1=FFFFFF&#038;IS2=1&#038;bg1=FFFFFF&#038;fc1=000000&#038;lc1=0000FF&#038;t=jefsnot-20&#038;o=1&#038;p=8&#038;l=as1&#038;m=amazon&#038;f=ifr&#038;md=10FE9736YVPPT7A0FBG2&#038;asins=1934356166" style="width:120px;height:240px;" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"></iframe>
</div>
<ol>
<li>You have obtained an Amazon Affiliate Program account (<a href="https://affiliate-program.amazon.com/gp/flex/associates/apply-login.html" target="_blank">here</a>)
<ul>
<li>be sure to actually read the <a href="https://affiliate-program.amazon.com/gp/advertising/api/detail/agreement.html" target="_blank">TOS</a> for this service because Amazon will terminate if you&#8217;re caught in violation</li>
</ul>
</li>
<li>You have installed Ruby, Rails, &amp; RubyGems (<a href="http://wiki.rubyonrails.org/getting-started/installation" target="_blank">here</a>)</li>
<li>You have a basic understanding of programing and Ruby.</li>
</ol>
<h3>Technologies</h3>
<p>The focus of this tutorial is both pragmatic and practical.  If you wish to understand SOAP here is the <a href="http://www.amazon.com/gp/product/0596000952?ie=UTF8&amp;tag=jefsnot-20&amp;linkCode=as2&amp;camp=1789&amp;creative=9325&amp;creativeASIN=0596000952">O&#8217;REILEY</a> text on the subject.  The short answer is that SOAP is a, for better or for worse, widely adopted specification for data exchange.  Most large companies that provide any sort of API to the outside world provide a SOAP based XML API. For this tutorial we will be using:</p>
<ul>
<li>Ruby (v 1.8.7)</li>
<li>Rails (v 2.3.8)</li>
<li>RubyGems (v 1.3.7)</li>
<li>Ruby/AWS (v 0.8.1)</li>
</ul>
<p>Additional, less helpful, guides are available from amazon for Perl, Java, PHP, C# <a href="http://docs.amazonwebservices.com/AWSECommerceService/2009-11-01/GSG/" target="_blank">here</a>.  Perhaps I will writeup some supplemental plug-in guides for the other versions if there is interest.</p>
<h3>Obtaining an AWS Developer Key</h3>
<div style='float:left;padding: 10px 10px 0px 0px'>
<a href="http://www.amazon.com/gp/product/1600377440?ie=UTF8&#038;tag=jefsnot-20&#038;linkCode=as2&#038;camp=1789&#038;creative=390957&#038;creativeASIN=1600377440"><img border="0" src="/wp-content/uploads/2010/07/internet_marketing.jpg"></a>
</div>
<p>In addition to being an affiliate program member Amazon also requires users of their Affiliate Product Advertising API to sign up for an AWS account in order to obtain an access key with which to sign their requests.  The <a href="https://aws-portal.amazon.com/gp/aws/developer/registration/index.html" target="_blank">registration page</a> is pretty straight forward.</p>
<ol>
<li>Login with your Amazon.com credentials</li>
<li>Verify your location</li>
<li>As Amazon puts it &#8220;Success&#8221;</li>
</ol>
<p>Once logged in at the Amazon Web Services home page go to Account &gt;&gt; <a href="https://aws-portal.amazon.com/gp/aws/developer/account/index.html?ie=UTF8&amp;action=access-key" target="_blank">Security Credentials</a>.  Your newly generated API key will be waiting for you under the Access Credentials Section.</p>
<div id="attachment_50" class="wp-caption alignnone" style="width: 448px"><a href="http://www.jeffreyjason.com/wp-content/uploads/2010/07/aws-access-credentials-section.png"><img class="size-full wp-image-50 " title="aws-access-credentials-section" src="http://www.jeffreyjason.com/wp-content/uploads/2010/07/aws-access-credentials-section.png" alt="Be sure to keep your private key private!" width="438" height="243" /></a><p class="wp-caption-text">https://aws-portal.amazon.com/gp/aws/developer/account/index.html?ie=UTF8&amp;action=access-key</p></div>
<h3>Ruby/AWS</h3>
<p>Ruby/AWS is the project name of the GPL RubyGem, written and maintained by <a href="http://www.caliban.org" target="_blank">Ian Macdonald</a>, that handles the lower level communication with tha Amazon SOAP API.  You can think of Ruby/AWS as a tangible implementation of the interface defined by the WSDL.  This means the creator took the datatypes and operations specified in the WSDL and implemented them as classes and methods in ruby code.  As of the time of this writing the Ruby/AWS Gem on RubyForge is version 0.7.0.  Due to the significant re-factoring and the statement that v 0.8.1 &#8220;more or less fully  supports&#8221; the Amazon AWS v 4.0 API, I choose to install the most up-to date version available at the bottom of <a href="http://www.caliban.org/ruby/ruby-aws/" target="_blank">this rdoc page</a>.</p>
<pre>wget http://caliban.org/files/ruby/ruby-aaws-0.8.1.gem
sudo gem install ruby-aaws-0.8.1.gem</pre>
<p>That&#8217;s it for the installation, pretty easy ehh. Well there is one more thing you need to do and that&#8217;s setup a file named .amazonrc in your home dir.  This file holds configuration information and should look something like this:</p>
<pre>key_id = 'someshorterpublickey'
secret_key_id = 'somelongprivatekeythatseemsthislong'
associate = 'some-associate-code'
cache = false
locale = 'us'
encoding = 'iso-8859-15'</pre>
<p>Your associate Tracking ID can be found in the upper left hand of your logged in associate <a href="https://affiliate-program.amazon.com/gp/associates/network/main.html" target="_blank">main page</a>:</p>
<p><a href="http://www.jeffreyjason.com/wp-content/uploads/2010/07/associated-identifier.png"><img class="alignnone size-full wp-image-60" title="associated-identifier" src="http://www.jeffreyjason.com/wp-content/uploads/2010/07/associated-identifier.png" alt="https://affiliate-program.amazon.com/gp/associates/network/main.html" width="446" height="246" /></a></p>
<p>Now that .amazonrc is setup we can write a little script to test things out.</p>
<h2>Hello World</h2>
<p>(or the demonstrable AWS equivalent)</p>
<pre class="brush: ruby; title: ; notranslate">
#!/usr/bin/ruby -w

require 'amazon/aws/search'

include Amazon::AWS
include Amazon::AWS::Search

is = ItemSearch.new( 'Baby',
  {
    'Keywords' =&gt; 'pants',
    'MinimumPrice' =&gt; '2500',
    'MaximumPrice' =&gt; '4999'
  } )
is.response_group = ResponseGroup.new( 'Small' )

req = Request.new
req.locale = 'us'

resp = req.search( is )
items = resp.item_search_response[0].items[0].item

items.each { |item| puts item, '' }
</pre>
<h4>ItemSearch</h4>
<p>Starting on line 8 we create a new ItemSearch object and give it our search parameters.  ItemSearch&#8217;s constructor takes searchIndex which is roughly equivalent to the department dropdown on Amazon.com, with a few extras like &#8220;Music&#8221; and &#8220;Video&#8221; or a particular &#8220;Merchant Id.&#8221;  The parameters are the conditions to search on which are once again roughly equivalent to the text input field and the additional filters of Amazon&#8217;s search results.  The complete list of search indices and parameters is available in the &#8220;AWS E-Commerce Service&#8221; API reference (yes, another name for it) entry for <a href="http://docs.amazonwebservices.com/AWSEcommerceService/4-0/ApiReference/ItemSearchOperation.html" target="_blank">ItemSearch</a>.  One thing to mention about ItemSearch is that you should be sure to check the <a href="http://docs.amazonwebservices.com/AWSEcommerceService/4-0/ApiReference/SearchIndexValues.html" target="_blank">search index compatibility matrix</a> because not all indices are available on all locales.  The US locale currently supports all but 3 of them, for the other locales support is sparse.</p>
<h4>ResponseGroup</h4>
<p>A ResponseGroup is amazon&#8217;s way of you telling them how much and what data you want.  Valid ResponseGroups include:</p>
<ul>
<li>Small, Medium, and Large</li>
<li>Reviews, EditorialReview</li>
<li>Offers</li>
<li>SalesRank</li>
<li>Accessories</li>
<li>etc&#8230;</li>
</ul>
<p>You cannot combine ResponseGroups so requests for Mutually exclusive pieces of data will require multiple requests.  There is some limited batching functionality, but you may still come across cases where you need to make more than one request for a page.</p>
<div style='float:left;padding-right:5px'>
<iframe src="http://rcm.amazon.com/e/cm?lt1=_blank&#038;bc1=FFFFFF&#038;IS2=1&#038;bg1=FFFFFF&#038;fc1=000000&#038;lc1=0000FF&#038;t=jefsnot-20&#038;o=1&#038;p=8&#038;l=as1&#038;m=amazon&#038;f=ifr&#038;md=10FE9736YVPPT7A0FBG2&#038;asins=B00395DVQS" style="width:120px;height:240px;" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"></iframe>
</div>
<div style='float:left;padding-right:5px'>
<iframe src="http://rcm.amazon.com/e/cm?lt1=_blank&#038;bc1=FFFFFF&#038;IS2=1&#038;bg1=FFFFFF&#038;fc1=000000&#038;lc1=0000FF&#038;t=jefsnot-20&#038;o=1&#038;p=8&#038;l=as1&#038;m=amazon&#038;f=ifr&#038;md=10FE9736YVPPT7A0FBG2&#038;asins=B00395564I" style="width:120px;height:240px;" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"></iframe>
</div>
<div style='float:left;padding-right:5px'>
<iframe src="http://rcm.amazon.com/e/cm?lt1=_blank&#038;bc1=FFFFFF&#038;IS2=1&#038;bg1=FFFFFF&#038;fc1=000000&#038;lc1=0000FF&#038;t=jefsnot-20&#038;o=1&#038;p=8&#038;l=as1&#038;m=amazon&#038;f=ifr&#038;md=10FE9736YVPPT7A0FBG2&#038;asins=B003TBJVUW" style="width:120px;height:240px;" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"></iframe>
</div>
<div style='float:left;padding-right:5px'>
<iframe src="http://rcm.amazon.com/e/cm?lt1=_blank&#038;bc1=FFFFFF&#038;IS2=1&#038;bg1=FFFFFF&#038;fc1=000000&#038;lc1=0000FF&#038;t=jefsnot-20&#038;o=1&#038;p=8&#038;l=as1&#038;m=amazon&#038;f=ifr&#038;md=10FE9736YVPPT7A0FBG2&#038;asins=B0039556RA" style="width:120px;height:240px;" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"></iframe>
</div>
<div style='float:left;padding-right:5px'>
<iframe src="http://rcm.amazon.com/e/cm?lt1=_blank&#038;bc1=FFFFFF&#038;IS2=1&#038;bg1=FFFFFF&#038;fc1=000000&#038;lc1=0000FF&#038;t=jefsnot-20&#038;o=1&#038;p=8&#038;l=as1&#038;m=amazon&#038;f=ifr&#038;md=10FE9736YVPPT7A0FBG2&#038;asins=B00395DWV2" style="width:120px;height:240px;" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"></iframe>
</div>
<h4>Request</h4>
<p>The request object is what is going to actually make the request to the web service like you would if you put a URL in your browsers address bar.  It then receives an XML response from Amazon and parses that into the appropriate set of objects for the given Operation.</p>
<h4>Terse Version</h4>
<p>This package has also made sure we have some common case optimizations built in.  In this case the first script was setup with us providing the Amazon specified defaults as our parameters.  Since we can leave the defaults out that script returns equivalent results to this much shorter version. </p>
<pre class="brush: ruby; title: ; notranslate">
#!/usr/bin/ruby -w

require 'amazon/aws/search'

include Amazon::AWS
include Amazon::AWS::Search

resp = Amazon::AWS.item_search( 'Baby',
  {
    'Keywords' =&gt; 'pants',
    'MinimumPrice' =&gt; '2500',
    'MaximumPrice' =&gt; '4999'
  } )

items = resp.item_search_response.items.item

items.each { |item| puts item, '' }
</pre>
<h4>Output</h4>
<p>When you run the script you should see something along the lines of:</p>
<pre>...
similar_products = similar_product = title = Pampers Easy Ups Value Pack for Girls, Size 5, 80-Count
 Box
asin = B0027VT9J8title = Pampers Easy Ups Value Pack for Boys, Size 6, 66-Count Box   asin = B0027VT9IEtitle = Pampers Cruisers Dry Max Diapers, Economy Plus, Size 6 (35+ Lbs), 100 Diapers
asin = B00347AFGU
title = Huggies Pull-Ups Learning Designs Training Pants, Girls, 4T-5T, 44-Count Biggie Pack
asin = B003D7LA8I
title = Pull-Ups Training Pants with Learning Designs, 3T-4T (32-40 lbs), Mega, 40 training pants
asin = B000096OKL

offer_summary = total_used = 0
lowest_new_price = currency_code = USD
formatted_price = $25.00amount = 2500
total_collectible = 0
total_new = 5total_refurbished = 0

image_sets = image_set = thumbnail_image = width = 75height = 75
url = http://ecx.images-amazon.com/images/I/51%2BUWKsDyGL._SL75_.jpg
small_image = width = 75
height = 75
url = http://ecx.images-amazon.com/images/I/51%2BUWKsDyGL._SL75_.jpg 

large_image = width = 500
height = 500
url = http://ecx.images-amazon.com/images/I/51%2BUWKsDyGL.jpg

tiny_image = width = 110
height = 110
url = http://ecx.images-amazon.com/images/I/51%2BUWKsDyGL._SL110_.jpg

swatch_image = width = 30
height = 30
url = http://ecx.images-amazon.com/images/I/51%2BUWKsDyGL._SL30_.jpg
...</pre>
<p>That&#8217;s it.  This is the string representation of the objects that were generated from Ruby/AWS parsing the XML returned from Amazon.  The fruits of your labor have been realized and you can process amazon data (that is relevant for your Affiliate Marketing purposes) till your hearts content.  As long contentment comes before you average 1 request per second.  Did I mention to read the TOS?</p>
<p><iframe src="http://rcm.amazon.com/e/cm?t=jefsnot-20&#038;o=1&#038;p=48&#038;l=ur1&#038;category=gourmet&#038;banner=0DSWRZ5A2FXV23WJNK02&#038;f=ifr" width="728" height="90" scrolling="no" border="0" marginwidth="0" style="border:none;" frameborder="0"></iframe></p>
<h2>XML &#8211;&gt; Objects</h2>
<p>Because XML makes me hate life you get a direct quote from a subsection of the README:</p>
<pre>In Ruby/AWS, each unique XML element name forms a class of the same name. All
such classes are subclasses of AWSObject. For example, OperationRequest is a
class, as is ItemAttributes.

As the XML tree is traversed, each element is converted to an instance of the
class of the same name. Every such object has instance variables, one per
unique child element name. The name of the instance variable is translated to
comply with Ruby convention by adding an underscore ('_') character at word
boundaries and converting the name to lower case.

For example, given the following XML:</pre>
<pre class="brush: xml; title: ; notranslate">
&lt;ItemAttributes&gt;
   &lt;Author&gt;Philip Pullman&lt;/Author&gt;
   &lt;Manufacturer&gt;Scholastic&lt;/Manufacturer&gt;
   &lt;ProductGroup&gt;Book&lt;/ProductGroup&gt;
   &lt;Title&gt;The Ruby in the Smoke (Sally Lockhart Quartet)&lt;/Title&gt;
&lt;/ItemAttributes&gt;
</pre>
<pre>the following statements would all be true:

- ItemAttributes, Author, Manufacturer, ProductGroup and Title would all be
  dynamically defined subclasses of AWSObject.

- An instance of the ItemAttributes class would be created, with instance
  variables @author, @manufacturer, @product_group and @title.

- To each of these instance variables would respectively be assigned an array
  of Author objects, an array of Manufacturer objects, an array of
  ProductGroup objects and an array of Title objects. In the above case, these
  would all be single element arrays, because there's only one instance of
  each kind of tag in the XML.

- The Author, Manufacturer, ProductGroup and Title objects would have no
  instance variables of their own, because the corresponding XML elements
  have no children, just a value. These objects are therefore directly
  assigned the value in question.</pre>
<p>Well that was easy, I should teach from the book more often&#8230; Thanks Ian, moving on.</p>
<h2>Common Errors</h2>
<p>&#8220;`require&#8217;: no such file to load &#8212; amazon/aws/search (LoadError)&#8221;<br />
<strong>solution:</strong> don&#8217;t forget to set RUBYOPT  in your .bash_profile via: <span style="font-family: Consolas, Monaco, 'Courier New', Courier, monospace; line-height: 18px; font-size: 12px; white-space: pre;">export RUBYOPT=rubygems</span></p>
<p><span style="font-family: Consolas, Monaco, 'Courier New', Courier, monospace; line-height: 18px; font-size: 12px; white-space: pre;"> </span><br />
&#8220;Your request should have atleast 1 of the following parameters: Signature, Timestamp.&#8221;<br />
<strong>solution:</strong> This error is the general purpose error Amazon gives you when your request does not appear to have been signed correctly.  Since the Ruby API we&#8217;re using does that for us the likely cause of this error is that you didn&#8217;t set your API secret key correctly in the .amazonrc file.</p>
<p>The README also points out that Amazon considers requests timestamped as being more than 15 minutes old to be invalid, so make sure your server&#8217;s closk is being sync&#8217;d appropriately.</p>
<h2>Thanks</h2>
<p>As always Comments are welcome and if you find any gaps in this tutorial, gaps in the Ruby/AWS API, or see that a new version is available, throw me a comment so I can update the tutorial and alert the maintainer.  For more information you can consult the 4000 word README doc in the rubygems directory for this gem.</p>
<h2>API References</h2>
<ul>
<li>ruby-aaws: (<a href="http://www.caliban.org/ruby/ruby-aws/" target="_blank">rdoc</a>) (or of course &#8216;gem server&#8217;)</li>
<li>Amazon documentation:
<ul>
<li>Getting Started: (<a href="http://docs.amazonwebservices.com/AWSECommerceService/2009-11-01/GSG/" target="_blank">html</a>) (<a href="http://s3.amazonaws.com/awsdocs/Associates/2009-11-01/prod-adv-api-gsg-20091101.pdf" target="_blank">pdf</a>)</li>
<li>Developer Guide: (<a href="http://docs.amazonwebservices.com/AWSECommerceService/2009-11-01/DG" target="_blank">html</a>) (<a href="http://s3.amazonaws.com/awsdocs/Associates/2009-11-01/prod-adv-api-dg-20091101.pdf" target="_blank">pdf</a>)</li>
<li>Reference Card: (<a href="http://s3.amazonaws.com/awsdocs/Associates/2009-11-01/prod-adv-api-qrc-20091101.pdf" target="_blank">pdf</a>)</li>
</ul>
</li>
</ul>
<p><iframe src="http://rcm.amazon.com/e/cm?t=jefsnot-20&#038;o=1&#038;p=26&#038;l=ur1&#038;category=videogames&#038;banner=1620DM2X8DSD2PK5Q6R2&#038;f=ifr" width="468" height="60" scrolling="no" border="0" marginwidth="0" style="border:none;" frameborder="0"></iframe></p>
]]></content:encoded>
			<wfw:commentRss>http://www.jeffreyjason.com/2010/07/12/amazon-product-advertising-api-w-ruby/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>

