GithubHelp home page GithubHelp logo

amazon-sp-api's Introduction

amazon-sp-api (client for the Amazon Selling Partner API)

The client handles calls to the Amazon Selling Partner API. It wraps up all the necessary stuff such as requesting access tokens and providing (a simplified!) way of calling the API, but also provides some convenience, i.e. a wrapper for requesting and downloading reports and an internal handling of rate limits when calls are throttled.

- Please note: There are a few breaking changes if you are
- upgrading from version 0.x.x to 1.x.x. Please see the link below.

List of breaking changes when upgrading to version 1.x.x

Contents

Prerequisites

Make sure that you followed the Selling Partner API Developer Guide and have successfully completed the steps Registering as a developer, Registering your application and have a valid refresh token (if you use the client only for your own seller account the easiest way is using the self authorization as described in the developer guide).

Installation

npm install amazon-sp-api

Getting Started

Before you can use the client you need to add your app client and secret.

Setting credentials from environment variables

Setting credentials from file

Instead of setting the credentials via environment variables you may load them from a credentials file. The default path to the file is ~/.amzspapi/credentials (path can be changed when creating a client) and you add the credentials one per line:

SELLING_PARTNER_APP_CLIENT_ID=<YOUR_APP_CLIENT_ID>
SELLING_PARTNER_APP_CLIENT_SECRET=<YOUR_APP_CLIENT_SECRET>

Setting credentials from constructor config object

Although the most convenient and recommended way of setting the credentials is via environment variables or config file it is also possible to pass the credentials inside the config object when creating an instance of the client (i.e. if you have no means of using env vars or a config file). The structure of the constructor config object will be explained below.

Usage

Require library:

// commonjs
const SellingPartner = require("amazon-sp-api");

// esm
import { SellingPartner } from "amazon-sp-api";

Create client and call API:

(async () => {
  try {
    const spClient = new SellingPartner({
      region: "eu", // The region to use for the SP-API endpoints ("eu", "na" or "fe")
      refresh_token: "<REFRESH_TOKEN>" // The refresh token of your app user
    });
    let res = await spClient.callAPI({
      operation: "getMarketplaceParticipations",
      endpoint: "sellers"
    });
    console.log(res);
  } catch (e) {
    console.log(e);
  }
})();

Config params

The class constructor takes a config object with the following structure as input:

{
  region:'<REGION>',
  refresh_token:'<REFRESH_TOKEN>',
  access_token:'<ACCESS_TOKEN>',
  endpoints_versions:{
    ...
  },
  credentials:{
    SELLING_PARTNER_APP_CLIENT_ID:'<APP_CLIENT_ID>',
    SELLING_PARTNER_APP_CLIENT_SECRET:'<APP_CLIENT_SECRET>'
  },
  options:{
    credentials_path:'~/.amzspapi/credentials',
    auto_request_tokens:true,
    auto_request_throttled:true,
    version_fallback:true,
    use_sandbox:false,
    only_grantless_operations:false,
    user_agent:'amazon-sp-api/<CLIENT_VERSION> (Language=Node.js/<NODE_VERSION>; Platform=<OS_PLATFORM>/<OS_RELEASE>)',
    debug_log:false,
    timeouts:{
      ...
    },
    retry_remote_timeout:true,
    https_proxy_agent:<HttpsProxyAgent>
  }
}

Valid properties of the config object:

Name Type Default Description
region
required
string - The region to use for the SP-API endpoints.
Must be one of: eu, na or fe
refresh_token
optional
string - The refresh token of your app user.
Required if only_grantless_operations option is set to false.
access_token
optional
string - The temporary access token requested with the refresh token of the app user.
endpoints_versions
optional
object - Defines the version to use for an endpoint as key/value pairs, i.e. "reports":"2021-06-30". If none given the client is using the first (meaning the oldest) version for an endpoint.
Call .endpoints on class instance to retrieve a complete list of all endpoints, versions and operations supported by the client.
credentials
optional
object - The app client credentials. Must include the two credentials properties SELLING_PARTNER_APP_CLIENT_ID and SELLING_PARTNER_APP_CLIENT_SECRET
NOTE: Should only be used if you have no means of using environment vars or credentials file!
options
optional
object - Additional options, see table below for all possible options properties.

Valid properties of the config options:

Name Type Default Description
credentials_path
optional
string ~/.amzspapi/credentials A custom absolute path to your credentials file location.
auto_request_tokens
optional
boolean true Whether or not the client should retrieve new access token if non given or expired.
auto_request_throttled
optional
boolean true Whether or not the client should automatically retry a request when throttled.
version_fallback
optional
boolean true Whether or not the client should try to use an older version of an endpoint if the operation is not defined for the desired version.
use_sandbox
optional
boolean false Whether or not to use the sandbox endpoint.
only_grantless_operations
optional
boolean false Whether or not to only use grantless operations.
user_agent
optional
string amazon-sp-api/<CLIENT_VERSION> (Language=Node.js/<NODE_VERSION>; Platform=<OS_PLATFORM>/<OS_RELEASE>) A custom user-agent header (see desired format in docs).
debug_log
optional
boolean false Whether or not the client should print console logs for debugging purposes.
timeouts
optional
object - Allows to set timeouts for requests. Valid keys are response, idle and deadline. Please see detailed information in the Timeouts section.
retry_remote_timeout
optional
boolean true Whether or not the client should retry a request to the remote server that failed with an ETIMEDOUT error
https_proxy_agent
optional
object - Possibility to add your own HTTPS Proxy Agent. Please see detailed information in the Using Proxy Agent section.

Use a proxy agent

If you are behind a firewall and would like to use a proxy server then you can pass a custom proxy agent to the options object. See the following example:

const { HttpsProxyAgent } = require("hpagent");
const agent = new HttpsProxyAgent({ proxy: "http://x.x.x.x:zzzz" });
const spClient = new SellingPartner({
  region: "eu",
  refresh_token: "<REFRESH_TOKEN>",
  options: {
    https_proxy_agent: agent
  }
});

Exchange an authorization code for a refresh token

If you already have a refresh token you can skip this step. If you only want to use the API for your own seller account you can just use the self authorization to obtain a valid refresh token.

If you want to exchange an authorization code of a seller you can use the .exchange() function of the client. The neccessary authorization code is returned to your callback URI as spapi_oauth_code when a seller authorizes your application (see authorization workflow in docs) or via a call to the getAuthorizationCode operation if you want to authorize a seller for the SP-API who has previously authorized you for the MWS API (the getAuthorizationCode workflow is explained in the Grantless operations section).

Once you have obtained the authorization_code you can exchange it for a refresh token:

const spClient = new SellingPartner({
  region: "eu",
  options: {
    only_grantless_operations: true
  }
});
let res = await spClient.exchange("<SELLER_AUTHORIZATION_CODE>");
console.log(res.refresh_token);

NOTE: You will have to create a new class instance once you have obtained the refresh_token and pass it inside the constructor in order to make calls to the API.

Request access token

If you only provide the region and refresh_token parameters the client will automatically request an access_token for you (with a TTL of 1 hour) and reuse it for future api calls for the class instance.

Instead of having the client handle the access_token requests automatically, you may also refresh them manually:

const spClient = new SellingPartner({
  region: "eu",
  refresh_token: "<REFRESH_TOKEN>",
  options: {
    auto_request_tokens: false
  }
});
await spClient.refreshAccessToken();

If you want to use the same credentials for multiple instances you can retrieve them via getters and use them as input for a new instance:

let access_token = spClient.access_token;

const spClient = new SellingPartner({
  region: "eu",
  refresh_token: "<REFRESH_TOKEN>",
  access_token: access_token
});

Call the API

All calls to the SP-API will be triggered by using the .callAPI() function, which takes an object with the following structure as input:

{
  operation:'<OPERATION_TO_CALL>',
  endpoint:'<ENDPOINT_OF_OPERATION>',
  path:{
    ...
  },
  query:{
    ...
  },
  body:{
    ...
  },
  api_path:'<FULL_PATH_OF_OPERATION>',
  method:'GET',
  headers:{
    ...
  },
  restricted_data_token:'<RESTRICTED_DATA_TOKEN>',
  options:{
    version:'<OPERATION_ENDPOINT_VERSION>',
    restore_rate:'<RESTORE_RATE_IN_SECONDS>',
    raw_result:false,
    timeouts:{
      ...
    }
  }
}

Valid properties of the object:

Name Type Default Description
operation
optional
string - The operation you want to request, see SP API Developer Guide.
May also include endpoint as shorthand dot notation.
Call .endpoints on class instance to retrieve a complete list of all endpoints, versions and operations supported by the client.
Required if api_path is not defined.
endpoint
optional
string - The endpoint of the operation, (see Endpoints).
Call .endpoints on class instance to retrieve a complete list of all endpoints, versions and operations supported by the client.
Required if endpoint is not part of operation as shorthand dot notation and api_path is not defined.
path
optional
object - The input paramaters added to the path of the operation.
query
optional
object - The input paramaters added to the query string of the operation.
body
optional
object - The input paramaters added to the body of the operation.
api_path
optional
string - The full api path of an operation. Can be used to call operations that are not yet supported or have a new version that is not yet supported by the client.
Required if operation is not defined.
method
optional
string - The HTTP method to use.
Required only if api_path is defined.
Must be one of: GET, POST, PUT,DELETE or PATCH.
headers
optional
object - Additional headers that will be added to the call.
restricted_data_token
optional
string - A token received from a createRestrictedDataToken operation. Neccessary to include PII (Personally Identifiable Informaton) for some restricted operations, see Tokens API use case guide for a list of restricted operations.
NOTE: Your developer account must be approved for PII by Amazon in order to be able to receive PII, otherwise the token will have no effect, meaning the result of restricted operations will not include PII.
options
optional
object - Additional options, see table below for all possible options properties.

Valid properties of the config options:

Name Type Default Description
version
optional
string - The endpoint's version that should be used when calling the operation. Will be preferred over an endpoints_versions setting.
NOTE: The call might still use an older version of the endpoint if the operation is not available for the specified version and version_fallback is set to true.
restore_rate
optional
number - The restore rate (in seconds) that should be used when calling the operation. Will be preferred over the default restore rate of the operation.
raw_result
optional
boolean false Whether or not the client should return the "raw" result, which will include the raw body, buffer chunks, statuscode and headers of the result. This will skip the internal formatting or error checking, but might be helpful when you need additional information besides the payload or when the client encounters JSON.parse errors such as the ones already encountered with old finance documents (see Known Issues).
timeouts
optional
object - Allows to set timeouts for requests. Valid keys are response, idle and deadline. Please see detailed information in the Timeouts section.

Examples

To call an operation of an API endpoint you pass in the operation and the endpoint it belongs to. See the following example:

let res = await spClient.callAPI({
  operation: "getMarketplaceParticipations",
  endpoint: "sellers"
});

Instead of using the endpoint property you may also prepend the endpoint to the operation as shorthand dot notation:

let res = await spClient.callAPI({
  operation: "sellers.getMarketplaceParticipations"
});

Here are a few examples that use some more properties:

let res = await spClient.callAPI({
  operation: "getOrderMetrics",
  endpoint: "sales",
  query: {
    marketplaceIds: ["A1PA6795UKMFR9"],
    interval: "2020-10-01T00:00:00-07:00--2020-10-01T20:00:00-07:00",
    granularity: "Hour"
  }
});
let res = await spClient.callAPI({
  operation: "catalogItems.getCatalogItem",
  path: {
    asin: "B084J4QQFT"
  },
  query: {
    marketplaceIds: ["A1PA6795UKMFR9"]
  },
  options: {
    version: "2022-04-01"
  }
});
let res = await spClient.callAPI({
  operation: "createReport",
  endpoint: "reports",
  body: {
    reportType: "GET_FLAT_FILE_OPEN_LISTINGS_DATA",
    marketplaceIds: ["A1PA6795UKMFR9"]
  }
});
let res = await spClient.callAPI({
  operation: "finances.listFinancialEvents",
  query: {
    PostedAfter: "2020-03-01T00:00:00-07:00",
    PostedBefore: "2020-03-02T00:00:00-07:00"
  },
  options: {
    raw_result: true
  }
});
try {
  let res = await spClient.callAPI({
    operation: "getCompetitivePricing",
    endpoint: "productPricing",
    query: {
      Asins: ["B00Z7T970I", "B01BHHE9VK"],
      ItemType: "Asin",
      MarketplaceId: "A1PA6795UKMFR9"
    },
    options: {
      version: "v0",
      raw_result: true,
      timeouts: {
        response: 5000,
        idle: 10000,
        deadline: 30000
      }
    }
  });
} catch (err) {
  if (err.code) {
    if (err.code === "API_RESPONSE_TIMEOUT")
      console.log(
        "SP-API ERROR: response timeout: " + err.timeout + "ms exceeded.",
        err.message
      );
    if (err.code === "API_IDLE_TIMEOUT")
      console.log(
        "SP-API ERROR: idle timeout: " + err.timeout + "ms exceeded.",
        err.message
      );
    if (err.code === "API_DEADLINE_TIMEOUT")
      console.log(
        "SP-API ERROR: deadline timeout: " + err.timeout + "ms exceeded.",
        err.message
      );
  }
}

Endpoints

The exact endpoint's name of an operation will be the references name (see SP API Developer Guide) without API and all spaces removed and continued with a capital letter. So the Catalog Items API endpoint's name will be catalogItems, FBA Small and Light API will be fbaSmallAndLight, Sellers API will be sellers and so on. You can also retrieve the endpoint names and their operations and versions by calling spClient.endpoints.

Versions

Every operation belongs to an endpoint that consists of one or more versions and each version consists of one or more operations. You will find a complete list of the endpoints with all versions and operations in the SP API Developer Guide. For a complete list of all currently by the client supported endpoints with versions and operations you can just call spClient.endpoints.

Version specific operation implementations

The client uses the first (in fact the oldest) endpoint version if no version is provided since new versions of some operations are not backward compatible. So in order to prevent breaking changes we can't enable latest endpoint versions by default. I.e. the two different implementations of the getCatalogItem operation (see catalogItemsV0 vs. catalogItems_2020-12-01) expect different input parameters and return different results.

The implementation of the getCatalogItem operation in the v0 version expects an asin and a MarketplaceId as input:

let res = await spClient.callAPI({
  operation: "getCatalogItem",
  endpoint: "catalogItems",
  query: {
    MarketplaceId: "A1PA6795UKMFR9"
  },
  path: {
    asin: "B084DWG2VQ"
  },
  options: {
    version: "v0"
  }
});

In contrast, the implementation of the getCatalogItem operation in the 2020-12-01 version expects an asin, a marketplaceIds array and an includedData array as input:

let res = await spClient.callAPI({
  operation: "getCatalogItem",
  endpoint: "catalogItems",
  query: {
    marketplaceIds: ["A1PA6795UKMFR9"],
    includedData: [
      "identifiers",
      "images",
      "productTypes",
      "salesRanks",
      "summaries",
      "variations"
    ]
  },
  path: {
    asin: "B084DWG2VQ"
  },
  options: {
    version: "2020-12-01"
  }
});

Trying to call the new 2020-12-01 version without explicitly setting it would result in an InvalidInput error as the required MarketplaceId parameter is missing.

Defining endpoints versions on class level

There are different ways of specifying the version to use for endpoints and their corresponding operations. You can specify the version directly inside the options object of the .callAPI() function as seen in the examples above. But you can also enable a newer version for all operations of an endpoint by using the endpoints_versions setting in the constructor config object. I.e. you can tell the class instance to use the new 2020-12-01 version for the catalogItems endpoint and thus enabling it for all operations of the endpoint throughout the class instance like this:

const spClient = new SellingPartner({
  region: "eu",
  refresh_token: "<REFRESH_TOKEN>",
  endpoints_versions: {
    catalogItems: "2020-12-01"
  }
});

By doing so you can skip setting the version inside the options object each time when you are using .callAPI() with the new version of the getCatalogItem operation.

Fallback

If trying to call an operation that is not part of the endpoint's version you specified, the client will automatically try to find the operation in an earlier endpoint's version and use that implementation if version_fallback is set to true (which is the default). I.e. the listCatalogCategories operation is not part of the new catalogItems endpoint version. So if the new version was set as in the example code above, the following call would still work, because it will automatically fallback to the operation's implementation in version v0:

let res = await spClient.callAPI({
  operation: "listCatalogCategories",
  endpoint: "catalogItems",
  query: {
    MarketplaceId: "A1PA6795UKMFR9",
    ASIN: "B084DWG2VQ"
  }
});

Unsupported endpoints/versions/operations

The newest client version should always have full support for all endpoints, versions and operations on release, however it might lack support for very recently added new endpoints, versions or operations. If you need an endpoint/version/operation that is not yet supported you can still call it by using the api_path parameter. I.e. if the new catalogItems version 2020-12-01 would not be supported yet we could still use the new implementation of the getCatalogItem operation by using the api_path and method properties:

let res = await spClient.callAPI({
  api_path: "/catalog/2020-12-01/items/B084DWG2VQ",
  method: "GET",
  query: {
    marketplaceIds: ["A1PA6795UKMFR9"],
    includedData: [
      "identifiers",
      "images",
      "productTypes",
      "salesRanks",
      "summaries",
      "variations"
    ]
  }
});

NOTE: If your api_path includes special characters that require encoding (i.e. an SKU that contains UTF-8 characters) you will have to encode these characters manually before passing your api_path to .callAPI().

Grantless operations

Some operations don't require an explicit authorization by a seller, see list of grantless operations. A grantless operation needs another access token than other operations and as such a grantless token is NOT the access_token you can provide in the constructor config object. However if the auto_request_tokens option is set to true the client should handle everything for you.

If you do the token request manually you need to create a grantless token by calling refreshAccessToken with the scope of the corresponding endpoint. Currently there are only two different scopes: sellingpartnerapi::migration for authorization endpoint and sellingpartnerapi::notifications for notifications endpoint.

If you don't need or have a refresh token (i.e. because you want to retrieve an SP API authorization code of an already via MWS authorized seller) you may use the client with the only_grantless_operations option set to true which allows you to create an instance of the client without a refresh_token.

To sum up, please see the following example that will request an auth code for an authorized MWS seller account.

First create a class instance that only allows to call grantless operations (no refresh_token included):

const spClient = new SellingPartner({
  region: "eu",
  options: {
    auto_request_tokens: false,
    only_grantless_operations: true
  }
});

Then request a grantless token with the scope needed for the operation you want to call:

await spClient.refreshAccessToken("sellingpartnerapi::migration");

Finally call the grantless operation:

let res = await spClient.callAPI({
  operation: "getAuthorizationCode",
  endpoint: "authorization",
  query: {
    sellingPartnerId: "<YOUR_CUSTOMERS_SELLER_ID>",
    developerId: "<YOUR_DEVELOPER_ID>",
    mwsAuthToken: "<YOUR_CUSTOMERS_MWS_TOKEN>"
  }
});

Restore rates

If you set the auto_request_throttled option in the class constructor config object to true (which is the default), the client will automatically retry the call if its throttled. It will either use the restore rate from the result header field x-amzn-ratelimit-limit if given (see Usage Plans and Rate Limits), or the value of restore_rate option in .callAPI() function if given, or otherwise use the default restore rate of the operation. For testing purposes you can also set debug_log to true, which will trigger a console log every time the client retries a call. If you set auto_request_throttled to false the client will throw a QuotaExceeded error when a request is throttled.

NOTE: If you are using the same operation with the same seller account across multiple class instances the restore rate logic might NOT work correct or, even worse, result in an infinite quota exceeded loop. So if you're planning to do that you should probably set auto_request_throttled to false, catch the QuotaExceeded errors and handle the restore rate logic on your own.

Timeouts

You may set timeouts to stop requests, i.e. to prevent scripts from "hanging" forever because a request is not finishing. The three different timeout types are response, idle and deadline. You may set these inside the class constructor config options to be used for all requests started via .callAPI() or via the config options of the .callAPI() method for that specific call only. The latter will override the timeouts set via class config options.

NOTE: The .download() method will NOT use the timeouts defined in class constructor config options. You have to provide the timeouts to each .download() call inside its options object.

See the table below for valid properties of the timeouts object:

Name Type Default Description
response
optional
number - Timeout (in milliseconds) until a response timeout is fired. If exceeded the request will abort with an API_RESPONSE_TIMEOUT error. Response timeout is the time between sending the request and receiving the first byte of the response. Includes DNS and connection time.
idle
optional
number - Timeout (in milliseconds) until an idle timeout is fired. if exceeded the request will abort with an API_IDLE_TIMEOUT error. Idle is the time between receiving the last chunk of the reponse and waiting for the next chunk to be received. Might be fired if a request is stalled before finished (i.e. when internet connection is lost).
deadline
optional
number - Timeout (in milliseconds) until a deadline timeout is fired. If exceeded the request will abort with an API_DEADLINE_TIMEOUT error. Deadline is the time from the start of the request to receiving the response body in full. If the deadline is too short large responses may not load at all on slow connections.

Download reports

The easiest way of downloading a report is to use the .downloadReport() function that will wrap up all operations needed to request and retrieve a report in a single call. The function internally calls the operations createReport, getReport, getReportDocument and the .download() function that will download the final report document in sequence.

The function takes a config object with the following parameters as input:

Name Type Default Description
body
required
object - Includes the parameters necessary to request the report. These are the parameters usually passed in to the createReport operation (see createReport 2021-06-30). The possible values will be described below.
version
optional
string 2021-06-30 The report endpointโ€™s version that should be used when retrieving the report.
interval
optional
number 10000 The request interval (in milliseconds) that should be used for re-requesting the getReport operation when the report is still queued or in progress.
cancel_after
optional
number - Cancels a report request after the specified number of retries. Each re-request defined by the interval value counts as one retry.
download
optional
object - Includes optional parameters for the download of the report, i.e. to enable a json result or to additionally save the report to a file. The possible values will be described below.

The body object may include the following properties:

Name Type Default Description
reportType
required
string - The report type.
marketplaceIds
required
< string > array - A list of marketplace identifiers. The report document's contents will contain data for all of the specified marketplaces, unless the report type indicates otherwise.
dataStartTime
optional
string - The start of a date and time range, in ISO 8601 date time format, used for selecting the data to report. The default is now. The value must be prior to or equal to the current date and time. Not all report types make use of this.
dataEndTime
optional
string - The end of a date and time range, in ISO 8601 date time format, used for selecting the data to report. The default is now. The value must be prior to or equal to the current date and time. Not all report types make use of this.
reportOptions
optional
object - Additional information passed to reports. This varies by report type.

The download object may include the following properties:

Name Type Default Description
json
optional
boolean false Whether or not the content should be transformed to json before returning it (from tab delimited flat-file or XML).
unzip
optional
boolean true Whether or not the content should be unzipped before returning it.
file
optional
string - The absolute file path to save the report to.
NOTE: Even when saved to disk the report content is still returned.
charset
optional
string utf8 The charset to use for decoding the content. If not defined, it uses per default the charset returned in content-type header or utf8 if no charset found in content-type header.
NOTE: Is ignored when content is compressed and unzip is set to false.
timeouts
optional
object - Allows to set timeouts for download requests. Valid keys are response, idle and deadline. Please see detailed information in the Timeouts section.

Please see the following example that will request a GET_FLAT_FILE_OPEN_LISTINGS_DATA report for the current report endpoint version 2021-06-30, re-request it every 8 seconds and, once its finished, will download the report, transform it to json and save it to disk:

let res = await sellingPartner.downloadReport({
  body: {
    reportType: "GET_FLAT_FILE_OPEN_LISTINGS_DATA",
    marketplaceIds: ["A1PA6795UKMFR9"]
  },
  version: "2021-06-30",
  interval: 8000,
  download: {
    json: true,
    file: "<ABSOLUTE_FILE_PATH>/report.json"
  }
});

Instead of using the .downloadReport() function you may as well call the necessary operations on your own and use the .download() function to retrieve the final report data. Please see the following information below:

The .download() function takes the download details received from a getReportDocument operation as input, downloads the content, unzips it (if result is compressed), decrypts it and returns it.

Retrieve the download details from a getReportDocument operation:

let report_document = await spClient.callAPI({
  operation: "getReportDocument",
  endpoint: "reports",
  path: {
    reportDocumentId: "<REPORT_DOCUMENT_ID>" // retrieve the reportDocumentId from a "getReport" operation (when processingStatus of report is "DONE")
  }
});

The structure of the returned report_document should look like this:

{
  reportDocumentId:'<REPORT_DOCUMENT_ID>',
  compressionAlgorithm:'GZIP', // Only included if report is compressed
  url: '<REPORT_DOWNLOAD_URL>' // Expires after 5 minutes!
}

Call the .download() function to receive the content of the report. The default without any config options will download, decrypt and unzip the content and return it without reformatting or saving it to the disk:

let report = await spClient.download(report_document);

You may also include an options object as a 2nd parameter to the .download() function, i.e. to enable a json result or to additionally save the report to a file. The possible parameters are the same as for the download object for the .downloadReport() function already documented above.

The following call will download the report, transform it to json and save it to disk:

let report = await spClient.download(report_document, {
  json: true,
  file: "<ABSOLUTE_FILE_PATH>/report.json"
});

Some reports may have an encoding other than UTF-8 and require special decoding with a different charset, i.e. the GET_MERCHANT_LISTINGS_ALL_DATA report is encoded as cp1252 for eu region marketplaces. The right charset to use for decoding is taken from the return header content-type, but you may force the use of a specific charset for decoding by passing in the optional charset property:

let report = await spClient.download(report_document, {
  charset: "cp1252"
});

Upload feeds

The .upload() function takes the feed upload details received from a createFeedDocument operation, the feed content and its content type to upload as input and uploads it.

Start by creating a feed object with a contentType and the content either as a string or a file path to a document:

Name Type Default Description
content
optional
string - The content to upload as a string.
Required if file is not provided.
file
optional
string - The absolute file path to the feed content document to upload.
Required if content is not provided.
contentType
required
string - The contentType of the content to upload.
Should be one of text/xml or text/tab-separated-values and the charset of the content, i.e. text/xml; charset=utf-8.

This will create an inventory feed (POST_INVENTORY_AVAILABILITY_DATA) that will update the quantity of a given SKU to 10:

let feed = {
  content: `<?xml version="1.0" encoding="utf-8"?>
    <AmazonEnvelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="amzn-envelope.xsd">
      <Header>
        <DocumentVersion>1.02</DocumentVersion>
        <MerchantIdentifier>YOUR_MERCHANT_IDENTIFIER</MerchantIdentifier>
      </Header>
      <MessageType>Inventory</MessageType>
      <Message>
        <MessageID>1</MessageID>
        <Inventory>
          <SKU>YOUR_SKU</SKU>
          <Quantity>10</Quantity>
        </Inventory>
      </Message>
    </AmazonEnvelope>`,
  contentType: "text/xml; charset=utf-8"
};

Before you can upload the feed you need to retrieve the feed upload details from a createFeedDocument operation:

let feed_upload_details = await spClient.callAPI({
  operation: "createFeedDocument",
  endpoint: "feeds",
  body: {
    contentType: feed.contentType
  }
});

Call the .upload() function to upload the content of the feed:

let res = await spClient.upload(feed_upload_details, feed);

After uploading the feed you have to trigger the processing of the feed by calling the createFeed operation with the necessary params (marketplaceIds, feedType and inputFeedDocumentId):

let feed_creation_infos = await spClient.callAPI({
  operation: "createFeed",
  endpoint: "feeds",
  body: {
    marketplaceIds: ["A1PA6795UKMFR9"],
    feedType: "POST_INVENTORY_AVAILABILITY_DATA",
    inputFeedDocumentId: feed_upload_details.feedDocumentId // retrieve the feedDocumentId from the "createFeedDocument" operation
  }
});

NOTE: Although uploading and creating the feed was successful it doesn't mean that the processing of the feed itself was also successful. You can check the result of the feed once it has been processed by downloading the processing result with the .download() function quite similar as how to download reports. Use the feedId returned by the createFeed operation and call the getFeed operation, which will include a resultFeedDocumentId if feed processing is already done. The resultFeedDocumentId can be used with a getFeedDocument operation that will return the feed download details needed for the feed result download.

TypeScript Support

All TypeScript related information can be found in lib/typings. Currently types are not yet defined for all operations and/or params, so feel free to add new types following the readme. You are also welcome to create a pull request.

Sandbox mode

You can easily enable sandbox mode by setting use_sandbox in the constructor config options to true. General information on sandbox setup and behaviour can be found in the corresponding section in the Selling Partner API Developer Guide.

When using the sandbox you have to make sure to use the correct request parameters for the operation you want to test. You can find these inside the api models definitions in the docs by searching the corresponding json file for x-amzn-api-sandbox.

For example, this will test the getPricing operation in sandbox mode:

let res = await spClient.callAPI({
  operation: "getPricing",
  endpoint: "productPricing",
  query: {
    MarketplaceId: "TEST_CASE_400"
  }
});

Known Issues

There is an issue with values of arrays as part of the query, when a value contains a ,. Due to Amazon expecting array values in query separated by , it will wrongfully split up values containing a , into two separate values. This is already a known issue communicated to Amazon.

Seller Support

We might be able to support you with everything else that can't be done with the API, i.e. a detailed sales dashboard, review management, automated reimbursements and more. Feel free to visit us at https://getarthy.com.

Breaking Changes

  • Removed refreshRoleCredentials function and the getter for role_credentials. As Amazon has removed the neccessity for signing requests to the SP API, role credentials are not needed anymore.

  • Trying to call an operation without specifying an endpoint now results in a NO_ENDPOINT_GIVEN error. Although deprecated since version 0.4.0 it was still possible to call an operation without an endpoint. This possibility has now been removed. However, it is still perfectly fine to omit the endpoint parameter and add it directly to the operation parameter via shorthand dot notation (i.e. operation: "sellers.getMarketplaceParticipations")

  • The reports and feeds endpoint's version 2020-09-04 is deprecated since 27th June 2023 and has been removed. As a result, encryptionDetails for downloading of reports and uploading of feeds is not returned anymore and all en-/decryption logic for reports and feeds has been removed as well.

amazon-sp-api's People

Contributors

alexotano avatar altruer avatar amz-tools avatar cohlar avatar ctiospl avatar dlamb22 avatar erayalakese avatar fmalekpour avatar goldbird-solutions avatar harelbaruchi avatar idom818 avatar igrybkov avatar jakeleventhal avatar jebirch avatar jesusvalle avatar juliensnz avatar livingforjesus avatar mgecmez avatar mihai9-lab avatar nifrigel avatar nohara-embark avatar omrishaked avatar roman991 avatar taimoor0217 avatar tho-masn avatar valentinmouret avatar vhle-ms avatar wangjue666 avatar yuriiherasymchuk avatar zirkelc avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

amazon-sp-api's Issues

TypeScript Types

Is there any plan to add TypeScript types across the package?

My team uses TypeScript and are very interested in using this package going forward. We would love to see types here to make the package easier to work with. If there are not plans to add types, would you be open to us making a PR to add some in the future?

Calling the OrderAPI -> InvalidInput

Hello together,

we are trying to call the Order-API with e.g. getOrderBuyerinfo, getOrderArddress or getOrder... but it will always result in the same error: "code": InvalidInput".

We are using the "eu" region.
Calls to the catalogAPI are working fine.

Is there a known issue or any idea whats could be the problem here?
If u need futher informations let me know.

Thank you very much.

CustomError: Client authentication failed - on every operation

CustomError: Client authentication failed at SellingPartner.refreshAccessToken (E:\Web Development Repos\sp-api\node_modules\amazon-sp-api\lib\SellingPartner.js:378:13) at processTicksAndRejections (internal/process/task_queues.js:93:5) at async SellingPartner._validateAccessTokenAndRoleCredentials (E:\Web Development Repos\sp-api\node_modules\amazon-sp-api\lib\SellingPartner.js:242:9) at async SellingPartner.callAPI (E:\Web Development Repos\sp-api\node_modules\amazon-sp-api\lib\SellingPartner.js:453:5) at async E:\Web Development Repos\sp-api\index.js:12:17 { code: 'invalid_client', type: 'error' }

We're receiving this regardless of operation called. I'm guessing this means that there's an issue with one of our environmental variables, but I've double checked everything.

Just to clarify:
SELLING_PARTNER_APP_CLIENT_ID= this is the LWA client id?
SELLING_PARTNER_APP_CLIENT_SECRET= this is the LWA client secret?
AWS_ACCESS_KEY_ID= The access key ID from the IAM user?
AWS_SECRET_ACCESS_KEY= the secret access key from the IAM user?
AWS_SELLING_PARTNER_ROLE= This is the full ARN for the IAM role?

Feel free to open up a PR!

As the development of the Sellingpartner API is largely in progress by Amazon, there are quite a lot of changes and updates coming. We try to keep up with all new features/changes coming from Amazon, but if you find some things not working as expected or you have an idea of a feature that would be helpful and a good addition to the client you are very much welcome to open up a PR. We will try to review and include the PR inside the module as soon as possible, usually in a couple of days.

Some possible features for a PR could be i.e.:

  • Full/better typesript support (see Issue #25)
  • Re-Request security_token and/or access_token when throwing an invalid/malformed/revoked error (see Issue #57)
  • "All in one" request and download report wrapper (createReport, getReport, getReportDocument, download all wrapped up in a convenient single call)
  • Implement a "smart queue" on the basis of ratelimit and burst when requesting the same endpoint excessively (see Issue #46)
  • More tests

I can't get CustomerInfo from API Order, please help me.

const getOrderItemBuyer = async (orderId) => {
(async () => {
try {
let res = await sellingPartner.callAPI({
api_path: /orders/v0/orders/${orderId}/orderItems/buyerInfo,
method: "GET",
query: {
MarketplaceId: "ATVPDKIKX0DER",
},
restricted_data_token:"my token from create RDT"
});
console.log(JSON.stringify(res));
} catch (e) {
console.log(e);
}
})();
};

My response:
CustomError: Access to requested resource is denied.

If Remove params restricted_data_token, API working, why ?
Please help me

Not getting data from listFinancialEvents prior to 2019-04-01

My teams has been experiencing issues with retrieving any data from the listFinancialEvents operation prior to 2019-04-01. We're just getting arrays. Our account has financial data all the way to 2016.

The 2019-04-01 is about 2 years from today. Does the listFinancialEvents have a new limit now of 2 years? or is this just a coincidence?

We also tried utilized listFinancialEventsByOrderId with an orderId that dated back to 2016 and it also returned a response full of empty arrays.

Anyone else having this issue?

Screenshot from 2021-04-13 15-50-22

Screenshot from 2021-04-13 15-50-45

Screenshot from 2021-04-13 16-44-29

No upload() function declared in types?

Hi, I have seen that in the documentation there are several references to the upload() function for uploading feeds, but it is not declared in the typescript, is this correct?

productFees.getMyFeesEstimateForASIN InvalidParameterValue

Hi @amz-tools,

I'm trying to get product fees.
I haven't any problem with other countries (FR, IT, ES, DE) but GB does not work, I'm getting this response:

{
  "FeesEstimateResult": {
    "Status": "ClientError",
    "FeesEstimateIdentifier": {
      "MarketplaceId": "A1F83G8C2ARO7P",
      "IdType": "ASIN",
      "SellerId": "XXXXX",
      "SellerInputIdentifier": "56f1174c-b87b-4247-8db5-3ca3798faa1d",
      "IsAmazonFulfilled": true,
      "IdValue": "B00SLIMMHW",
      "PriceToEstimateFees": {
        "ListingPrice": {
          "CurrencyCode": "EUR",
          "Amount": 20
        },
        "Shipping": {
          "CurrencyCode": "EUR",
          "Amount": 0
        }
      }
    },
    "Error": {
      "Type": "Sender",
      "Code": "InvalidParameterValue",
      "Message": "There is an client-side error. Please verify your inputs.",
      "Detail": []
    }
  }
}

The error message is not clear. All parameters (except MarketplaceId) are the same with other countries but do not work.
Do you have any idea?

Thanks !!

Getting attributes from getCatalogItem

Thanks for creating amazon-sp-api. I'm just getting started trying to use the sp-api and it has been super helpful.

I'm running into some issues getting product attributes from the catalogItem endpoint and was wondering if anyone could help point me in the right direction. Right now my API call looks like this:

await sellingPartner.callAPI({
        operation: "getCatalogItem",
        endpoint: "catalogItems",
        query: {
          marketplaceIds: ["ATVPDKIKX0DER"],
          includedData: ["attributes"],
        },
        path: {
          asin:  <my_asin>,
        },
        options: {
          version: "2020-12-01",
        },
      });

If I make the request on asins of certain brands we sell I get this error:

CustomError: FORBIDDEN
 ... 
 ... 
 ...
 ...
  details: 'The includedData requested requires Brand Registry affiliation with the requested item. Remove the requested includedData item or register the brand of the item in Brand Registry with the seller.',
  type: 'error'
}

Is there a way to set the sellerId? I think the issue is because we are identified as the owner of multiple brands, and it's not assuming the correct sellerId.

How to get the request IDs?

Hello there!
We've been using the library for a few months and it has worked nicely for us. Right now we're facing some issues with Amazon's API that we can't find a solution for and we'd like to ask them what's going on. However, in order to do this, we need to send them the request IDs of the specific requests that we see go wrong, but we don't see them anywhere when making the requests through this library. Is there a way we can get the request IDs for our calls?

Thanks in advance.

download report returns empty array when json: true with some reports.

I am attempting to download a "GET_MERCHANT_LISTINGS_DATA_LITE" report, using SellingPartner.download(). The download succeeds and returns a tab delimited filed when passing no options object, or when {json: false}. When passing {json: true}, the function returns only '[]'. This does not happen for all reports, for example "GET_FLAT_FILE_OPEN_LISTINGS_DATA" reports download perfectly.

Sandbox

Hello! Is there a way to add sandbox mode?, I've been working on this library for an while but I can't make it work.

I was adding a boolean sandbox variable in SellingPartner.js, which is passed to Signer.js (only for signing requests not request of credentials) and finally to the request.js , all it does is add "sandbox." string at the begining of the URL, asi in the documentation.

But I'm getting

CustomError: We encountered an internal error. Please try again.
    at SellingPartner.callAPI (/var/www/nodes/oadmin/node_modules/amazon-sp-api/lib/SellingPartner.js:234:13)
    at processTicksAndRejections (internal/process/task_queues.js:97:5)
    at async Object.exports.getOrdersAmazon (/var/www/nodes/oadmin/dist/router-mongo/amazon/amazon.helper.js:152:15)
    at async /var/www/nodes/oadmin/dist/router-mongo/amazon/amazon.helper.js:284:28 {
  code: 'InternalFailure',
  type: 'error'
}

Any hints? I was planning to finish this feature and make a PR, seams like a very simple feature but so helpful.

How to call grantless operation?

I keep getting Access to requested resource is denied when call getDestinations operation.

I'm sure i have access because orderMatrix and getMarketplaceParticipations works as expected.

(async () => {
  const sp = new SellingPartnerAPI({
    region: 'na',
    refresh_token: 'token',
  });

  await sp.refreshAccessToken();
  await sp.refreshRoleCredentials();

  const res = await sp.callAPI({ operation: 'getDestinations' });

  console.log(JSON.stringify(res));
})();


// Error: Access to requested resource is denied.

Operation 'getMyFeesEstimateForASIN' has wrong URI

Hi,

thanks for your great work. It helped a lot.

I think, for operation 'getMyFeesEstimateForASIN' there is a wrong URI.
In lib/resources/productFees.js, you wrote

api_path:'/products/fees/v0/listings/' + req_params.path.Asin + '/feesEstimate',

But according to documentation, api_path should be:

api_path: '/products/fees/v0/items/' + req_params.path.Asin + '/feesEstimate'

Would be great to fix that, thanks.

JSON_PARSE_ERROR from listFinancialEvents

Hey @amz-tools, we've been experiencing one of the known issues that is mentioned on your repo/library.

We've utilized the raw response option but we're pretty lost on how to actually handle the parsing properly.

Any idea what the best way to handle these type of errors?

Keep getting Unauthorized using sample requests

Keep getting Unauthorized error using sample requests in docs. Should these examples work?

I'm trying to work out if I'm not authenticating properly or whether the API endpoint is giving the error as I'm trying to request data that I don't have access to (as it's your data)

Using a brand new marketplace seller account / no products / data.

CustomError: Access to requested resource is denied.
    at SellingPartner.callAPI (/Users/steven/Documents/boilerplate/versiontwo/node_modules/amazon-sp-api/lib/SellingPartner.js:203:13)
    at processTicksAndRejections (node:internal/process/task_queues:93:5)
    at /Users/steven/Documents/boilerplate/versiontwo/packages/amazon/lib/SP-api.ts:19:19 {
  code: 'Unauthorized',
  details: '',
  type: 'error'
}
require("dotenv").config();
require("source-map-support").install();

import SellingPartnerAPI from "amazon-sp-api";

(async () => {
  try {
    const sellingPartner = new SellingPartnerAPI({
      region: "eu",
      refresh_token:
        "XXX",
      options: {
        auto_request_tokens: true,
        auto_request_throttled: true,
      },
    });

    let res = await sellingPartner.callAPI({
      operation: "createReport",
      body: {
        reportType: "GET_FLAT_FILE_OPEN_LISTINGS_DATA",
        marketplaceIds: ["A1PA6795UKMFR9"],
      },
    });

    console.log(res);
  } catch (e) {
    console.log(e);
  }
})();

"CustomError: Access to requested resource is forbidden" after a certain number of calls

We're building an app that loops a bunch of different ASINS and makes different API calls on each one. Everything works fine if it's just running up to 300 ASINS. However, if we try to run 800+ it starts giving this error:

CustomError: Access to requested resource is forbidden

Do I need to be doing rate limiting of some sort? Or is there another possible issue here?

Stream mode in reports download

Hello,

There are some sellers that in their reports (for example, the merchant listings report), have more than 1500000 products, which implies that they exceed the node memory.

In the previous library we used with MWS there was an option to download the reports in "stream" mode, to go through the content and incorporate it progressively. Is there any way to do it with this package?

Thank you very much!

Custom Error: Access to requested resource is denied.

I am passing all the credentials as below and calling the SP-API for getinventory summary API.SELLING_PARTNER_APP_CLIENT_ID=<YOUR_APP_CLIENT_ID>
SELLING_PARTNER_APP_CLIENT_SECRET=<YOUR_APP_CLIENT_SECRET>
AWS_ACCESS_KEY_ID=<YOUR_AWS_USER_ID>
AWS_SECRET_ACCESS_KEY=<YOUR_AWS_USER_SECRET>
AWS_SELLING_PARTNER_ROLE=<YOUR_AWS_SELLING_PARTNER_API_ROLE>

But I am getting the below error.
Custom Error: Access to requested resource is denied.

Should I use the access_key and secret that is tied to my IAM user or should I get the keys from assumeRole command (in CLI) and use that in the nodejs ?

(I do not see any issue with the client_id or refresh_token credentiaks since I am able to get the access_token when trying the API (/auth/o2/token?grant_type=refresh_token).

I am kind of stuck here. Any help please.

How to use it with typescript?

I saw it has type declaration files in lib/typings, but I'm still getting Cannot find module 'amazon-sp-api' or its corresponding type declarations.ts(2307).

    "amazon-sp-api": "^0.3.5",

Error uploading feed with document: SignatureDoesNotMatch

Hi!

When i try to use the upload method after the use of the "createFeedDocument" i have a problem signing the request, i',m using the information recovered from the previous call, and this for the feed:

let feed = {    
    content:"<?xml version=\"1.0\" ?></xml>",
    contentType:'text/xml; charset=utf-8'
}

This throws the following error:

CustomError: The request signature we calculated does not match the signature you provided. Check your key and signing method.

Somebody can help me with this? i don't know why doesn't work,

Below i paste the code used for testing the functionality:

(async() => {
        try {   
        let sellingPartner = await new SellingPartnerAPI({        
            region:'eu', // The region of the selling partner API endpoint ("eu", "na" or "fe")        
            refresh_token:process.env.REFRESH_TOKEN // The refresh token of your app user      
        });  
        console.log(sellingPartner._credentials)      
        let feed_upload_details = await sellingPartner.callAPI({    
            operation:'createFeedDocument',    
            body:{      
                contentType:'text/xml; charset=UTF-8'    
            }  
        });

        console.log(feed_upload_details);
        let res = await sellingPartner.upload(feed_upload_details, feed);

        console.log(feed_upload_details);  
        console.log(feed_creation_infos); 
         
    } catch(e){      
        console.log(e);    
    }  

URL is not a constructor

Hiya!

(Vue/NuxtJS)
I'm able to get an output by console logging the response, but it comes with this error:
image

I'm currently using the amazon-sp-api through a js file that's imported to the Vue instance through Nuxt plugins.
I'm not sure if it's because of that way im using it, and maybe Vue is doing something to it, or if i've made a silly mistake in where i put it (but i've already had some issues like with the env vars not being available before the plugin is run).

CustomError: Access to requested resource is denied.

Hi,

Any idea with this error:

CustomError: Access to requested resource is denied.
    at SellingPartner.callAPI (/Users/k/node_modules/amazon-sp-api/lib/SellingPartner.js:503:13)
    at processTicksAndRejections (node:internal/process/task_queues:94:5)
    at async /Users/k/Y/Lister/amz-sp-api-node.js:17:15 {
  code: 'Unauthorized',
  details: '',
  type: 'error'
}

Here are the codes we use:

const SellingPartnerAPI = require("amazon-sp-api"); 
  
(async () => {
  try {
    console.log(process.env);
    let sellingPartner = new SellingPartnerAPI({
      region: "na",
      refresh_token: process.env.AMZ_SP_API_REFRESH_TOKEN,
      credentials: {
        SELLING_PARTNER_APP_CLIENT_ID: process.env.SELLING_PARTNER_APP_CLIENT_ID,
        SELLING_PARTNER_APP_CLIENT_SECRET: process.env.SELLING_PARTNER_APP_CLIENT_SECRET,
        AWS_ACCESS_KEY_ID: process.env.AWS_ACCESS_KEY_ID,
        AWS_SECRET_ACCESS_KEY: process.env.AWS_SECRET_ACCESS_KEY,
        AWS_SELLING_PARTNER_ROLE: process.env.AWS_SELLING_PARTNER_ROLE
      },
    });
    let res = await sellingPartner.callAPI({
      operation:'getMarketplaceParticipations'
    });
    console.log(res);
  } catch (e) {
    console.log(e);
  }
})();

We tried other operations too, but all of them return the same error. We can access the MWS API just fine. But, we don't have active Amazon Seller Account (is this an issue?)

Amazon listing date parsing

The question is not related to library, I need a help so thought to ask it here.
it's regarding parsing date of listing publication date, so the date is in format "23/09/2021 10:34:38 MEST" i want to convert it to UTC since different marketplace can have different timezone it's hard to parse it in JS with even libraries without having proper timezone name. Thanks for your comment in advance.

Update Order-API

Hello,

first off all, big thanks to this well made script. We every much appreciate it!

Just a short question:
Must the script be updated due the current change of the Order API section? According to Amazon, you can now get BuyerInfo and Address via the "GetOrder(s)" call, if you make the call with a RDT token.

Thank you very much and kind regards

Can only call some endpoints

I've set up a client:

const sellingPartner = new SellingPartnerAPI({
      region: 'eu',
      refresh_token:
        '<Censored>',
      credentials: {
        SELLING_PARTNER_APP_CLIENT_ID: '<censored>',
        SELLING_PARTNER_APP_CLIENT_SECRET: '<censored>',
        AWS_ACCESS_KEY_ID: '<censored>',
        AWS_SECRET_ACCESS_KEY: '<censored>',
        AWS_SELLING_PARTNER_ROLE: '<censored>',
      },
    });

And, this works well for getMarketplaceParticipation:

const res = await sellingPartner.callAPI({
      operation: 'getMarketplaceParticipations',
      endpoint: 'sellers',
    });
    console.log(res);

Gives nice output:

{
marketplace: {
id: 'AZMDEXL2RVFNN',
countryCode: 'GB',
name: 'SI UK Prod Marketplace',
defaultCurrencyCode: 'GBP',
defaultLanguageCode: 'en_GB',
domainName: 'siprodukmarketplace.stores.amazon.co.uk'
},
participation: { isParticipating: true, hasSuspendedListings: false }
}

But, if I try to call getOrderMetrics:

const resSales = await sellingPartner.callAPI({
      operation: 'getOrderMetrics',
      endpoint: 'sales',
      query: {
        marketplaceIds: ['A38D8NSA03LJTC'],
        interval: '2020-10-01T00:00:00-07:00--2020-10-01T20:00:00-07:00',
        granularity: 'Hour',
      },
    });
    console.log(resSales);

I get the equivalent of "computer says no":

CustomError: Access to requested resource is denied.
at SellingPartner.callAPI (/node_modules/amazon-sp-api/lib/SellingPartner.js:592:13)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at async main () {
code: 'Unauthorized',
details: '',
type: 'error'
}

Clearly my role, etc., must be setup right, or the first call would not work, I would assume? Any idea why this second call to getOrderMetrics fails?

Problems with getOrderMetrics passing an array to marketplaceIds

Hello,
I'm trying to testing get ordermetrics, everything's gone well till i use an array of strings for marketplaceIds parametrer.

This is the req_params:
{ operation: 'getOrderMetrics', query: { granularity: 'Total', interval: '2021-03-31T00:00:00-07:00--2021-03-31T23:59:59-07:00', marketplaceIds: [ 'A13V1IB3VIYZZH', 'A1F83G8C2ARO7P', 'A1PA6795UKMFR9', 'A1RKKUPIHCS9HS', 'APJ6JRA9NG5V4' ] }, method: 'GET', api_path: '/sales/v1/orderMetrics', restore_rate: 2 }

and this is the error:
CustomError: Invalid Input at SellingPartner.callAPI (/var/www/djs-app/node_modules/amazon-sp-api/lib/SellingPartner.js:368:13) at processTicksAndRejections (internal/process/task_queues.js:93:5) { code: 'InvalidInput', details: '', type: 'error' }
I don't understand why it works with marketplaceIds: ['APJ6JRA9NG5V4'] and not with marketplaceIds: ['A13V1IB3VIYZZH','A1F83G8C2ARO7P','A1PA6795UKMFR9','A1RKKUPIHCS9HS','APJ6JRA9NG5V4']

Any ideas?
thank you so much

Daniele

Invalid security_token in role credentials

Hello, I'm getting The security token included in the request is invalid error and when I log the securit token I get this : security_token: 15. What's the problem?
?

CustomError: Access to requested resource is denied.

I get the following error when calling the getOrders operation:

2020-12-26T17:03:52.835Z 5531d3c3-8473-4da8-a4b2-034f137044c5 INFO CustomError: Access to requested resource is denied.
at SellingPartner.callAPI (/var/task/node_modules/amazon-sp-api/lib/SellingPartner.js:229:13)
at processTicksAndRejections (internal/process/task_queues.js:97:5)
at async Runtime.exports.handler (/var/task/index.js:22:15) {
code: 'Unauthorized',
details: '',
type: 'error'
}

When running the same code for my own store: I get null object back for getOrders - meaning its all working end to end.

When I try to get the getOrder for a clients store, where we got the spapi_auth correctly, and generated the refresh_token successfully, we hit the error. Unfortunately I only have one clients store to try it out.

BTW, what is also interesting is that, I did get the result early in the morning with my clients store. I did not change anything, and hence I am a little lost as to what could be throwing this error.

Any way to view all (or at least more) offers by ASIN?

When I use the Item Offers API (operation getItemOffers), it will give a handful of LowestPrices and BuyboxPrices, but only show maybe 5 total offers. In the NumberOfOffers array however, it says there should be 50+ offers.

Is there a different API to view more of the offers? Or any query parameters to utilize?

Error: Invalid Input - for parameter values with spaces.

Can be fixed by calling the encodeURIComponent method for parameter values. (In that case for SKUs)

API Endpoint productPricing
Operation getPricing
Query

{ query: { Skus: [<Some SKU With slash/44>], ItemType: 'Sku', MarketplaceId: <Some Marketplace Id> }

version: 'v0'

Error:
Error: Invalid Input
at SellingPartner.callAPI (/Users/Documents/projects/pricing-platform/amazon-integration-plugin/node_modules/amazon-sp-api/lib/SellingPartner.js:574:13)

Upload does not work for string feed.content (TypeError [ERR_INVALID_ARG_TYPE]: The "list" argument must be an instance of Array. Received type string ('<?xml version="1.0" enco...))

Following the instruction https://www.npmjs.com/package/amazon-sp-api#encrypt-and-upload-feeds
There is an option to upload feed data as a string.
For example:

let feed = {
  content:`<?xml version="1.0" encoding="utf-8"?>
    <AmazonEnvelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="amzn-envelope.xsd">
      <Header>
        <DocumentVersion>1.02</DocumentVersion>
        <MerchantIdentifier>YOUR_MERCHANT_IDENTIFIER</MerchantIdentifier>
      </Header>
      <MessageType>Inventory</MessageType>
      <Message>
        <MessageID>1</MessageID>
        <Inventory>
          <SKU>YOUR_SKU</SKU>
          <Quantity>10</Quantity>
        </Inventory>
      </Message>
    </AmazonEnvelope>`,
  contentType:'text/xml; charset=utf-8'
}
const feedUploadDetails = {
      feedDocumentId: '3d4e42b5-1d6e-44e8-a89c-2abfca0625bb',
      url: 'https://d34o8swod1owfl.cloudfront.net/Feed_101__POST_PRODUCT_DATA_.xml'
}
let res = await sellingPartner.upload(feed_upload_details, feed);

The error is thrown in the case

TypeError [ERR_INVALID_ARG_TYPE]: The "list" argument must be an instance of Array. Received type string ('<?xml version="1.0" enco...)

Looks like on the line (https://github.com/amz-tools/amazon-sp-api/blob/main/lib/SellingPartner.js#L690) we need to have
something like:
content_buffer = Buffer.concat([Buffer.from(feed_content)]);
instead of
content_buffer = Buffer.concat(feed_content);

FBM

Hallo...
I sell my product by email. so how can I inform amazon(FBM) by API ? my product is delivered.

'The request signature we calculated does not match the signature you provided'. eu-west-1

Hi! I'm trying to make a createFeedDocument operation but the signature doesn't match.
I have been looking at some closed issues (from this library and also from the SP-API), and the error is similar from This closed issue, nevertheless, both content types of my request are the same.
code

let feed = {
      content:"<?xml version="1.0" encoding="utf-8"?>"
      contentType:"text/xml; charset=utf-8"
    };

    let feed_upload_details = await sellingPartner.callAPI({
      operation:"createFeedDocument",
      body:{
        contentType:feed.contentType
        //I have tried typing directly "text/xml; charset=utf-8"
      }
    });
    console.log(feed_upload_details);  
    let report = await sellingPartner.download(feed_upload_details);

log of feed_upload_details
{ encryptionDetails:
{ standard: 'AES',
initializationVector: 'rXLA88xa2w08qyciDOsAkw==',
key: 'BJchi...' },
feedDocumentId:
'amzn1.tortuga.3.2565a8da-1d07-49ad-80cf-da834d5d3ed2.T1Y139K3PX9PFI',
url:
'https://tortuga-prod-eu.s3-eu-west-1.amazonaws.com/%2FNinetyDays/amzn1.tortuga.3.2565a8da...' }
error of report(download operation)
{ Error: The request signature we calculated does not match the signature you provided. Check your key and signing method.
at SellingPartner._validateUpOrDownloadSuccess (/)
at SellingPartner.download (/)
at process._tickCallback (internal/process/next_tick.js:68:7)
code: 'SignatureDoesNotMatch',
message:
'The request signature we calculated does not match the signature you provided. Check your key and signing method.',
type: 'error' }

The URL goes to a most extensive error report (SignatureDoesNotMatch), where the most useful information is that the string to sign has this format:
AWS4-HMAC-SHA256 20210422T061142Z 20210422/eu-west-1/s3/aws4_request cd3fcaa53c9938740839d92bbdaa12275e2c4a5e481d744ff9aa74129f098291

And the Signature provided:
af97666f7c47a9198e02ebff79c042758d4d854667f8dc91c5d5123a45a00736

Another useful information from the URL:

<Canonical Request>
GET //NinetyDays/amzn1.tortuga...
...
content-type: host:tortuga-prod-eu.s3-eu-west-1.amazonaws.com content-type;host UNSIGNED-PAYLOAD
</Canonical Request>

Thank you very much!

Tests

Are there any plans to add tests to the package?

My team is a big proponent of automated testing and noticed this package does not yet have any. We're interesting in using the package going forward and think it would be beneficial to have tests here to verify the functionality as new features are added and changed. If there are not plans for this, would you be open to us making a PR down the line to add tests?

CustomError: Access to requested resource is denied.

I'm trying to migrate our application from MWS to SP-API hybrid.
We are using "fe" region and Japan marketplace id "A1VC38T7YXB528"
I saw issue #34 and I check the setting on seller central, I changed existing application to a hybrid type and assign IAM to get LWA client ID and Secret.

I use this endpoint

let res = await sellingPartner.callAPI({ operation:'getMarketplaceParticipations', endpoint:'sellers' }); console.log(res);

This is what I get from response.

CustomError: Access to requested resource is denied.
at SellingPartner.callAPI (C:\Users\James Lin\Workspace\amz-sp-api-demo\node_modules\amazon-sp-api\lib\SellingPartner.js:572:13)
at processTicksAndRejections (internal/process/task_queues.js:93:5)
at async C:\Users\James Lin\Workspace\amz-sp-api-demo\index.js:19:19 {
code: 'Unauthorized',
details: '',
type: 'error'
}

I check my Access Key on AWS, it shows Last Usage is "2021-06-11 10:30 PDT with sts in us-east-1", but base on the document region fareast "fe" host region should be us-west-2.
Is this the reason causing this issue? Where should I change? My understanding is that IAM doesn't have the region.

Thanks for your help.

Example for POST_ORDER_FULFILLMENT_DATA

Someone successful using the POST_ORDER_FULFILLMENT_DATA feed to update the Order Status to shipped?

Can please someone provide an example of the Feed XML?
Also can I update more than one Order with this Feed?
Because there is also the POST_FLAT_FILE_FULFILLMENT_DATA Feed ...

Currently I'm testing with this Feed XML

<?xml version="1.0" encoding="utf-8"?>
<AmazonEnvelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="amzn-envelope.xsd">
    <Header>
        <DocumentVersion>1.02</DocumentVersion>
        <MerchantIdentifier>XXX</MerchantIdentifier>
    </Header>
    <MessageType>OrderFulfillment</MessageType>
    <Message>
        <MessageID>1</MessageID>
        <OrderFulfillment>
            <AmazonOrderID>XXX-XXXXXX-XXXXXX</AmazonOrderID>
            <FulfillmentDate>2021-02-28T12:34:56</FulfillmentDate>
        </OrderFulfillment>
    </Message>
</AmazonEnvelope>

but I'm not sure if it's correct like that ... I used the Schema and tried to reproduce the given example in the Docs.


Also, can I see any details of a Feed Report?
I'm getting:

{
  processingEndTime: '2021-02-28T12:14:49+00:00',
  processingStatus: 'DONE',
  marketplaceIds: [ 'A1PA6795UKMFR9' ],
  feedId: 'XXXXXXXX',
  feedType: 'POST_ORDER_FULFILLMENT_DATA',
  createdTime: '2021-02-28T12:14:25+00:00',
  processingStartTime: '2021-02-28T12:14:31+00:00',
  resultFeedDocumentId: 'amzn1.tortuga.3.9690c3e4-802b-4baa-8b62-XXX...'
}

but when I use the resultFeedDocumentId for the getFeedDocument call - I get the same response as for createFeedDocument

Update Inventory Price

Hi @amz-tools,
Thanks for this library, it's really useful and good job!

I want to update the inventory price. I tried 4 different ways but neither worked.
Am I missing something?

I'm working in the European zone.

Option 1

listingsItems.patchListingsItem
request

 .callAPI({
      operation: 'listingsItems.patchListingsItem',
      path: { sku:[sku], sellerId:[sellerId] },
      query: { marketplaceIds: [marketPlaceId], issueLocale: 'en_US' },
      body: { productType: 'PRODUCT', patches: [{ op: 'replace', path: '/attributes/item-price', value: 19.99 }] },
    })

response

code:'InternalFailure'
details:''
message:'We encountered an internal error. Please try again.'
type:'error'

My opinion: I think this endpoint has not been implemented yet for Europe.

Option 2

JSON_LISTINGS_FEED

  const feedType = 'JSON_LISTINGS_FEED';
  const content = {
    header: {
      sellerId: aspConfig.sellerId,
      version: '2.0',
      issueLocale: 'en_US',
    },
    messages: [
      {
        messageId: 1,
        sku: sku,
        operationType: 'PATCH',
        productType: 'PRODUCT',
        patches: [{ op: 'replace', path: '/attributes/listing-price', value: [{ value: price }] }],
      },
    ],
  };

  const contentType = 'application/json; charset=UTF-8';
  const feedUploadDetails = await spService.callAPI({ operation: 'feeds.createFeedDocument', body: { contentType } });

  await spService
    .upload(feedUploadDetails, JSON.stringify(content), contentType)
    .then(res => console.log(res))
    .catch(err => console.log(err));

  const inputFeedDocumentId = feedUploadDetails.feedDocumentId;

  return spService
    .callAPI({
      operation: 'feeds.createFeed',
      body: { feedType, marketplaceIds: [marketPlace.id], inputFeedDocumentId },
    })
    .then(res => console.log(res))
    .catch(err => console.log(err));

The feed document was uploaded successfully and also the feed was created successfully, but the status of the feed is IN_PROGRESS for 12 hours.

createdTime:'2021-07-16T07:42:27+00:00'
feedId:'138122018824'
feedType:'JSON_LISTINGS_FEED'
marketplaceIds:(1) ['A1PA6795UKMFR9']
processingStartTime:'2021-07-16T07:42:35+00:00'
processingStatus:'IN_PROGRESS'

My opinion: I think this feed is related to listingsItems.patchListingsItem at the backend so may has the same problem.

Option 3 & Option 4

feeds: POST_FLAT_FILE_INVLOADER_DATA & POST_FLAT_FILE_PRICEANDQUANTITYONLY_UPDATE_DATA

  const feedType = 'POST_FLAT_FILE_INVLOADER_DATA'; // or 'POST_FLAT_FILE_PRICEANDQUANTITYONLY_UPDATE_DATA'
  const content = `sku price\n${sku} ${price}`;

  const contentType = 'text/tab-separated-values; charset=UTF-8';

  const feedUploadDetails = await spService.callAPI({ operation: 'feeds.createFeedDocument', body: { contentType } });

  await spService
    .upload(feedUploadDetails, content, contentType)
    .then(res => console.log(res))
    .catch(err => console.log(err));

  const inputFeedDocumentId = feedUploadDetails.feedDocumentId;

  await spService
    .callAPI({
      operation: 'feeds.createFeed',
      body: { feedType, marketplaceIds: [marketPlace.id], inputFeedDocumentId },
    })
    .then(res => console.log(res))
    .catch(err => console.log(err));

feeds status:

{  createdTime:'2021-07-17T19:12:48+00:00'
   feedId:'139649018825'
   feedType:'POST_FLAT_FILE_INVLOADER_DATA' 
   marketplaceIds:(1) ['A1PA6795UKMFR9']
   processingStatus:'IN_QUEUE'
}
{
   createdTime:'2021-07-17T16:52:53+00:00'
   feedId:'139397018825'
   feedType:'POST_FLAT_FILE_PRICEANDQUANTITYONLY_UPDATE_DATA'  
   marketplaceIds:(1) ['A1PA6795UKMFR9']
   processingStatus:'IN_QUEUE'
}

I think I'm going to lose the remaining half of my hair because of seller-partner-api and it's documentation.

Thanks for your help

I can't get CatalogItems from listCatalogItems

let catalogItem = await sellingPartner.callAPI({
operation:'listCatalogItems',
endpoint:'catalogItems',
query:{
MarketplaceId:'ATVPDKIKX0DER'
},
options:{
version:'2020-12-01'
}
});

My response:
CustomError: Required parameter IdType not found.

What is IdType? Please help me.

Thank you in advance for your help.

Create Solicitation API Issue

Hi
I am working on create Solicitation API , But it provide me Error that marketplaceIds is missing or invalid,
When I check the code of npm repository I saw the marketplaceIds is not added in API route ,
We are able to create Solicitation by postman . The Example of request URL is
https://sellingpartnerapi-eu.amazon.com/solicitations/v1/orders/{orderId}/solicitations/productReviewAndSellerFeedback?marketplaceIds={marketpalaceId}
And After adding marketpalaceId in node module of amazon-sp-api in Create Solicitation the Error Changes to
The request signature we calculated does not match the signature you provided.
But by postman we are able to create Solicitation with same AWS credentials.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.