Amazon selling partner API (SPAPI) — how to quickly and simply integrate with new API (Part 2)

Volodymyr Danyliv
6 min readDec 20, 2020

--

This is the second part of my introduction to MWS / SPAPI integration, if you are interested in how to register an application in a seller account, please take a look at the first part of the article.

Requirements:

  1. Amazon Developer Seller Account
  2. AWS basic knowledge
  3. NodeJS basic knowledge

In this article I will show you:

  1. How to generate a Refresh token for your application (self-authorized).
  2. How to request an Access token
  3. How to Sign your SPAPI requests
  4. How to make your first API call!

Before we start, you need to have a region config, which you can find in my repo.

Please download regions.js as it will be required to correctly set up future requests. (Also in this repo you can find another useful information that will be needed in this article)

How to generate a Refresh token for your application (self-authorized).

What is the refresh token? If you use OAuth authentication flow or self-authorized in-app, you will get a refresh token, this is a token that Amazon gives you to have the possibility to exchange it for an access token. The refresh token is the secret token and you should keep it in a safe place and do not share it with others. This token does not have any TTL.

So how to get a Refresh token?

  1. Sign-in into your seller account
  2. Open App & Services -> Develop App
  3. Find an app for which you want to get a Refresh token
Applications page

Find an app for which you want to generate a token, click on the arrow icon and select Authorize

Click on Generate Refresh Token, copy, and save your token in a safe place. (I save secret tokens generated by users or my own inside AWS DynamoDB, you can save in any other storage type, if you want to know more about secrets rotation and possible solutions where to save such data, please leave a comment.)

How to request an Access token

The access token is used to have the possibility to communicate with Amazon SPAPI. The difference between the Refresh token and the Access token is that the last is temporary and has 1hr TTL, after that time you need to re-generate it. Below you can find a file with a possible solution for how you can request an access token.

This request does not require any kind of sign.

const qs = require('qs');
const fetch = require('node-fetch');
module.exports.requestAccessToken = async() => {
const body = {
grant_type: 'refresh_token',
client_id: process.env.LWA_CLIENT_IDENTIFIER,
refresh_token: process.env.AMAZON_REFRESH_TOKEN,
client_secret: process.env.LWA_CLIENT_SECRET,
};
const acccessToken = await fetch('https://api.amazon.com/auth/o2/token', {
method: 'POST',
body: qs.stringify(body),
headers: {'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'},
});
if (acccessToken.ok) {
return await acccessToken.json();
} else {
throw new Error(acccessToken.statusText);
}
};

Expected result:

{“access_token”:”Atza|XXXX”,”token_type”:”bearer”,”expires_in”:3600}

The second step is done! Now using a combination of access_token and refresh_token you can make SPAPI requests.

How to Sign your SPAPI requests?

Amazon requires you to sign each request to SPAPI using Signature Version 4. In this part, we will generate temporary access using AWS STS and sign all requests using the aws4 package.

Signature Version 4 is the process to add authentication information to AWS requests sent by HTTP. For security, most requests to AWS must be signed with an access key, which consists of an access key ID and secret access key. These two keys are commonly referred to as your security credentials. For details on how to obtain credentials for your account, see Understanding and getting your AWS credentials.

If you plan to run this code on lambda, please assign a role that has STS write permissions (check the link below). If you plan to use a user with programmatic access, don't forget to correctly initialize AWS SDK.

Let's write a simple piece of code that will generate temporary AWS credentials.

const AWS = require('aws-sdk');
const STS = new AWS.STS();

const createTemporaryAWSCredentials = async() => {
const {
Credentials: {
AccessKeyId,
SecretAccessKey,
SessionToken,
}
} = await STS.assumeRole({
RoleArn: process.env.SP_ROLE_ARN // YOUR IAM ROLE ARN
RoleSessionName: 'sp-api',
}).promise();

return {
AccessKeyId,
SecretAccessKey,
SessionToken,
};
};

Don't forget to input the correct SP_ROLE_ARN. This is the role you input while creating your amazon application. Link to source below

We almost finished, the last thing that you need to do is to sign your request with AccessKeyId, SecretAccessKey, SessionToken. Let's do it!

I will implement requestConfig method that will accept temporary credentials tokens and application access_token that we generated at the top of this article. As a result, you have a signed request that you can pass into https.request();

In this example, I will use a Catalog API (/catalog/v0/items) with Query pen and marketplaceId ATVPDKIKX0DER

const aws4 = require('aws4');
const {regions} = require('./regions');

const requestConfig = (marketplaceId, access_token, tempCreds) => {
const {
AccessKeyId,
SessionToken,
SecretAccessKey,
} = tempCreds;

const {
aws_region,
end_point,
} = regions.get(marketplaceId);

const params = {
path: `/catalog/v0/items?MarketplaceId=${marketplaceId}&Query=toys`,
method: 'GET',
host: end_point,
region: aws_region,
service: 'execute-api',
headers: {
'User-Agent': 'MyAmazonApp/1.0 (Language=JavaScript;)',
'x-amz-access-token': access_token,
},
};

return aws4.sign(params, {accessKeyId: AccessKeyId, secretAccessKey: SecretAccessKey, sessionToken: SessionToken});
}

Signed request data example:

{
"path": "/catalog/v0/items?MarketplaceId=ATVPDKIKX0DER&Query=pen",
"method": "GET",
"host": "sellingpartnerapi-na.amazon.com",
"region": "us-east-1",
"service": "execute-api",
"headers": {
"User-Agent": "MyAmazonApp/1.0 (Language=JavaScript;)",
"x-amz-access-token": "XXX",
"Host": "sellingpartnerapi-na.amazon.com",
"X-Amz-Security-Token": "YYY",
"X-Amz-Date": "20201219T195941Z",
"Authorization": "AWS4-HMAC-SHA256 Credential=ZZZ, SignedHeaders=host;x-amz-access-token;x-amz-date;x-amz-security-token, Signature=PPP"
}
}

How to make your first API call

Now we need to pass signed request data into https.request() method. In this example, I use NodeJS https module.

const request = signedRequest => {
return new Promise((resolve, reject) => {
https.request(signedRequest, (res) => {
let data = '';
const {statusCode} = res;

res.on('data', chunk => (data += chunk));
res.on('end', async() => {
const res = data && JSON.parse(data);

if (statusCode === 200) {
resolve(res);
} else {
reject({statusCode, res});
}
});
}).end();
});
};

After a successful method invocation you should get the next result:

{
"payload": {
"Items": [
{
"Identifiers": {
"MarketplaceASIN": {
"MarketplaceId": "ATVPDKIKX0DER",
"ASIN": "B07Y8KN5QD"
}
},
"AttributeSets": [
{
"MaterialType": [
"metal"
],
"Binding": "Office Product",
"Brand": "Asvine",
"Color": "Black",
"Label": "Hongdian",
"Manufacturer": "Hongdian",
"PackageDimensions": {
"Height": {
"value": 1.2199999987556,
"Units": "inches"
},
"Length": {
"value": 7.4799999923704,
"Units": "inches"
},
"Width": {
"value": 2.4799999974704,
"Units": "inches"
},
"Weight": {
"value": 0.18077905484,
"Units": "pounds"
}
},
"PartNumber": "HD B EF 0.38",
"ProductGroup": "Office Product",
"ProductTypeName": "WRITING_INSTRUMENT",
"Publisher": "Hongdian",
"Size": "Extra Fine Point",
"SmallImage": {
"URL": "https://m.media-amazon.com/images/I/41TcQSxOraL._SL75_.jpg",
"Height": {
"Units": "pixels",
"value": 75
},
"Width": {
"Units": "pixels",
"value": 75
}
},
"Studio": "Hongdian",
"Title": "Matte Black Forest Fountain Pen Extra Fine Nib Classic Design with Converter and Metal Pen Box Set by Hongdian"
}
],
"SalesRankings": [
{
"ProductCategoryId": "office_product_display_on_website",
"Rank": 113
},
{
"ProductCategoryId": "1085704",
"Rank": 1
}
]
},....
]
}
}

Congratulations! You successfully run your first call to SPAPI.

Thanks for reading, hope you’ve enjoyed my story and had fun. In case you have any questions please let me know in the comments.

--

--