How to Make Amazon SP-API Requests in PHP Without SDK
Mar 29, 2023Computer HardwareComments (39)
This post covers how to make requests to the Amazon Selling Partner API (SP-API). The examples used here will be for the getItemOffers operation of the Product Pricing API, but you should be able to adapt what you learn to any of the operations or Selling Partner APIs.
Amazon makes an SDK available for these operations, but doing it yourself only takes about 100 lines of code, and you'll learn a lot more, including how to get access tokens, build canonical requests, sign requests, and create authorizations.
Requirements: Using the SP-API requires that you have accounts with both Amazon AWS and Seller Central, and that you have completed a developer profile in the Developer Central section of Seller Central. Please do these steps first if needed (if this is your first time, choose "private developer" or a similar option). This process may take some time to go through approval.
Region Note: The endpoint, region, and marketplace ID values in the examples are for the US (sellingpartnerapi-na.amazon.com, us-east-1, and ATVPDKIKX0DER respectively). Find other endpoints and regions here and marketplace IDs here if needed.
Head over to IAM (Identity and Access Management):
https://console.aws.amazon.com/iam/home
The first thing to do is create a permission policy. Click Policies under Access Management. Then Create Policy. You should see four entries: Service, Actions, Resources, and Request Conditions. You only need to set data for the first three:
Service: Click Service and it should give you a large list of services. In the search box put in "ExecuteAPI" and that should filter it to just that one result. Click it to set it.
Actions: You can just click the checkbox for "All ExecuteAPI actions".
Resources: Click "Any in this account" checkbox.
Now click Next: Tags at the bottom of the page (nothing needed on this page), and then Next: Review. Give your policy a descriptive name like "SellingPartnerAPI".
You now have a policy, which you can assign to a new user. Go to Users under Access Management and click Add Users. Give the user a descriptive name (such as "SellingPartnerAPIUser") then hit Next. On the Set permissions page click "Attach policies directly". Search for your policy ("SellingPartnerAPI") and click the checkbox next to it, then Next, then Create User.
You'll need three things from your new IAM User in order to make SP-API calls:
On the main Users page, click on your new user and copy down the ARN from the summary.
You now need to create an access key for this user. Click Security Credentials and scroll down to Access Keys. Click Create Access Key. For the "best practices" page click any of the options, or just "Other", then Next. Give the access key a description like "SellingPartnerAPIAccessKey", then Create Access Key. You'll be shown the Access key and Secret Access Key. Copy both down. Note: This is the only time you'll be able to get the secret. If you lose it, you'll have to delete and create a new key.
You can now leave AWS.
An app is needed to request access tokens, which are used in conjunction with your IAM User credentials to make SP-API calls.
Go to the Developer Central section of Amazon Seller Central:
https://sellercentral.amazon.com/sellingpartner/developerconsole
As long as you are an authorized developer, you'll be able to click Add new app client. Do so now to create a new app.
Give the new app a descriptive name and for the type choose "SP API". More options should pop up now. Enter the IAM ARN that you copied from your IAM User above. This ties the app to your IAM User (this can be edited later). Next click the checkbox (or checkboxes) for any Roles your app needs. These will be restricted to what you had originally chosen when creating your Developer Profile. For this guide we are only using the "Pricing" roll. Then click "No" for the RDT options. Save and exit.
You now have an app and should be back on the main Developer Central page. You need to copy down three things from your new app:
Click "View" under LWA Credentials and copy the ID and Secret. Note that on here Amazon will show you the secret as much as you want. Take care to differentiate the LWA ID and Secret from the IAM User Key and Secret from earlier; they are entirely different.
To get the refresh token you have to click the drop-down arrow next to "Edit App" and choose "Authorize". On the next page click Authorize App. This gives you a Refresh Token. Copy it.
You now have all the information you need to make an SP-API call! You can leave Seller Central.
Your code must have access to five of the six things you had created above:
(The User ARN is not needed.)
It is highly recommended not to store the secrets or token in plain text, or commit them to a repository. Amazon recommends that you encrypt them. For the below examples you'll just be putting them into constants for ease, since this is just for learning how to make SP-API requests. It is up to you how you want to securely handle these in a real-world application.
For these examples and simplicity we'll define this data as such:
You'll need a basic cURL function that can handle both GET and POST requests. Also for the sake of simplicity any errors we run across will just exit with some details we can use to debug them.
You'll need to define some basic settings that will get used in different areas:
The refresh token is used to get a temporary access token, which is the first request you must make before making any SP-API requests. To be clear, the refresh token itself cannot be used in SP-API requests. It can only be used to get an access token.
The access token is usually valid for 3600 seconds (1 hour) so you can cache it, and only make this request when it is expired or missing. The response you get will be JSON and have an expires_in value, so you can know the exact timestamp it will no longer be valid.
You may want to store the access token in a database. For this example we're just storing it in a secure cache file (preferably something not in public root) as JSON data.
We can now call this getAccessToken function during the next step.
We're going to make a generic function that can be used to make most SP-API requests. It just takes in four arguments:
Inside this function you will do these things, which are the nuts and bolts of the Amazon SP-API request. Amazon's API is one of the more complicated ones, so I have tried to break each step down as logically as I can.
I have heavily commented the below function to help you understand each step:
With the generic Amazon SP-API request function made, we can use it to call most any SP-API operation. Here is a small function that calls the getItemOffers operation. It just takes in the ASIN that you want to get the list of offers for.
Note: The Amazon docs may not show it, but the query string arguments likely need to be in alphabetical order, as they are above.
You can download the full code example here.
Amazon makes an SDK available for these operations, but doing it yourself only takes about 100 lines of code, and you'll learn a lot more, including how to get access tokens, build canonical requests, sign requests, and create authorizations.
Requirements: Using the SP-API requires that you have accounts with both Amazon AWS and Seller Central, and that you have completed a developer profile in the Developer Central section of Seller Central. Please do these steps first if needed (if this is your first time, choose "private developer" or a similar option). This process may take some time to go through approval.
Region Note: The endpoint, region, and marketplace ID values in the examples are for the US (sellingpartnerapi-na.amazon.com, us-east-1, and ATVPDKIKX0DER respectively). Find other endpoints and regions here and marketplace IDs here if needed.
Step 1: Create IAM User and Policy in AWS
Head over to IAM (Identity and Access Management):
https://console.aws.amazon.com/iam/home
Policy
The first thing to do is create a permission policy. Click Policies under Access Management. Then Create Policy. You should see four entries: Service, Actions, Resources, and Request Conditions. You only need to set data for the first three:
Service: Click Service and it should give you a large list of services. In the search box put in "ExecuteAPI" and that should filter it to just that one result. Click it to set it.
Actions: You can just click the checkbox for "All ExecuteAPI actions".
Resources: Click "Any in this account" checkbox.
Now click Next: Tags at the bottom of the page (nothing needed on this page), and then Next: Review. Give your policy a descriptive name like "SellingPartnerAPI".
User
You now have a policy, which you can assign to a new user. Go to Users under Access Management and click Add Users. Give the user a descriptive name (such as "SellingPartnerAPIUser") then hit Next. On the Set permissions page click "Attach policies directly". Search for your policy ("SellingPartnerAPI") and click the checkbox next to it, then Next, then Create User.
Credentials
You'll need three things from your new IAM User in order to make SP-API calls:
- ARN
- Access Key
- Access Key Secret
On the main Users page, click on your new user and copy down the ARN from the summary.
You now need to create an access key for this user. Click Security Credentials and scroll down to Access Keys. Click Create Access Key. For the "best practices" page click any of the options, or just "Other", then Next. Give the access key a description like "SellingPartnerAPIAccessKey", then Create Access Key. You'll be shown the Access key and Secret Access Key. Copy both down. Note: This is the only time you'll be able to get the secret. If you lose it, you'll have to delete and create a new key.
You can now leave AWS.
Step 2: Create an App in Seller Central
An app is needed to request access tokens, which are used in conjunction with your IAM User credentials to make SP-API calls.
Go to the Developer Central section of Amazon Seller Central:
https://sellercentral.amazon.com/sellingpartner/developerconsole
As long as you are an authorized developer, you'll be able to click Add new app client. Do so now to create a new app.
Give the new app a descriptive name and for the type choose "SP API". More options should pop up now. Enter the IAM ARN that you copied from your IAM User above. This ties the app to your IAM User (this can be edited later). Next click the checkbox (or checkboxes) for any Roles your app needs. These will be restricted to what you had originally chosen when creating your Developer Profile. For this guide we are only using the "Pricing" roll. Then click "No" for the RDT options. Save and exit.
You now have an app and should be back on the main Developer Central page. You need to copy down three things from your new app:
- LWA Client ID
- LWA Client Secret
- Refresh Token
Click "View" under LWA Credentials and copy the ID and Secret. Note that on here Amazon will show you the secret as much as you want. Take care to differentiate the LWA ID and Secret from the IAM User Key and Secret from earlier; they are entirely different.
To get the refresh token you have to click the drop-down arrow next to "Edit App" and choose "Authorize". On the next page click Authorize App. This gives you a Refresh Token. Copy it.
You now have all the information you need to make an SP-API call! You can leave Seller Central.
Step 3: Writing the Code
Your code must have access to five of the six things you had created above:
- IAM User Access Key
- IAM User Access Key Secret
- App LWA Client ID
- App LWA Client Secret
- App Refresh Token
(The User ARN is not needed.)
It is highly recommended not to store the secrets or token in plain text, or commit them to a repository. Amazon recommends that you encrypt them. For the below examples you'll just be putting them into constants for ease, since this is just for learning how to make SP-API requests. It is up to you how you want to securely handle these in a real-world application.
For these examples and simplicity we'll define this data as such:
define('IAM_USER_KEY', 'YOUR_DATA_HERE');
define('IAM_USER_SECRET', 'YOUR_DATA_HERE');
define('APP_LWA_ID', 'YOUR_DATA_HERE');
define('APP_LWA_SECRET', 'YOUR_DATA_HERE');
define('APP_REFRESH_TOKEN', 'YOUR_DATA_HERE');
Step 3.1: cURL Function
You'll need a basic cURL function that can handle both GET and POST requests. Also for the sake of simplicity any errors we run across will just exit with some details we can use to debug them.
// This function can do GET and POST for many SP-API operations.
// Some SP-API operations use other methods, such as PUT, which
// are not covered in this guide.
function httpRequest($url, $post = '', $header = null, &$status = null) {
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_SSL_VERIFYPEER => true, CURLOPT_SSL_VERIFYHOST => 2,
CURLOPT_RETURNTRANSFER => true, CURLOPT_FOLLOWLOCATION => true,
CURLOPT_CONNECTTIMEOUT => 5,
]);
if ($post) curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
if ($header) curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
$out = curl_exec($ch);
if (curl_errno($ch)) exit('Error: ' . curl_error($ch));
if ($status !== null) $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
return $out;
}
Step 3.2: Settings
You'll need to define some basic settings that will get used in different areas:
// Every request should have a user agent included
define('USER_AGENT', 'My Pricing Tool/1.0 (Language=PHP)');
// The host, endpoint, region, and marketplace ID (change if not in US)
define('HOST', 'sellingpartnerapi-na.amazon.com');
define('ENDPOINT', 'https://' . HOST);
define('REGION', 'us-east-1');
define('MP_ID', 'ATVPDKIKX0DER');
Step 3.3: Requesting Access Tokens
The refresh token is used to get a temporary access token, which is the first request you must make before making any SP-API requests. To be clear, the refresh token itself cannot be used in SP-API requests. It can only be used to get an access token.
The access token is usually valid for 3600 seconds (1 hour) so you can cache it, and only make this request when it is expired or missing. The response you get will be JSON and have an expires_in value, so you can know the exact timestamp it will no longer be valid.
You may want to store the access token in a database. For this example we're just storing it in a secure cache file (preferably something not in public root) as JSON data.
// Path to secure token cache file
define('TOKEN_CACHE', '/path/to/token-cache-file.json');
function getAccessToken() {
// Return existing access token if exists and not expired
if (file_exists(TOKEN_CACHE)) {
$file = file_get_contents(TOKEN_CACHE);
$json = json_decode($file, true);
if ($json && !empty($json['token'])) {
if (!empty($json['expires']) && time() < $json['expires']) {
return $json['token'];
}
}
}
// Otherwise get new access token
$post = 'grant_type=refresh_token&refresh_token=' . APP_REFRESH_TOKEN
. '&client_id=' . APP_LWA_ID . '&client_secret=' . APP_LWA_SECRET;
$url = 'https://api.amazon.com/auth/o2/token';
$response = httpRequest($url, $post, ['user-agent:' . USER_AGENT]);
// Validate new access token response
if (strpos($response, '{"access_token":') !== 0) {
exit('Error: Access token response was bad: ' . $response);
}
if (strpos($response, 'expires_in') === false) {
exit('Error: No "expires_in" found in response: ' . $response);
}
$json = json_decode($response, true);
if (!$json || empty($json['access_token']) || empty($json['expires_in'])) {
exit('Error: Access token JSON decode failure: ' . $response);
}
// Cache access token with an expires timestamp
$cacheData = json_encode([
'token' => $json['access_token'],
'expires' => time() + $json['expires_in'],
]);
file_put_contents(TOKEN_CACHE, $cacheData);
// Return access token
return $json['access_token'];
}
We can now call this getAccessToken function during the next step.
Step 3.4: Amazon SP-API Request Function
We're going to make a generic function that can be used to make most SP-API requests. It just takes in four arguments:
- $method - Usually GET or POST
- $path - The path, not including the host/endpoint or query string. For example: "/products/pricing/v0/items/{Asin}/offers"
- $qs - The query string without a '?' - Optional (usually needed for GET operations)
- $post - The post data or often called the payload - Optional (usually needed for POST operations)
Inside this function you will do these things, which are the nuts and bolts of the Amazon SP-API request. Amazon's API is one of the more complicated ones, so I have tried to break each step down as logically as I can.
- Build a canonical request
- Create a signing key
- Create a string to be signed
- Sign the string to create a signature
- Create an authorization header
- Create the headers for the cURL request
- Run the cURL request and validate the response
I have heavily commented the below function to help you understand each step:
function amazonRequest($method, $path, $qs = '', $post = '') {
// Get access token
$accessToken = getAccessToken();
// Two formats for date used throughout the function, in GMT.
// Make sure your server's time is accurate or else requests may fail.
$date = gmdate('Ymd\THis\Z');
$ymd = gmdate('Ymd');
// Build a canonical request. This is just a highly-structured and
// ordered version of the request you will be making. Each part is
// newline-separated. The number of headers is variable, but this
// uses four headers. Headers must be in alphabetical order.
$canonicalRequest = $method . "\n" // HTTP method
. $path . "\n" // Path component of the URL
. $qs . "\n" // Query string component of the URL (without '?')
. 'host:' . HOST . "\n" // Header
. 'user-agent:' . USER_AGENT . "\n" // Header
. 'x-amz-access-token:' . $accessToken . "\n" // Header
. 'x-amz-date:' . $date . "\n" // Header
. "\n" // A newline is needed here after the headers
. 'host;user-agent;x-amz-access-token;x-amz-date' . "\n" // Header names
. hash('sha256', $post); // Hash of the payload (empty string okay)
// Create signing key, which is hashed four times, each time adding
// more data to the key. Don't ask me why Amazon does it this way.
$signKey = hash_hmac('sha256', $ymd, 'AWS4' . IAM_USER_SECRET, true);
$signKey = hash_hmac('sha256', REGION, $signKey, true);
$signKey = hash_hmac('sha256', 'execute-api', $signKey, true);
$signKey = hash_hmac('sha256', 'aws4_request', $signKey, true);
// Create a String-to-Sign, which indicates the hash that is used and
// some data about the request, including the canonical request from above.
$stringToSign = 'AWS4-HMAC-SHA256' . "\n"
. $date . "\n"
. $ymd . '/' . REGION . '/execute-api/aws4_request' . "\n"
. hash('sha256', $canonicalRequest);
// Sign the string with the key, which will create the signature
// you'll need for the authorization header.
$signature = hash_hmac('sha256', $stringToSign, $signKey);
// Create Authorization header, which is the final step. It does NOT use
// newlines to separate the data; it is all one line, just broken up here
// for easier reading.
$authorization = 'AWS4-HMAC-SHA256 '
. 'Credential=' . IAM_USER_KEY . '/' . $ymd . '/'
. REGION . '/execute-api/aws4_request,'
. 'SignedHeaders=host;user-agent;x-amz-access-token;x-amz-date,'
. 'Signature=' . $signature;
// Create the header array for the cURL request. The headers must be
// in alphabetical order. You must include all of the headers that were
// in the canonical request above, plus you add in the authorization header
// and an optional content-type header (for POST requests with JSON payload).
$headers = [];
$headers[] = 'authorization:' . $authorization;
if ($post) $headers[] = 'content-type:application/json;charset=utf-8';
$headers[] = 'host:' . HOST;
$headers[] = 'user-agent:' . USER_AGENT;
$headers[] = 'x-amz-access-token:' . $accessToken;
$headers[] = 'x-amz-date:' . $date;
// Run the http request and capture the status code
$status = '';
$fullUrl = ENDPOINT . $path . ($qs ? '?' . $qs : '');
$result = httpRequest($fullUrl, $post, $headers, $status);
// Validate the response
if (strpos($result, 'Error:') === 0) exit($result);
if (empty($result)) exit('Error: Empty response');
if ($status != 200) exit('Error: Status code ' . $status . ': ' . $result);
if (strpos($result, '{') !== 0) exit('Error: Invalid JSON: ' . $result);
// Decode json and return it
$json = json_decode($result, true);
if (!$json) exit('Error: Problem decoding JSON: ' . $result);
return $json;
}
Step 3.5: Make the getItemOffers Request
With the generic Amazon SP-API request function made, we can use it to call most any SP-API operation. Here is a small function that calls the getItemOffers operation. It just takes in the ASIN that you want to get the list of offers for.
// Use the 'getItemOffers' operation to get offers for a single ASIN
function getOffersForAsin($asin) {
$method = 'GET';
$url = '/products/pricing/v0/items/' . $asin . '/offers';
$qs = 'Asin=' . $asin . '&ItemCondition=New&MarketplaceId=' . MP_ID;
return amazonRequest($method, $url, $qs);
}
Note: The Amazon docs may not show it, but the query string arguments likely need to be in alphabetical order, as they are above.
Step 4: Full Code Example
You can download the full code example here.