Signing Amazon Product Advertising API – C#/WCF – Part 2

Just the sample code

Download the sample project from:, 324KB, 8/1/2009

The sample is self-contained, and does not require you to read this post to use. If trying to replicate the sample’s behavior in a different program, take a look at step 2 below regarding adding the Amazon ECS service reference.

Part 2

In part 1, we built a simple C# console application that used WCF to send an authenticated ItemSearch request to the Amazon Product Advertising API (formerly the Amazon Associates Web Service — AWS). If you are just starting out with authenticated requests to that service, you might want to read that post first.

While the sample worked well for some of you, others reported difficulties with more complicated usage patterns. I will try to tackle these issues in this post. I am also updating the sample solution to include additional sample programs.

Asynchronous requests

The sample program in part 1 showed how to send a synchronous request to the product advertising web service. The program prepared a request object, and then called an ItemSearch method. This method sent the request, and then waited for the reply to come back. In many situations, we do not want the program to wait. Instead, we want to accomplish other tasks until the response arrives.

If we could send an ItemSearch request asynchronously instead, the method would return immediately, allowing the program to continue doing other things. We would then expect a callback method to be invoked when the request completes, allowing us to make use of the results. Luckily, WCF supports asynchronous methods. Here’s what you could do:

Step 1 – Create a sample console application

  1. In Visual Studio 2008, select New/Project… from the File menu.
  2. Select Console Application from Visual C#/Windows, and enter the application’s name.
  3. Click OK

Visual Studio creates a new console application. So far, this isn’t different than any other console app.

Step 2 – Add a web service reference

  1. In the Solution Explorer view, right click References, then select Add Service Reference…
  2. The Add Service Reference dialog appears. Type in the Amazon Product Advertising API WSDL URL in the Address box:
  3. Click Go. Visual Studio takes a moment to download the service description, and then populates the Services box. You should see AWSECommerceService in there.
  4. Type in the namespace for the code that Visual Studio will generate. I used Amazon.ECS.
  5. Click Advanced… Check the Generate asynchronous operations box. This will create the asynchronous versions of the methods.
  6. Click OK.

Visual Studio now creates a service reference and adds it to your project. You can see it under the Service References folder in Solution Explorer. Your application will now also have an app.config file added, listing WCF binding and endpoint information for the added service.

Step 3 – Add the helper classes that aid in signing the requests.

You can just copy and paste the Amazon.ECS.Addons folder from the sample project, with its three classes: AmazonHeader, AmazonSigningEndpointBehavior and AmazonSigningMessageInspector.

Step 4 – Add code to access the product advertising API asynchronously.

This code is modeled after the sample in part 1:

using System;
using System.ServiceModel;
using Async.Amazon.ECS;

namespace Async {
    class Program {
        // your Amazon ID's
        private const string accessKeyId = "XXXXXXXXXXXXXXXXXXXX";
        private const string secretKey   = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";

        // the program starts here
        static void Main(string[] args) {

            // create a WCF Amazon ECS client
            BasicHttpBinding binding       = new BasicHttpBinding(BasicHttpSecurityMode.Transport);
            binding.MaxReceivedMessageSize = int.MaxValue;
            AWSECommerceServicePortTypeClient client = new AWSECommerceServicePortTypeClient(
                new EndpointAddress(""));

            // add authentication to the ECS client
            client.ChannelFactory.Endpoint.Behaviors.Add(new AmazonSigningEndpointBehavior(accessKeyId, secretKey));

            // prepare an ItemSearch request
            ItemSearchRequest request = new ItemSearchRequest();
            request.SearchIndex       = "Books";
            request.Title             = "WCF";
            request.ResponseGroup     = new string[] { "Small" };

            ItemSearch itemSearch     = new ItemSearch();
            itemSearch.Request        = new ItemSearchRequest[] { request };
            itemSearch.AWSAccessKeyId = accessKeyId;

            // prepare to handle the results, once the async request is completed
            client.ItemSearchCompleted += (source, e) => {
                // write out the results
                foreach (var item in e.Result.Items[0].Item) {

            // issue the ItemSearch request
            Console.WriteLine("Waiting for results...");

            // wait for results

Like in part 1, this program creates a client object, associates it with an endpoint behavior that ensures requests will be authenticated, and creates an ItemSearch request object. This is where things change, however. While in part 1 our program just used the client to send the request and wait for the response. Our version is different.

First, we attach an event handler to the client’s ItemSearchCompleted event. The event handler will get called when the response to our request comes back. Our event handler here just prints out the item titles to the console. If you find the (source, e) => { … } syntax a bit strange — this is just a C# shorthand notation for defining a named event handler, perhaps something like void RequestCompleted(object sender, ItemSearchCompletedEventArgs e) { … }.

Only then does the program invoke the ItemSearch operation, by calling ItemSearchAsync. ItemSearchAsync sends the request to the web service, and returns immediately. At this point our program continues processing, by printing out that it’s waiting for results, and then waiting for a key press. This gives the asynchronous operation time to complete and invoke our event handler. A real application could use the opportunity to carry out real work here.

Note that the request is signed exactly the same way it was in part 1. There is no difference in how synchronous and asynchronous requests are authenticated. In fact, the SOAP messages look identical. The distinction is only in how these operations are used in your application.

Batch requests

The product advertising API allows you to combine two requests of the same type in one SOAP message. This practice is named batch requests. Batch requests are authenticated exactly the same way that single requests are. To try this out, follow step 1-3 above, then try out this main program:

using System;
using System.ServiceModel;
using Batch.Amazon.ECS;

namespace Batch {
    class Program {
        // your Amazon ID's
        private const string accessKeyId = "XXXXXXXXXXXXXXXXXXXX";
        private const string secretKey   = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";

        // the program starts here
        static void Main(string[] args) {

            // create a WCF Amazon ECS client
            BasicHttpBinding binding       = new BasicHttpBinding(BasicHttpSecurityMode.Transport);
            binding.MaxReceivedMessageSize = int.MaxValue;
            AWSECommerceServicePortTypeClient client = new AWSECommerceServicePortTypeClient(
                new EndpointAddress(""));

            // add authentication to the ECS client
            client.ChannelFactory.Endpoint.Behaviors.Add(new AmazonSigningEndpointBehavior(accessKeyId, secretKey));

            // prepare the first ItemSearchRequest
            ItemSearchRequest request1 = new ItemSearchRequest();
            request1.SearchIndex       = "Books";
            request1.Keywords          = "WCF";
            request1.ItemPage          = "1";
            request1.ResponseGroup     = new string[] { "Small" };

            // prepare a second ItemSearchRequest
            ItemSearchRequest request2  = new ItemSearchRequest();
            request2.SearchIndex        = "Books";
            request2.Keywords           = "WCF";
            request2.ItemPage           = "2";
            request2.ResponseGroup      = new string[] { "Small" };

            // batch the two requests together
            ItemSearch itemSearch       = new ItemSearch();
            itemSearch.Request          = new ItemSearchRequest[] { request1, request2 };
            itemSearch.AWSAccessKeyId   = accessKeyId;

            // issue the ItemSearch request
            ItemSearchResponse response = client.ItemSearch(itemSearch);

            // write out the results
            foreach (var item in response.Items[0].Item) {
            foreach (var item in response.Items[1].Item) {

This variation of the sample program creates two ItemSearchRequest objects instead of just one, and adds them as an array to an ItemSearch object before invoking the web service request, with:

itemSearch.Request = new ItemSearchRequest[] { request1, request2 };

When the response is returned, the program prints out the results from both requests separately.

Note how the requests obtain results for the first two pages of the same query in one go. Those could have been completely different queries too.

A simpler constructor for AmazonSigningEndpointBehavior

A recurring stumbling block for readers of part 1 was the constructor of the AmazonSigningEndpointBehavior class. The first constructor argument was the operation name — the string that is embedded in the SOAP request as the Action header. The constructor needed to track the operation name, in order to include it as part of the request signature. For an ItemSearch operation, this value was supposed to be “ItemSearch”. For an asynchronous ItemSearchAsync operation, this value was supposed to be “ItemSearch” too.

<s:Envelope xmlns:s="">
  <s:Header xmlns:aws="" xmlns:a="">
    <a:Action s:mustUnderstand="1">

// add authentication to the ECS client
    new AmazonSigningEndpointBehavior(
        "ItemSearch", accessKeyId, secretKey));

There was an opportunity for confusion here. Some folks placed ItemSearchAsync here, or BeginItemSearch, which wouldn’t work. To avoid the problem, the endpoint behavior constructor no longer requires an operation name. Instead, the operation is extracted from the Action header, just before the request is signed. If you are using the helper classes in the updated sample, you don’t need to do anything to take advantage of that.

// extract the operation from the url in the Action header,
// past the last '/' character.
string operation = Regex.Match(


Product advertising API responses can grow quite long, especially once you go beyond the puny “Small” response group. This quickly overwhelms the 16KB default limit in our WCF client code, causing an exception in client.ItemSearch(…). To avoid the exception, set the MaxReceivedMessageSize property of the binding object to some larger value. The sample program does this:

BasicHttpBinding binding = new BasicHttpBinding(
binding.MaxReceivedMessageSize = int.MaxValue;

What’s in the sample code?

The sample solution has three console applications:

  1. Simple — just the bare-bones sample, issuing a synchronous ItemSearch request
  2. Batch — a modified version of Simple, sending two batched requests in one SOAP message
  3. Async — a modified version of Simple, sending an asynchronous request and waiting for the response

All three samples authenticate their requests, using the exact same three helper classes.

To run the samples, don’t forget to substitute your own Amazon access key and secret key, at the constants at the top of Program.cs. The samples do not include valid keys, and will not return any results from the Amazon web service without your valid keys.

More information

How to: Call WCF Service Operations Asynchronously (MSDN)

My thanks to Erica Sanders, Jason Foglia and Andrew for helping figure all this out.

About these ads

44 Responses to Signing Amazon Product Advertising API – C#/WCF – Part 2

  1. AndySee says:

    This is awesome – works perfectly! Thank you very much, our code now works just before tomorrow’s deadline.

    What is your licensing on this code.


  2. Oren Trutner says:

    Hey Andy, I’m glad it was useful!

    As for license — it’s sample code after all, and I’d like you to be able to do with it what you like. You don’t have to share any derivative code, and I don’t care about attribution either. As I’m no lawyer, I put an MIT license on the sample, which is the most permissive license I could find. The portion of the license I care about is the no-warranty part — if the code ends up taking down your entire system, you can’t come after me, nor can someone else you give the code to.

    Another way to look at it is this: you read the article, found a useful idea or two, and went and implemented your version of the ideas. Your code is unrelated to mine, and we have no claims on each other. Just keep in mind that this article too is provided “as-is”, with no warranty of any kind =)

    I hope this is permissive enough to let you use it, and please let me know if there’s a better idea.

    You can find the MIT license at

  3. AndySee says:

    Thank you very much Oren. That works and is very much appreciated!

  4. Oren:

    FYI, I did a quick & dirty VB.Net translation of the sample solution, posted here: . That language community on the AWS/PAA forums seemed to need a bit of assistance ( ).

  5. IliaKor says:

    hello Oren ,

    Your AmazonProductAdvtApiWcfSample examples with signature work fine,thank you

    The problem is: in our lookup programm I am using a WebReference not a ServiceReference. This means that the AWSECommerceService object is used instead of the AWSECommerceServicePortTypeClient object.
    i did not find a reference to use you Amazon.ECS.Addons moduls.
    if it is imposible to process signature with AWSECommerceService,I could change the programm to use AWSECommerceServicePortTypeClient.
    i try to verify if I can use AWSECommerceServicePortTypeClient to send request for operation: “offerSummary”

    Could you correct this example,please?

    example: find amazon offers(best prices) for asin=”B000065UFD” .I have changed your AmazonProductAdvtApiWcfSample Simple example project (but it doesn’t work):
    // test with itemlookup
    ItemLookupRequest itemLookupRequest = new ItemLookupRequest();
    itemLookupRequest.IdType = ItemLookupRequestIdType.ASIN;
    itemLookupRequest.ItemId = new string[] { “B000065UFD”};
    //itemLookupRequest.responseGroup = new string[] { “offerSummary” };

    ItemLookup itemLookup = new ItemLookup();
    itemLookup.AWSAccessKeyId = accessKeyId;
    itemLookup.Request = new ItemLookupRequest[] { itemLookupRequest };
    ItemLookupResponse itemLookupResponse = new ItemLookupResponse();
    // issue the ItemLookup request
    itemLookupResponse = client.ItemLookup(itemLookup);
    catch (Exception ex) { string test = ex.Message;}
    // write out the results
    foreach (var item in itemLookupResponse.Items[0].Item)

    instead of
    // prepare an ItemSearch request
    ItemSearchRequest request = new ItemSearchRequest();
    request.SearchIndex = “Books”;
    request.Title = “WCF”;
    request.ResponseGroup = new string[] { “Small” };

    ItemSearch itemSearch = new ItemSearch();
    itemSearch.Request = new ItemSearchRequest[] { request };
    itemSearch.AWSAccessKeyId = accessKeyId;

    // issue the ItemSearch request
    ItemSearchResponse response = client.ItemSearch(itemSearch);..

    • Oren Trutner says:

      Ilia, if you decide to stay with the web reference, Amazon’s original code sample might help you out. On the other hand, if you decide to move on to WCF (the service reference), note that the API differences aren’t all that much, and your application might only require a few changes.

      • Joe says:

        In my old code from a few years ago I have it set up as a web reference. I began making changes to comply with the new Amazon requirements and I was still setting it up as a web reference since I’m making the call to Amazon through a webpage not a console application. I’ve been having some issues getting it running correctly. Am I able to use the service reference via a web project and make the calls like you have in your sample project? I don’t mind changing any of the existing code for the new API. Thanks!

  6. Shabdar says:

    I have implemented fully working sample using new signature requirement from Amazon. It doesn’t use web reference but WebRequest instead. You can download it from following link,

    • Oren Trutner says:

      Shabdar, thank you for sharing the reference! For those interested, Shardbar’s sample uses neither a service reference nor a web reference. Instead, it uses the Amazon REST interface (not SOAP), sends and receives xml payloads, and encodes them internally as data set objects. The obvious benefits are that it’s dead simple, and that you get full control of the request format. One downside is that you don’t get strongly typed request and reply objects.

      Let Shardbar know if you use the sample, or if you have any comments.

  7. Oren Trutner says:

    Joe, you should be able to use the service reference in your web application. The classes and methods you use to invoke requests and process the results are almost identical to the ones you get from a web reference. It’s really a judgment call on your part which type of reference to choose. For a new app, I’d pick a service reference. For a legacy app that uses the web reference all over the place, it’s a question of how comfortable you would feel going in and updating those portions of the code.

  8. Bill Blair says:

    I keep getting this message when I try to run the code: Message = “The HTTP request was forbidden with client authentication scheme ‘Anonymous’.” So, how do I fix this?

    • Bill Blair says:

      Also, I get the following error message: ‘Simple.AmazonSigningEndpointBehavior’ does not contain a constructor that takes ’3′ arguments, when I add “ItemSearch” to AmazonSigningEndpointBehavior. Why is this happening?

    • Oren Trutner says:

      Hey Bill, does the sample app work for you? Just make sure to insert your own amazon id’s in it.

  9. Oren Trutner says:

    I removed the endpoint behavior’s constructor 3rd argument from the sample for part 2 of this post. You can just pass the first two arguments. (the 3rd argument carried the SOAP operation name; this was redundant and, and created an opportunity to pass an incorrect value. Instead, the operation name is now extracted from the request in BeforeSendRequest()).

  10. Muhammad Elnahrawi says:

    I suggest to use this additional code:

    binding.ReaderQuotas = System.Xml.XmlDictionaryReaderQuotas.Max

  11. Pedro Maia says:

    Before anything, I should say it is a great post.
    I’m trying to apply the same receipt to the SimpleDB service (I know I’ve the C# library but I would like to have access from raw WSDL).

    I’ve managed to avoid the error (“…missing credentials”) but now I’m stuck with the (” … bad request”).
    Have you tried to use simple DB with soap requests? Any luck?

  12. Voja says:

    Im trying to search for a book via it’s ISBN number without any success. Do you have any code examples doing that?

  13. parminder says:

    Hi there,
    I am trying to use your code, but it doesnt work.
    The error is The remote server returned an unexpected response: (400) Bad Request

    One question. Do i need to pay to amazon or need to be a paid member to get it work or its free to search books. I have access key and secret key. is it enough.


  14. Adnan Khan says:

    My respone from ItemSearchResponse are returning nothing.
    I have done everything and I still can’t figure it out. Can you please take a look at the code below and reply me. thank you..

    I have used version and it works fine in the class project. But now I have created new 3.5 framework website. in which I am using this code. Everything is same but how come it’s not working in the website.

    ‘create a WCF Amazon ECS client
    Dim binding As New BasicHttpBinding(BasicHttpSecurityMode.Transport)
    binding.MaxReceivedMessageSize = Int32.MaxValue
    Dim client As New AWSECommerceServicePortTypeClient( _
    binding, _
    New EndpointAddress(“ Service=AWSECommerceService”))

    ‘add authentication to the ECS client
    client.ChannelFactory.Endpoint.Behaviors.Add(New AmazonSigningEndpointBehavior(MY_AWS_ACCESS_KEY_ID, MY_AWS_SECRET_KEY))
    client.ClientCredentials.ClientCertificate.Certificate = New X509Certificate2(“C:\Inetpub\wwwroot\my.pem”)

    ‘prepare an ItemSearch request
    Dim request As New ItemSearchRequest()
    request.SearchIndex = “Books”
    request.Title = “WCF”
    request.ResponseGroup = New String() {“Small”}

    Dim ItemSearch As New ItemSearch()
    ItemSearch.Request = New ItemSearchRequest() {request}
    ItemSearch.AWSAccessKeyId = MY_AWS_ACCESS_KEY_ID
    ItemSearch.AssociateTag = MY_AssociateTag
    ‘ItemSearch.Validate = True

    ‘issue the ItemSearch request
    Dim response As ItemSearchResponse = client.ItemSearch(ItemSearch)
    Dim title As String

    ‘write out the results
    For Each item As Item In response.Items(0).Item
    title = item.ItemAttributes.Title

    Please help ! and Thank You
    Adnan Khan

  15. Tom Lianza says:

    If you happen to run the AmazonSigningMessageInspector in an environment that uses a different date/time format, you’ll notice the tostring that generates the timestamp puts dots between the hours/minutes/days when amazon requires colons. I had to change this:

    string timestamp = now.ToString("yyyy-MM-ddTHH:mm:ssZ");

    To this:

    string timestamp = now.ToString("yyyy-MM-ddTHH':'mm':'ssZ");

    Hope it helps someone!

  16. Numan says:

    I am searching books through ISBN it is returning fine now I am trying to get the category of a book but i can’nt find it How to get the category of a book.?
    i have looked into this item.ItemAttributes.Category but it returns null.
    can any one help me ?

  17. Numan says:

    IS there any way to get more than 4000 records.?

  18. coolj says:

    Hi, thank you for the great sample code I used it just fine. I just would like to know if Im missing something or not on one process I am implementing. I have tested different response groups already but I cannot get the OfferListings Response group. The OfferSummary display the correct values like how many offers are available but there is noting in the Offers collection. Thanks again…

  19. Prabu says:

    when using latest wsdl, CartCreate is returning null due to some namespace problem.

    Service returns xml but wcf is not able to deserialize.

    Anyone faced this problem and able to solve it?

  20. John says:

    Hi, thanks for the works fine especially with the itemlookup, but im having a hard time figuring out the issue on SellerLookup as the Response always returns null.

    SellerLookup sellerLooker = new SellerLookup();
    SellerLookupRequest sellerRequest = new SellerLookupRequest();

    sellerLooker.AWSAccessKeyId = accessKeyId;

    sellerRequest.SellerId = new string[] { MerchantId };
    sellerRequest.FeedbackPage = “1″;
    sellerRequest.ResponseGroup = new string[] { “Seller” };
    sellerLooker.Request = new SellerLookupRequest[] { sellerRequest };

    SellerLookupResponse sellerResponse = portClient.SellerLookup(sellerLooker);
    if (null != sellerResponse)
    string name = sellerResponse.Sellers[0].Seller[0].Nickname;

    am I missing something in the parameters here?thanks!

  21. Paul says:

    I get the following error message when trying to use the sample above? I have entered in my access Key and secret key…

    {“The remote server returned an unexpected response: (400) Bad Request.”}

    • stan says:

      I downloaded the sample project but the project falls over here:

      foreach (var item in response.Items[0].Item) {

      It doesn’t bring back any results, leaving item to be null and I get the error message:

      Object reference not set to an instance of an object.

      Has amazon API changed since you wrote this code or am I missing something obvious?

      • I also have this problem, every amazon sample that I can find fails on the foreach loop with null reference exception, I know its going to be simple but I just cant figure it out, please help.

  22. Stan,
    In the app.settings file add a new key:

    then in the program.cs file, under the line

    itemSearch.AWSAccessKeyId = ConfigurationManager.AppSettings["accessKeyId"];

    add the line:

    itemSearch.AssociateTag = ConfigurationManager.AppSettings["associateTag"];

  23. the should have said, add a new key:

    it appears that associate tag is now required and as this code has no error handing, it returns no results but has no wqy of telling you :)

  24. Hey great code. It sure is cleaner to do this stuff with WCF then it is by hand or even with WSE 3.0. The code didn’t work “out of the box” since requests now need an AssociateTag as well. So if you add
    search.AssociateTag = “your associate tag”;
    then it works great.


  25. Nuno Senica says:

    HI there! Nice blog! I’m trying to run your code now and the result of the SearchItem always returns null. It used to work before, so I guess Amazon changed something recently. Can someone please confirm this? What can I do?

  26. Scott says:

    Hi, I had been using this code (with modifications) for over a year, and it has suddenly stopped working. I’ve added basic error handling, and I’m not getting any errors back from Amazon. I just get a null return from request. I’ve already added the associate tag, when that became mandatory, and it was working after that. Something else must have changed, but I’ve no idea what. Can anybody help?

  27. Karin Gates says:

    Same as Scott above. It suddenly broke in the last month or so. (Feb-Mar 2012) Anybody know how to fix?

  28. Karin Gates says:

    @Anonymous: Yes, the solution from “savagepandas”:
    1. Right-click on your service reference and select and choose “Update Service Reference”.
    2. Open AWSECommerceService.wsdl file and change these 2 lines:


    3. Open reference.cs and change all occurrences of “ImageSet[][]” to “ImageSet[]“.

    • Karin Gates says:

      My last posting removed the html sample on #2, so find the lines in AWSECommerceService.wsld that contain ImageSet and ImageSets, and change maxOccurs from “unbounded” to “1″.

      • Anonymous says:

        Karin,Thank you! Thank you! Thank you! Your directions worked perfectly!!! Thank you again!!!!


  29. Imran Asif says:

    Such a great article. However i am facing problem. The program stops at the foreach loop in main. I am new to programming and have no idea why is it happening. Please help me thanks.

  30. Pingback: Signing a Amazon Product Advertising API Request | VB.NET Guru

  31. Andy says:

    One, you’re incredibly patient with everybody saying ‘please do all my work for me.’
    Second, I got your code to work perfectly, but the single items.item that’s returned is always null. Any pointers?

  32. Monopoly says:

    Can you give me the code for getting daily deals of amazon or special discount products.
    Many thanks!

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


Get every new post delivered to your Inbox.

%d bloggers like this: