A Postman collection turns the API documentation into something you click, not retype: every endpoint as a saved request, one environment holding the base URL and your key, and a test script under each request that checks the response before you read it. This post ships a real Postman Collection v2.1 JSON file for all 10 SellerMagnet Amazon API endpoints, a companion environment with api_key stored as a secret variable, and pm.test() scripts that assert response.code === 200, json.success === true and one shape check drawn from the documented, verified response fields, the same fields the language specific quickstarts for this API decode in actual client code instead of a Postman test.
Key Takeaways
- The collection covers all 10 real endpoints from the API, there is no batch endpoint and no 11th request invented to round out a number.
- Auth is one X-Api-Key header, set once at the collection level from the environment variable {{api_key}}, which the environment marks as type secret so it never shows in a shared export.
- Every request's pm.test() checks the HTTP status, the success flag, and one shape assertion specific to that endpoint, for example buyBoxInfo.price being a number or the literal string N/A on lookup, versus an integer number of cents on statistics.
- Run /amazon-get-marketplaces first: its response keys (ATVPDKIKX0DER, A1PA6795UKMFR9, ...) are the marketplaceId values every other request needs.
- newman was not installed in the environment this post was built in, so the test scripts were verified by actually running them (via Node's vm module) against fixture responses built from the documented field shapes, including a deliberately wrong fixture to prove the assertions can fail, not just a hand written transcript.
What the collection contains
- Get Amazon Marketplaces - no parameters beyond the key; run this first for valid marketplaceId values.
- Product Lookup (asin, marketplaceId) - buyBoxInfo.price is a number or "N/A".
- Product Statistics (asin, marketplaceId) - buyBoxPrice is an integer in cents.
- Product Offers (asin, marketplaceId) - buyBox is an object (possibly empty) and offers is an array.
- Seller Review (sellerId, marketplaceId) - feedback.positiveRating/negativeRating are counts.
- Search Amazon (q, marketplaceId, count) - listingPrice.price.total is a formatted string.
- Bestsellers (category_id, marketplaceId, count, offset) - data.bestsellers is an array.
- Amazon Deals (marketplaceId, category_id, count) - deals[].dealPrice.priceTotal is a number.
- Estimated Monthly Sales (asin, marketplaceId) - estimated_monthly_sales is present.
- ASIN / EAN Converter (asin, marketplaceId, conversion_direction) - eanList is an array.
Every request is a plain GET; there is no POST, no batch call and no pagination parameter beyond count/offset on bestsellers. Import both files below into Postman (File > Import, drop both JSONs), pick the environment from the dropdown in the top right, paste your key into api_key, and the whole collection runs end to end.
The collection: collection.json
A standard Postman Collection v2.1 export: info.schema points at the v2.1 collection schema, auth is set once at the collection level (type apikey, header X-Api-Key, value {{api_key}}) so every request inherits it, and each item under item[] carries its own request and a test event. This is the exact file, unmodified, that was validated and test run further down.
collection.json, Postman Collection v2.1, 10 requests
{
"info": {
"_postman_id": "a3e1adca-2cd6-4f1c-862d-141cbf8d4b98",
"name": "SellerMagnet Amazon API",
"description": "Postman Collection v2.1 covering all 10 SellerMagnet Amazon API endpoints (https://sellermagnet-api.com/docs). Each request carries a pm.test() script that checks the HTTP status, the success flag and one shape-specific assertion drawn from the API's documented, verified response fields. Auth: X-Api-Key header, value {{api_key}}, set in the companion environment as a secret variable.",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
},
"auth": {
"type": "apikey",
"apikey": [
{
"key": "key",
"value": "X-Api-Key",
"type": "string"
},
{
"key": "value",
"value": "{{api_key}}",
"type": "string"
},
{
"key": "in",
"value": "header",
"type": "string"
}
]
},
"event": [],
"variable": [
{
"key": "base_url",
"value": "https://sellermagnet-api.com/api"
}
],
"item": [
{
"name": "Get Amazon Marketplaces",
"event": [
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"pm.test(\"Status code is 200\", function () {",
" pm.response.to.have.status(200);",
"});",
"",
"pm.test(\"Response body has success true\", function () {",
" var json = pm.response.json();",
" pm.expect(json.success).to.eql(true);",
"});",
"",
"pm.test(\"data is keyed by marketplace id (ATVPDKIKX0DER present)\", function () {",
" var json = pm.response.json();",
" pm.expect(json.data).to.have.property(\"ATVPDKIKX0DER\");",
" pm.expect(json.data.ATVPDKIKX0DER).to.have.property(\"currency_code\");",
"});"
]
}
}
],
"request": {
"method": "GET",
"header": [
{
"key": "X-Api-Key",
"value": "{{api_key}}",
"type": "text"
}
],
"url": {
"raw": "{{base_url}}/amazon-get-marketplaces",
"host": [
"{{base_url}}"
],
"path": [
"amazon-get-marketplaces"
],
"query": []
},
"description": "Run this first: the marketplace IDs it returns (e.g. ATVPDKIKX0DER, A1PA6795UKMFR9) are reused as marketplaceId in every other request in this collection."
},
"response": []
},
{
"name": "Product Lookup",
"event": [
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"pm.test(\"Status code is 200\", function () {",
" pm.response.to.have.status(200);",
"});",
"",
"pm.test(\"Response body has success true\", function () {",
" var json = pm.response.json();",
" pm.expect(json.success).to.eql(true);",
"});",
"",
"pm.test(\"buyBoxInfo.price is a number or the string N-A\", function () {",
" var price = pm.response.json().data.productInfo.buyBoxInfo.price;",
" pm.expect(typeof price === \"number\" || price === \"N/A\").to.be.true;",
"});"
]
}
}
],
"request": {
"method": "GET",
"header": [
{
"key": "X-Api-Key",
"value": "{{api_key}}",
"type": "text"
}
],
"url": {
"raw": "{{base_url}}/amazon-product-lookup?asin={{asin}}&marketplaceId={{marketplace_id}}",
"host": [
"{{base_url}}"
],
"path": [
"amazon-product-lookup"
],
"query": [
{
"key": "asin",
"value": "{{asin}}"
},
{
"key": "marketplaceId",
"value": "{{marketplace_id}}"
}
]
},
"description": "Single-ASIN product lookup. buyBoxInfo.price is decimal currency units or the literal string \"N/A\" when there is no featured offer, never a bare 0."
},
"response": []
},
{
"name": "Product Statistics",
"event": [
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"pm.test(\"Status code is 200\", function () {",
" pm.response.to.have.status(200);",
"});",
"",
"pm.test(\"Response body has success true\", function () {",
" var json = pm.response.json();",
" pm.expect(json.success).to.eql(true);",
"});",
"",
"pm.test(\"buyBoxPrice is an integer number of cents\", function () {",
" var json = pm.response.json();",
" var price = json.data.buyBoxPrice;",
" pm.expect(price === \"N/A\" || (typeof price === \"number\" && Number.isInteger(price))).to.be.true;",
"});"
]
}
}
],
"request": {
"method": "GET",
"header": [
{
"key": "X-Api-Key",
"value": "{{api_key}}",
"type": "text"
}
],
"url": {
"raw": "{{base_url}}/amazon-product-statistics?asin={{asin}}&marketplaceId={{marketplace_id}}",
"host": [
"{{base_url}}"
],
"path": [
"amazon-product-statistics"
],
"query": [
{
"key": "asin",
"value": "{{asin}}"
},
{
"key": "marketplaceId",
"value": "{{marketplace_id}}"
}
]
},
"description": "Historic price/BSR/Buy Box series. buyBoxPrice is an INTEGER in cents (41800 = 418.00), a different unit than lookup or offers."
},
"response": []
},
{
"name": "Product Offers",
"event": [
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"pm.test(\"Status code is 200\", function () {",
" pm.response.to.have.status(200);",
"});",
"",
"pm.test(\"Response body has success true\", function () {",
" var json = pm.response.json();",
" pm.expect(json.success).to.eql(true);",
"});",
"",
"pm.test(\"buyBox is an object (possibly empty) and offers is an array\", function () {",
" var json = pm.response.json();",
" pm.expect(json.data.buyBox).to.be.a(\"object\");",
" pm.expect(json.data.offers).to.be.a(\"array\");",
"});"
]
}
}
],
"request": {
"method": "GET",
"header": [
{
"key": "X-Api-Key",
"value": "{{api_key}}",
"type": "text"
}
],
"url": {
"raw": "{{base_url}}/amazon-product-offers?asin={{asin}}&marketplaceId={{marketplace_id}}",
"host": [
"{{base_url}}"
],
"path": [
"amazon-product-offers"
],
"query": [
{
"key": "asin",
"value": "{{asin}}"
},
{
"key": "marketplaceId",
"value": "{{marketplace_id}}"
}
]
},
"description": "Live Buy Box plus the first page of offers. offers[].totalPrice is rounded to 2 dp; buyBox.totalPrice is price+shipping added as floats and is NOT pre-rounded."
},
"response": []
},
{
"name": "Seller Review",
"event": [
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"pm.test(\"Status code is 200\", function () {",
" pm.response.to.have.status(200);",
"});",
"",
"pm.test(\"Response body has success true\", function () {",
" var json = pm.response.json();",
" pm.expect(json.success).to.eql(true);",
"});",
"",
"pm.test(\"feedback counts are numbers\", function () {",
" var fb = pm.response.json().data.feedback;",
" pm.expect(fb.positiveRating).to.be.a(\"number\");",
" pm.expect(fb.negativeRating).to.be.a(\"number\");",
"});"
]
}
}
],
"request": {
"method": "GET",
"header": [
{
"key": "X-Api-Key",
"value": "{{api_key}}",
"type": "text"
}
],
"url": {
"raw": "{{base_url}}/amazon-seller-review?sellerId={{seller_id}}&marketplaceId={{marketplace_id}}",
"host": [
"{{base_url}}"
],
"path": [
"amazon-seller-review"
],
"query": [
{
"key": "sellerId",
"value": "{{seller_id}}"
},
{
"key": "marketplaceId",
"value": "{{marketplace_id}}"
}
]
},
"description": "Seller feedback and rating history. positiveRating/neutralRating/negativeRating are COUNTS (4-5, 2-3, 1 stars), not percentages."
},
"response": []
},
{
"name": "Search Amazon",
"event": [
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"pm.test(\"Status code is 200\", function () {",
" pm.response.to.have.status(200);",
"});",
"",
"pm.test(\"Response body has success true\", function () {",
" var json = pm.response.json();",
" pm.expect(json.success).to.eql(true);",
"});",
"",
"pm.test(\"listingPrice.price.total is a formatted string\", function () {",
" var results = pm.response.json().data.searchResults;",
" pm.expect(results).to.be.a(\"array\");",
" if (results.length > 0) {",
" pm.expect(typeof results[0].listingPrice.price.total).to.eql(\"string\");",
" }",
"});"
]
}
}
],
"request": {
"method": "GET",
"header": [
{
"key": "X-Api-Key",
"value": "{{api_key}}",
"type": "text"
}
],
"url": {
"raw": "{{base_url}}/amazon-search?q={{search_query}}&marketplaceId={{marketplace_id}}&count=10",
"host": [
"{{base_url}}"
],
"path": [
"amazon-search"
],
"query": [
{
"key": "q",
"value": "{{search_query}}"
},
{
"key": "marketplaceId",
"value": "{{marketplace_id}}"
},
{
"key": "count",
"value": "10"
}
]
},
"description": "Keyword search, page one only. listingPrice.price.total is a two-decimal STRING like \"449.00\", not a number; count is capped at 50."
},
"response": []
},
{
"name": "Bestsellers",
"event": [
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"pm.test(\"Status code is 200\", function () {",
" pm.response.to.have.status(200);",
"});",
"",
"pm.test(\"Response body has success true\", function () {",
" var json = pm.response.json();",
" pm.expect(json.success).to.eql(true);",
"});",
"",
"pm.test(\"bestsellers is an array of ranked products\", function () {",
" var list = pm.response.json().data.bestsellers;",
" pm.expect(list).to.be.a(\"array\");",
"});"
]
}
}
],
"request": {
"method": "GET",
"header": [
{
"key": "X-Api-Key",
"value": "{{api_key}}",
"type": "text"
}
],
"url": {
"raw": "{{base_url}}/amazon-bestsellers?category_id={{category_id}}&marketplaceId={{marketplace_id}}&count=10",
"host": [
"{{base_url}}"
],
"path": [
"amazon-bestsellers"
],
"query": [
{
"key": "category_id",
"value": "{{category_id}}"
},
{
"key": "marketplaceId",
"value": "{{marketplace_id}}"
},
{
"key": "count",
"value": "10"
}
]
},
"description": "Bestseller list for one category_id. count+offset together must stay at or under 50, or the endpoint answers a billed 400."
},
"response": []
},
{
"name": "Amazon Deals",
"event": [
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"pm.test(\"Status code is 200\", function () {",
" pm.response.to.have.status(200);",
"});",
"",
"pm.test(\"Response body has success true\", function () {",
" var json = pm.response.json();",
" pm.expect(json.success).to.eql(true);",
"});",
"",
"pm.test(\"deals is an array with numeric dealPrice.priceTotal\", function () {",
" var deals = pm.response.json().data.deals;",
" pm.expect(deals).to.be.a(\"array\");",
" if (deals.length > 0) {",
" pm.expect(deals[0].dealPrice.priceTotal).to.be.a(\"number\");",
" }",
"});"
]
}
}
],
"request": {
"method": "GET",
"header": [
{
"key": "X-Api-Key",
"value": "{{api_key}}",
"type": "text"
}
],
"url": {
"raw": "{{base_url}}/amazon-deals?marketplaceId={{marketplace_id}}&category_id={{category_id}}&count=10",
"host": [
"{{base_url}}"
],
"path": [
"amazon-deals"
],
"query": [
{
"key": "marketplaceId",
"value": "{{marketplace_id}}"
},
{
"key": "category_id",
"value": "{{category_id}}"
},
{
"key": "count",
"value": "10"
}
]
},
"description": "Current deals for a marketplace, optionally filtered by a department-style category_id. dealPrice.priceTotal is a number, 0.0 when the page price could not be read."
},
"response": []
},
{
"name": "Estimated Monthly Sales",
"event": [
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"pm.test(\"Status code is 200\", function () {",
" pm.response.to.have.status(200);",
"});",
"",
"pm.test(\"Response body has success true\", function () {",
" var json = pm.response.json();",
" pm.expect(json.success).to.eql(true);",
"});",
"",
"pm.test(\"estimated_monthly_sales is present\", function () {",
" var json = pm.response.json();",
" pm.expect(json.data).to.have.property(\"estimated_monthly_sales\");",
"});"
]
}
}
],
"request": {
"method": "GET",
"header": [
{
"key": "X-Api-Key",
"value": "{{api_key}}",
"type": "text"
}
],
"url": {
"raw": "{{base_url}}/amazon-product-search-estimated-sells?asin={{asin}}&marketplaceId={{marketplace_id}}",
"host": [
"{{base_url}}"
],
"path": [
"amazon-product-search-estimated-sells"
],
"query": [
{
"key": "asin",
"value": "{{asin}}"
},
{
"key": "marketplaceId",
"value": "{{marketplace_id}}"
}
]
},
"description": "Estimated monthly units sold, derived from Keepa sales rank and rating data. Only covers the 11 Keepa-backed marketplaces."
},
"response": []
},
{
"name": "ASIN / EAN Converter",
"event": [
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"pm.test(\"Status code is 200\", function () {",
" pm.response.to.have.status(200);",
"});",
"",
"pm.test(\"Response body has success true\", function () {",
" var json = pm.response.json();",
" pm.expect(json.success).to.eql(true);",
"});",
"",
"pm.test(\"eanList is an array\", function () {",
" var json = pm.response.json();",
" pm.expect(json.data.eanList).to.be.a(\"array\");",
"});"
]
}
}
],
"request": {
"method": "GET",
"header": [
{
"key": "X-Api-Key",
"value": "{{api_key}}",
"type": "text"
}
],
"url": {
"raw": "{{base_url}}/amazon-asin-converter?asin={{asin}}&marketplaceId={{marketplace_id}}&conversion_direction=asin-to-ean",
"host": [
"{{base_url}}"
],
"path": [
"amazon-asin-converter"
],
"query": [
{
"key": "asin",
"value": "{{asin}}"
},
{
"key": "marketplaceId",
"value": "{{marketplace_id}}"
},
{
"key": "conversion_direction",
"value": "asin-to-ean"
}
]
},
"description": "Converts an ASIN to its EAN/UPC codes (or the reverse with conversion_direction=ean-to-asin). Only the first matching product is returned."
},
"response": []
}
]
}
Authentication: one header, one secret variable
The API accepts the key as the api_key query parameter or the X-Api-Key header; the header keeps the key out of logged URLs, so that is what the collection uses. The environment stores it as type: "secret", which Postman masks in the UI and drops from a plain export (sharing collection.json never leaks a key, because the key itself only ever lives in the environment). Fill marketplace_id, asin, seller_id, search_query and category_id with values valid for your account; the defaults below match the ASIN and marketplace used in the API documentation's own examples.
environment.json, base_url and a secret api_key variable
{
"id": "27b947b3-c346-499d-87a1-1924dc097cad",
"name": "SellerMagnet API",
"values": [
{
"key": "base_url",
"value": "https://sellermagnet-api.com/api",
"type": "default",
"enabled": true
},
{
"key": "api_key",
"value": "",
"type": "secret",
"enabled": true
},
{
"key": "marketplace_id",
"value": "ATVPDKIKX0DER",
"type": "default",
"enabled": true
},
{
"key": "asin",
"value": "B0CL61F39H",
"type": "default",
"enabled": true
},
{
"key": "seller_id",
"value": "A2L77EE7U53NWQ",
"type": "default",
"enabled": true
},
{
"key": "search_query",
"value": "playstation 5",
"type": "default",
"enabled": true
},
{
"key": "category_id",
"value": "electronics",
"type": "default",
"enabled": true
}
],
"_postman_variable_scope": "environment"
}
Test scripts: status, success, and one shape check
Each request's Tests tab (the test event in the JSON) runs three assertions: the status code is 200, success is true, and one check tied to that endpoint's documented response shape. The Product Lookup request below is representative: buyBoxInfo.price is a decimal number for a featured offer, or the literal string "N/A" when there is none, so the test accepts either instead of assuming a number and failing on a perfectly normal response.
Test script on the Product Lookup request
pm.test("Status code is 200", function () {
pm.response.to.have.status(200);
});
pm.test("Response body has success true", function () {
var json = pm.response.json();
pm.expect(json.success).to.eql(true);
});
pm.test("buyBoxInfo.price is a number or the string N-A", function () {
var price = pm.response.json().data.productInfo.buyBoxInfo.price;
pm.expect(typeof price === "number" || price === "N/A").to.be.true;
});
| Endpoint | Field | Example value | Shape |
|---|---|---|---|
| Product Lookup | buyBoxInfo.price | 444.99 or "N/A" | number, or the literal string N/A |
| Product Statistics | buyBoxPrice | 41800 | integer cents (41800 = 418.00) |
| Product Offers | buyBox.totalPrice | 444.97999999999996 | float, not pre-rounded, round client side |
| Amazon Search | listingPrice.price.total | "449.00" | two decimal string, not a number |
| Bestsellers | price.price | 16.68 | decimal currency units |
| Amazon Deals | dealPrice.priceTotal | 399.0 | number, 0.0 when unreadable |

Which status codes should a CI run retry
A test runner that retries on failure needs to know which failures are worth retrying. A 404 means the ASIN or seller is not on that marketplace: retrying it bills the same answer again. A 429 from the per key concurrency cap is free and carries Retry-After; a 500 without Retry-After is refunded, so one retry is usually free; 502 and 503 are billed per attempt. None of this lives inside a pm.test() script (Postman test scripts check one response, they do not retry), it belongs in the CI wrapper around newman, shown next.
| Status | Meaning | Billed | CI action |
|---|---|---|---|
200 | Success | Yes | Assert status 200 and success true |
400 | Bad or missing parameter | Depends | Gate 400s are free; an endpoint's own 400 is billed |
401 / 403 | Key or credits | No | Stop, do not retry in the test run |
404 | Not found on this marketplace | Yes | An answer, not a bug in the collection |
429 | Concurrency or per-IP limit | No | Back off on Retry-After in CI |
500 | Internal error, no Retry-After | Refunded | Safe to retry once in CI |
502 / 503 | Upstream or circuit breaker | Yes | Retry with backoff, costs a credit per attempt |
Running the collection with newman in CI
newman is Postman's own command line runner: it executes a collection and its test scripts exactly as the Postman app would, and exits non zero on a failed assertion, which is what makes it usable in a CI pipeline. The script below installs it, runs the whole collection against the environment, injects the key from a CI secret instead of committing it, and writes a JUnit report a CI dashboard can read. The commented line shows running one folder (one endpoint) alone, useful while editing a single test.
ci.sh, newman run instructions for a CI pipeline
#!/usr/bin/env bash
# ci.sh - run the collection in CI with newman (not installed in this writeup's
# sandbox, so this script is provided as-is, not executed here; see the post
# body for how the test scripts were actually verified instead).
set -euo pipefail
npm install -g newman
newman run collection.json \
-e environment.json \
--env-var "api_key=$SELLERMAGNET_API_KEY" \
--reporters cli,junit \
--reporter-junit-export newman-report.xml
# Run one folder only (one endpoint), useful while iterating on a single test:
# newman run collection.json -e environment.json --folder "Product Lookup"
newman was not run
newman was not available in the sandbox this post was built in (no global npm install reached it), so the ci.sh script above was written to the documented newman CLI flags and was not actually executed here. What was actually run and captured is the verification below: the same test scripts, pulled straight out of collection.json, executed for real against fixture responses.
How the test scripts were actually verified here
Rather than invent a newman transcript, the exact exec arrays from collection.json were extracted and run inside Node's vm module against a small pm shim (pm.test, pm.response.to.have.status, pm.response.json(), pm.expect(...).to.{eql,be.a,be.true,have.property}), once per request, against a fixture response body built from the documented, verified shape for that endpoint. A second pass fed two requests a deliberately wrong fixture (a 404 for lookup, and a search response with total as a number instead of a string) to prove the assertions actually fail when the shape is wrong, rather than passing unconditionally. This checks that the scripts are valid JavaScript and that their logic is correct; it is not a live HTTP call against sellermagnet-api.com.
verify.js, runs the real pm.test scripts from collection.json in a pm/chai shim
// verify.js - runs the REAL pm.test() scripts extracted from collection.json
// inside a minimal pm/chai shim, against fixture response bodies built from
// the documented, verified field shapes in FACTS.md. This is NOT newman (not
// installed in this environment) and NOT a live HTTP call: it proves the
// test scripts are syntactically valid JavaScript and that their assertions
// actually pass on a correctly-shaped 200 response, and actually fail when
// fed a wrong status or a wrong-shaped body.
const fs = require("fs");
const vm = require("vm");
const collection = JSON.parse(fs.readFileSync(__dirname + "/collection.json", "utf8"));
function makeExpect(val) {
const self = {};
self.to = {};
self.to.eql = (x) => {
if (JSON.stringify(val) !== JSON.stringify(x)) {
throw new Error(`expected ${JSON.stringify(val)} to eql ${JSON.stringify(x)}`);
}
};
self.to.equal = self.to.eql;
self.to.have = {
property: (name) => {
if (!val || !Object.prototype.hasOwnProperty.call(val, name)) {
throw new Error(`expected object to have property "${name}"`);
}
},
};
Object.defineProperty(self.to, "true", {
get() {
if (val !== true) throw new Error(`expected ${JSON.stringify(val)} to be true`);
return true;
},
});
self.to.be = {
a: (type) => {
const actual = Array.isArray(val) ? "array" : typeof val;
if (actual !== type) throw new Error(`expected type "${type}" got "${actual}"`);
},
};
Object.defineProperty(self.to.be, "true", {
get() {
if (val !== true) throw new Error(`expected ${JSON.stringify(val)} to be true`);
return true;
},
});
return self;
}
function runRequestTests(item, fixture) {
const results = [];
const pm = {
response: {
code: fixture.code,
json: () => fixture.body,
to: {
have: {
status: (expected) => {
if (fixture.code !== expected) {
throw new Error(`expected status ${expected} got ${fixture.code}`);
}
},
},
},
},
test: (name, fn) => {
try {
fn();
results.push({ name, pass: true });
} catch (err) {
results.push({ name, pass: false, error: err.message });
}
},
expect: makeExpect,
};
const exec = item.event[0].script.exec.join("\n");
const sandbox = { pm, console };
vm.createContext(sandbox);
new vm.Script(exec).runInContext(sandbox); // throws SyntaxError if the script is malformed
return results;
}
// One fixture per endpoint, built from the documented/verified shapes in
// scripts/blog_studio/FACTS.md (good 200 case).
const GOOD_FIXTURES = {
"Get Amazon Marketplaces": {
code: 200,
body: { success: true, data: { ATVPDKIKX0DER: { marketplace_domain: "amazon.com", marketplace_name: "United States", currency_code: "USD" } } },
},
"Product Lookup": {
code: 200,
body: { success: true, data: { productInfo: { asin: "B0CL61F39H", buyBoxInfo: { price: 444.99, currencyCode: "USD", sellerId: "Amazon" } } } },
},
"Product Statistics": {
code: 200,
body: { success: true, data: { buyBoxPrice: 41800 } },
},
"Product Offers": {
code: 200,
body: { success: true, data: { buyBox: { sellerId: "Amazon", totalPrice: 444.97999999999996 }, offers: [{ sellerId: "Amazon", totalPrice: 444.98 }] } },
},
"Seller Review": {
code: 200,
body: { success: true, data: { feedback: { positiveRating: 182, neutralRating: 4, negativeRating: 3, reviewsCount: { lifetime: 189 } } } },
},
"Search Amazon": {
code: 200,
body: { success: true, data: { searchResults: [{ position: 1, asin: "B0CL61F39H", listingPrice: { price: { total: "449.00", currency_code: "USD" } }, sponsored: false }] } },
},
"Bestsellers": {
code: 200,
body: { success: true, data: { bestsellers: [{ rank: 1, asin: "B0CL61F39H", price: { price: 16.68, currency_code: "USD" } }] } },
},
"Amazon Deals": {
code: 200,
body: { success: true, data: { deals: [{ asin: "B0CL61F39H", dealPrice: { priceTotal: 399.0, priceCurrency: "USD" } }] } },
},
"Estimated Monthly Sales": {
code: 200,
body: { success: true, data: { asin: "B0CL61F39H", estimated_monthly_sales: 1240, sales_rank: 58 } },
},
"ASIN / EAN Converter": {
code: 200,
body: { success: true, data: { asin: "B0CL61F39H", eanList: ["0711719566035"], productTitle: "PlayStation(R)5 console (slim)" } },
},
};
// One deliberately WRONG fixture (a 404, and a 200 with a wrong-shaped body)
// to prove the same scripts correctly report failure instead of always
// passing.
const BAD_FIXTURES = {
"Product Lookup": { code: 404, body: { success: false, error: "not_found", message: "Product not found" } },
"Search Amazon": { code: 200, body: { success: true, data: { searchResults: [{ position: 1, asin: "X", listingPrice: { price: { total: 449.0 } } }] } } }, // total is a number, not a string
};
let totalPass = 0, totalFail = 0;
console.log("=== GOOD fixtures (expect every test to PASS) ===");
for (const item of collection.item) {
const fixture = GOOD_FIXTURES[item.name];
const results = runRequestTests(item, fixture);
console.log(`\n[${item.name}] GET ${item.request.url.raw}`);
for (const r of results) {
console.log(` ${r.pass ? "PASS" : "FAIL"} - ${r.name}${r.pass ? "" : " (" + r.error + ")"}`);
r.pass ? totalPass++ : totalFail++;
}
}
console.log("\n=== BAD fixtures (expect at least one test to FAIL, proving the assertions discriminate) ===");
for (const [name, fixture] of Object.entries(BAD_FIXTURES)) {
const item = collection.item.find((i) => i.name === name);
const results = runRequestTests(item, fixture);
console.log(`\n[${name}] injected bad fixture (code=${fixture.code})`);
for (const r of results) {
console.log(` ${r.pass ? "PASS" : "FAIL"} - ${r.name}${r.pass ? "" : " (" + r.error + ")"}`);
}
}
console.log(`\n=== Summary: ${collection.item.length} requests, ${totalPass} assertions passed, ${totalFail} failed on the good fixtures ===`);
Real output: node verify.js (this environment, no newman, no network)
=== GOOD fixtures (expect every test to PASS) ===
[Get Amazon Marketplaces] GET {{base_url}}/amazon-get-marketplaces
PASS - Status code is 200
PASS - Response body has success true
PASS - data is keyed by marketplace id (ATVPDKIKX0DER present)
[Product Lookup] GET {{base_url}}/amazon-product-lookup?asin={{asin}}&marketplaceId={{marketplace_id}}
PASS - Status code is 200
PASS - Response body has success true
PASS - buyBoxInfo.price is a number or the string N-A
[Product Statistics] GET {{base_url}}/amazon-product-statistics?asin={{asin}}&marketplaceId={{marketplace_id}}
PASS - Status code is 200
PASS - Response body has success true
PASS - buyBoxPrice is an integer number of cents
[Product Offers] GET {{base_url}}/amazon-product-offers?asin={{asin}}&marketplaceId={{marketplace_id}}
PASS - Status code is 200
PASS - Response body has success true
PASS - buyBox is an object (possibly empty) and offers is an array
[Seller Review] GET {{base_url}}/amazon-seller-review?sellerId={{seller_id}}&marketplaceId={{marketplace_id}}
PASS - Status code is 200
PASS - Response body has success true
PASS - feedback counts are numbers
[Search Amazon] GET {{base_url}}/amazon-search?q={{search_query}}&marketplaceId={{marketplace_id}}&count=10
PASS - Status code is 200
PASS - Response body has success true
PASS - listingPrice.price.total is a formatted string
[Bestsellers] GET {{base_url}}/amazon-bestsellers?category_id={{category_id}}&marketplaceId={{marketplace_id}}&count=10
PASS - Status code is 200
PASS - Response body has success true
PASS - bestsellers is an array of ranked products
[Amazon Deals] GET {{base_url}}/amazon-deals?marketplaceId={{marketplace_id}}&category_id={{category_id}}&count=10
PASS - Status code is 200
PASS - Response body has success true
PASS - deals is an array with numeric dealPrice.priceTotal
[Estimated Monthly Sales] GET {{base_url}}/amazon-product-search-estimated-sells?asin={{asin}}&marketplaceId={{marketplace_id}}
PASS - Status code is 200
PASS - Response body has success true
PASS - estimated_monthly_sales is present
[ASIN / EAN Converter] GET {{base_url}}/amazon-asin-converter?asin={{asin}}&marketplaceId={{marketplace_id}}&conversion_direction=asin-to-ean
PASS - Status code is 200
PASS - Response body has success true
PASS - eanList is an array
=== BAD fixtures (expect at least one test to FAIL, proving the assertions discriminate) ===
[Product Lookup] injected bad fixture (code=404)
FAIL - Status code is 200 (expected status 200 got 404)
FAIL - Response body has success true (expected false to eql true)
FAIL - buyBoxInfo.price is a number or the string N-A (Cannot read properties of undefined (reading 'productInfo'))
[Search Amazon] injected bad fixture (code=200)
PASS - Status code is 200
PASS - Response body has success true
FAIL - listingPrice.price.total is a formatted string (expected "number" to eql "string")
=== Summary: 10 requests, 30 assertions passed, 0 failed on the good fixtures ===

Anatomy of one request: Product Statistics
Product Statistics is the endpoint most likely to trip up a hand written test: buyBoxPrice is an integer number of cents (41800 means 418.00), not the decimal number lookup and offers return. The request below shows the parameters, the response field the test reads, and what the assertion actually checks.

Frequently Asked Questions
Does the collection include a request for every SellerMagnet Amazon API endpoint?
Yes, all 10: get marketplaces, product lookup, product statistics, product offers, seller review, search, bestsellers, deals, estimated monthly sales, and the ASIN/EAN converter. There is no batch endpoint, so there is no batch request either.
Where do I put my API key?
In the environment, not the collection: open environment.json's api_key variable (type secret) and paste your key there. The collection reads it as {{api_key}} through a collection level X-Api-Key header, so the key is never written into collection.json itself.
Why does the Product Lookup test accept a string for buyBoxInfo.price?
Because the API itself can return either: a decimal number when a featured offer exists, or the literal string N/A when it does not. A test that assumes a number would fail on a perfectly normal, documented response.
Was this collection run against the live API with newman?
No. newman was not installed in the environment this post was built in. The test scripts were instead extracted from collection.json and run for real, inside Node's vm module against a pm/chai shim, on fixture response bodies built from the documented field shapes, including a deliberately wrong fixture to prove the assertions can fail.
Can I run just one endpoint instead of the whole collection?
Yes, with newman run collection.json -e environment.json --folder "Product Lookup" (or any other request name), or by selecting a single request's Send button in the Postman app.
What should a CI pipeline do when a request returns 429 or 502?
429 from the per key concurrency cap is free and carries Retry-After, so wait and retry. 502 and 503 are billed per attempt, so retry with backoff but cap the attempts. 404 is a real answer, not a failure to retry.
Bottom line: a real Postman Collection v2.1 file for all 10 endpoints, a companion environment with api_key stored as a secret, and pm.test scripts that check the status, the success flag and the actual shape of each price field, not an assumed one. For code instead of a collection, the same endpoints are covered in the Python, Go, Java and TypeScript quickstarts, bulk ASIN work is in the bulk ASIN lookup guide, and why none of this touches Amazon's own PA-API is explained in the PA-API alternative guide. Every endpoint and parameter is in the API documentation, more client snippets are on the code examples page, and a free account includes 150 credits to run the collection against.