Guide: How to use the Walmart Affiliate API with NodeJS

Guide: How to use the Walmart Affiliate API with NodeJS

As I explained in my post "On Making Price Trackers", I prefer to only make a price tracker for a shop if it has an API for me. And luckily, Walmart has one, so I decided to build Waltrack.net, the first free Walmart Price Tracker.

However, the road from making my Walmart developer account to actually making my first API call was a bumpy one. The API requires you to create a keypair and use the secret key to sign the request. Something I haven't done before.

Luckily I managed to get it working and hopefully with this blogpost you won't have to spend as much time figuring things out as I did.

Assuming you already have your Walmart.io developer account, the first step is to create an application and create an RSA keypair.

Walmart's guide on this is solid, so you can just follow it.

When you copy-and-pasted the public key to the upload page on Walmart.io you are ready to make your first API call

Making your first API call to Walmart.io

If you want to comment on this code, you can do so on the Github Gist I created.


import NodeRSA from "node-rsa";

const keyData = {
    consumerId: "CONSUMER_ID",
    privateKey: `-----BEGIN RSA PRIVATE KEY-----
MY PRIVATE KEY
-----END RSA PRIVATE KEY-----`,
  keyVer: 1,
  impactId: "YOUR IMPACT AFFILIATE ID" // not required
},
    
    
const generateWalmartHeaders = () => {
  const { privateKey, consumerId, keyVer } = keyData;
  const hashList = {
    "WM_CONSUMER.ID": consumerId,
    "WM_CONSUMER.INTIMESTAMP": Date.now().toString(),
    "WM_SEC.KEY_VERSION": keyVer,
  };

  const sortedHashString = `${hashList["WM_CONSUMER.ID"]}\n${hashList["WM_CONSUMER.INTIMESTAMP"]}\n${hashList["WM_SEC.KEY_VERSION"]}\n`;
  const signer = new NodeRSA(privateKey, "pkcs1");
  const signature = signer.sign(sortedHashString);
  const signature_enc = signature.toString("base64");

  return {
    "WM_SEC.AUTH_SIGNATURE": signature_enc,
    "WM_CONSUMER.INTIMESTAMP": hashList["WM_CONSUMER.INTIMESTAMP"],
    "WM_CONSUMER.ID": hashList["WM_CONSUMER.ID"],
    "WM_SEC.KEY_VERSION": hashList["WM_SEC.KEY_VERSION"],
  };
};



export const getProductById = async (productId) => {
  const options = {
    method: "GET",
    headers: generateWalmartHeaders(),
  };

  const res = await fetch(
    `https://developer.api.walmart.com/api-proxy/service/affil/product/v2/items/${productId}?publisherId=${keyData.impactId}`,
    options
  );
  
  return await res.json()
};

Note: If you get an error saying thate node-rsa Expected 0x2: got 0x30, you can try to convert your key. I am not that proficient with SSL/RSA and keys and all that, but using this tool to transform my PKCS#8 to PKCS#1 did the trick.

Also make sure you don't have any wrong indentation.

This will work:

const keyData = {
    consumerId: "CONSUMER_ID",
    privateKey: `-----BEGIN RSA PRIVATE KEY-----
MY PRIVATE KEY
-----END RSA PRIVATE KEY-----`,
  keyVer: 1,
  impactId: "YOUR IMPACT AFFILIATE ID" // not required
},
    

This won't work:

const keyData = {
    consumerId: "CONSUMER_ID",
    privateKey: `-----BEGIN RSA PRIVATE KEY-----
                 MY PRIVATE KEY
                 -----END RSA PRIVATE KEY-----`,
  keyVer: 1,
  impactId: "YOUR IMPACT AFFILIATE ID" // not required
},
    

This won't work either:

const keyData = {
    consumerId: "CONSUMER_ID",
    privateKey: `-----BEGIN RSA PRIVATE KEY-----MY PRIVATE KEY-----END RSA PRIVATE KEY-----`,
  keyVer: 1,
  impactId: "YOUR IMPACT AFFILIATE ID" // not required
},
    

Explaining how to make Walmart API Headers


const generateWalmartHeaders = () => {
  const { privateKey, consumerId, keyVer } = keyData;
  const hashList = {
    "WM_CONSUMER.ID": consumerId,
    "WM_CONSUMER.INTIMESTAMP": Date.now().toString(),
    "WM_SEC.KEY_VERSION": keyVer,
  };

  const sortedHashString = `${hashList["WM_CONSUMER.ID"]}\n${hashList["WM_CONSUMER.INTIMESTAMP"]}\n${hashList["WM_SEC.KEY_VERSION"]}\n`;
  const signer = new NodeRSA(privateKey, "pkcs1");
  const signature = signer.sign(sortedHashString);
  const signature_enc = signature.toString("base64");

  return {
    "WM_SEC.AUTH_SIGNATURE": signature_enc,
    "WM_CONSUMER.INTIMESTAMP": hashList["WM_CONSUMER.INTIMESTAMP"],
    "WM_CONSUMER.ID": hashList["WM_CONSUMER.ID"],
    "WM_SEC.KEY_VERSION": hashList["WM_SEC.KEY_VERSION"],
  };
};

To create the headers we need 4 things:

  1. The private key
  2. The Consumer ID from the Walmart IO Dashboard
  3. The Key Version, also from the Walmart IO Dashboard (see screenshot below)
  4. A NodeJS library to read and sign the private key – I used node-rsa ( yarn add node-rsa )

Basically what you have to do with the headers is concatenate the Consumer ID, the Timestamp and the Key Version with new lines between them.

Then sign that string with your private key and convert it to base64. Then append this base64 string in the headers, so Walmart on their side can check with the public key you uploaded before if it's actually you. It's kinda how SSL certificates work as well.


That's in a nutshell how to create API calls to the WalmartIO API. All in all it's not that hard, but it's kinda unusual to use a secret key to sign an API request.

For any questions or problems you run into, you can comment on the Github gist here.

Happy Walmart API hacking!

Jelle