Don’t Write your API this way.

How eBay made my life hell.

Sean Clark
On Coding
Published in
6 min readApr 1, 2014

--

This is the story of a lonely eBay API called the Large Merchant Services API. Technically this is a part two of my “How Not to Write an API” series. But before we begin down the potholed road of the bulk API, let’s go through real quick how the eBay API normally works.

Ebay actually has a fairly decent API. That is to say, if you only need to use one of their APIs. eBay has seventeen APIs I’ve found so far. Even though I’ve found seventeen APIs, eBays developer test tool only shows three. I guess you should only use these.

Only 3 APIs

So Let’s look really quickly on how to use these APIs in your application. I’ll be using PHP code examples, but really any language will do, there is no SDK used here.

Just skim this

  1. Get an eBay developer account on developer.ebay.com
  2. Create a set of sandbox and production keys.
  3. Generate a user token for those keysets. These will open up an oauth 1.0 connect window in which you login to your eBay account and connect to your developer account. This results in an auth token that you’ll use in your API calls.
  4. Based on the type of API, figure out your endpoint and XMLNS url. How do you figure it out? If your API is lucky enough to be in the API test tool, look there, otherwise start googling.
  5. Set your headers. This doesn’t include your auth token, just your X-EBAY-API-CALL-NAME which is your method, your content type of XML and and eBay compatibility level. For some API calls (not in the API test tool) you need additional headers. You’ll need to google around for those as well.
  6. Turn your PHP array (or other) into XML. Even though your method might be ReviseFixedPricedItem your XML packet needs to be wrapped with <ReviseFixedPricedItemRequest>
  7. Make sure to add the correct XMLNS url on your root Request node.
  8. Add a <RequesterCredentials> XML node with your auth token that you got from your API keys.
  9. Send that over cURL to the eBay end point and get your result back.

Not bad eBay. I’ve seen worse.

With some small differences here and there, and a bunch of googling for details out of the way, you can use the above 9 steps for almost every eBay call you need to make. Ebay of course gives you a 34MB java SDK to use. If you don’t want to do these 9 steps.

I went ahead and made a 6KB version in PHP that allows you do:

$ebay = new ebay($auth);
$ebay->call(“trading/RevisedFixedPricedItem”,[
"ItemID"=>123456789,
"Title"=>"Medium Rocks!"
]);

Anyway, so now that you know how eBay APIs work, and how I’m using them. Let’s talk about the bulk API.

Forget everything, none of it applies.

Steps 1-3 where you create your developer account apply. But that’s where it stops. This one API call, to bulk update listings is completely different. Let’s dive in.

Just skim this too

You don’t need to understand the details unless you want to, just share in the ridiculousness of this journey.

  1. Use the above 1-9 steps to make a normal XML API call to “createUploadJob” passing it the type of job you want, and a UUID. You will get back a jobId and a fileId. This call is to the BulkDataExchangeService API.
  2. Form an XML document that repeats the type of request you want as many times as you need it to be called. So for ReviseFixedPricedItem it would be <ReviseFixedPricedItemRequest> x 20 for 20 products.
  3. Make sure to add the correct XMLNS to every single one of those 20 nodes.
  4. Wrap that entire XML document in a <BulkDataExchangeRequests> node.
  5. Before your massive array of repeated fields, add a <Header> node that contains the SiteID and API Version. This is instead of the Site and Version headers from the previous API calls.
  6. Save the XML document to disk.
  7. GZIP the XML document. (You can do step 6 and 7 together).
  8. Read the GZIP file back in.
  9. Create two new UUIDs, one for a request and one for an attachment.
  10. Setup a SOAP 1.1 or 1.2 with MTOM encoding or XML over HTTP to include the file. This, I learned means you form your XML packet for the FileTransferAPI’s method of uploadFile to have a key called fileAttachment. This key has Size and Data. The Data key is a <xop:Include node which it’s own XMLNS:xop url and a cid:urn:uuid that matches the attachment UUID you created in step 9.
  11. Create the earlier steps 1-9 XML style request for “uploadFile”
  12. Instead of passing that XML like the old days, we now need to form a multi-part related request.
  13. Create your first MIME part using the Content-Type of
Content-Type: application/xop+xml; charset=UTF-8; type=”text/xml;

14. Set the Content-ID part to something like <0.urn.uuid:123456 where 123456 matches the request UUID from step 9

15. Now dump your XML from step 11 with an ending MIME boundary

16. Create your second MIME section using and application/octet stream. This sections Content-ID should match the attachment UUID from step 9.

17. Before you end the last boundary, make sure to include your read in file data from step 8

18. Setup your headers for the request to have this complex Content-Type of multipart-related. Here you tell it what your boundary is, that the first type is xop+xml and your starting <0.urn:uuid that matches the request UUID from step 9.

19. Combine both sections from step 13 and 16 and send that request with the headers from step 18.

20. Make a normal 1-9 style XML API call to the BulkDataExchangeService API using the method “startUploadJob” to actually start the upload.

21. Later you can check on this with Bulk data services / getJobStatus

UPDATE 4/3/14

22. If you want to find out any errors, you need to make a new Bulk Data Exchange Service API call to DownloadFile using a new FileID you got in the result from step 21. Once you get that data you can save it your file system as a GZ file, ungzip it and read the XML results of any errors that occured.

So that’s about it. You’d think they tried to make that as hard as possible. Maybe I should have kept making thousands of calls to eBay every hour to change listing inventory levels.

How it should be done

It would be really one sided for me to just complain and not offer any solutions. Quickbooks online actually does a great job with their new API. It’s all JSON.

Always expect an array of inputs

When making an API, always expect an array to process. Even if you offer convenience method like updateOne vs updateAll, have your API always process multiple records. Just normalize the updateOne call. If your system needs to do some crazy batch processing like eBay, offload that once the API call is made. Don’t make me jump through hoops.

Don’t upload files unless they are actually files

It was pretty ridiculous of eBay to have me form a perfectly good XML packet only to save it out, gzip it up and send binary data. If we’re just sending data, just let me send the data. Only use attachments for things that are actually binary files.

Keep headers consistent across API calls

Don’t have some API methods that pass auth in the call, and others in the headers. Keep it consistent across the board.

Have good docs

I know this is hard. But invest the money. Keep your docs up to date and easy to read. Be nice to developers and they will be nice to you. eBay’s docs say the GZIP file has to contain base64 encoded data, but that is just not true.

Accept JSON and return JSON

This is probably the Javascript guy in my head speaking. But please just let me pass JSON to you, and you pass JSON back to me. No need for XML or SOAP anymore. I know for something XML is still needed, but not for anything I’ve dealt with for eBay.

Thanks guys. I’ve had a hell of a journey learning the depths of the eBay API, Amazon MWS API, Amazon ADV API, Quickbooks online API, Google docs API, Stripe API, PayPal API and more. We use all of these technologies to automate E-Commerce at http://Whitebox.co

--

--