DEV Community

Cover image for Build a Four-Stage Amazon ASIN Research Pipeline with Node.js
Nexscope Team for Nexscope

Posted on Originally published at nexscope.ai

Build a Four-Stage Amazon ASIN Research Pipeline with Node.js

An ASIN is a useful starting key, but one endpoint rarely answers a complete product-research question. Traffic exposure, reverse-ASIN keywords, comparable products, and niche-level demand describe different parts of the market.

This tutorial connects four Amazon data APIs into one Node.js pipeline. The result is a reviewable JSON report with explicit missing values, per-stage errors, and enough provenance to prevent a visibility score from being mistaken for verified sessions or sales.

Four Amazon ASIN API workflows for traffic, keywords, competitors, and niche validation

Pipeline Design

The pipeline uses four stages:

Stage Endpoint Research question
Traffic summary amazon-asin-traffic-summary Where does the ASIN appear to gain or lose exposure?
Reverse keywords amazon-asin-keywords Which organic and paid keywords are associated with the ASIN?
Related products amazon-related-asins Which same-niche products should enter the competitor set?
Niche validation amazon-niche-info-by-asin What demand, concentration, and opportunity signals describe the niche?

The current documentation exposes all four as authenticated POST requests under the same base path:

https://api.nexscope.ai/api/skill-api/v1/skills/{skill}/run
Enter fullscreen mode Exit fullscreen mode

Set the API key on the server. Do not place it in browser code or commit it to a repository.

export NEXSCOPE_API_KEY="nk_your_key_here"
export AMAZON_ASIN="B072MQ5BRX"
Enter fullscreen mode Exit fullscreen mode

Build a Resilient Client

The client below retries transient network failures, HTTP 429 responses, and server errors. It fails immediately on invalid input or authentication errors because repeating the same request will not fix either condition.

const API_KEY = process.env.NEXSCOPE_API_KEY;
const API_BASE = "https://api.nexscope.ai/api/skill-api/v1/skills";

if (!API_KEY) throw new Error("Set NEXSCOPE_API_KEY first");

const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

async function callSkill(skill, body, maxAttempts = 3) {
  let lastError;

  for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
    let response;

    try {
      response = await fetch(`${API_BASE}/${skill}/run`, {
        method: "POST",
        headers: {
          Authorization: `Bearer ${API_KEY}`,
          "Content-Type": "application/json",
        },
        body: JSON.stringify(body),
      });
    } catch (error) {
      lastError = error;
      if (attempt < maxAttempts) {
        await wait(500 * 2 ** (attempt - 1));
        continue;
      }
      break;
    }

    if (response.ok) return response.json();

    const message = await response.text();
    const retryable = response.status === 429 || response.status >= 500;

    if (!retryable) {
      throw new Error(`${skill} failed (${response.status}): ${message}`);
    }

    lastError = new Error(`${skill} transient failure (${response.status})`);
    if (attempt < maxAttempts) await wait(500 * 2 ** (attempt - 1));
  }

  throw lastError;
}
Enter fullscreen mode Exit fullscreen mode

The published API definitions do not specify a fixed rate limit. Start with sequential requests, measure actual behavior, and only add concurrency after confirming the limits that apply to the account.

Define the Four Requests

The request fields below match the current live documentation. Traffic summary supports up to ten comma-separated ASINs, while the reverse-keyword endpoint accepts one ASIN per call. Related-ASIN and niche-by-ASIN currently document US, JP, and DE country codes.

async function collectResearch(asin, country = "US") {
  const traffic = await callSkill("amazon-asin-traffic-summary", {
    searchValue: asin,
    country,
    last7d: true,
    pageNum: 1,
    pageSize: 10,
    desc: true,
  });

  const keywords = await callSkill("amazon-asin-keywords", {
    asin,
    country,
    timePieceType: "latelyDay",
    timePieceValue: "7",
    pageNum: 1,
    pageSize: 100,
    desc: true,
  });

  const related = await callSkill("amazon-related-asins", {
    asin,
    countryCode: country,
    page: 1,
    pageSize: 50,
  });

  const niches = await callSkill("amazon-niche-info-by-asin", {
    asin,
    countryCode: country,
    count: 10,
  });

  return { traffic, keywords, related, niches };
}
Enter fullscreen mode Exit fullscreen mode

For production work, make country validation endpoint-specific. Passing UK to an endpoint that currently documents only US, JP, and DE should be rejected before the request is sent.

Normalize Optional Fields

Direct upstream payloads can contain optional fields. A missing value should remain null, not become 0. Zero means the API observed a value of zero; null means the field was absent or unusable.

function numberOrNull(value) {
  if (value === null || value === undefined || value === "") return null;
  const parsed = Number(value);
  return Number.isFinite(parsed) ? parsed : null;
}

function arrayOrEmpty(value) {
  return Array.isArray(value) ? value : [];
}

function normalizeTraffic(payload) {
  const row = arrayOrEmpty(payload?.data)[0] ?? {};

  return {
    asin: row.asin ?? null,
    periodStart: row.dataPeriodStartDate ?? null,
    totalExposureScore: numberOrNull(row.totalExposureScore),
    totalKeywordCount: numberOrNull(row.totalTrafficKeywordCount),
    incomingKeywordCount: numberOrNull(row.totalTrafficKeywordCountIn),
    outgoingKeywordCount: numberOrNull(row.totalTrafficKeywordCountOut),
    naturalExposureScore: numberOrNull(row.naturalSearchExposureScore),
    sponsoredExposureScore: numberOrNull(row.sponsoredProductsExposureScore),
  };
}

function normalizeRows(payload) {
  if (Array.isArray(payload?.data)) return payload.data;
  if (Array.isArray(payload?.items)) return payload.items;
  if (Array.isArray(payload)) return payload;
  return [];
}
Enter fullscreen mode Exit fullscreen mode

The flexible row extractor is defensive. It does not invent records or promise that every endpoint uses the same wrapper. Keep the original payload beside the normalized report when auditability matters.

Produce a Research Report

The report records the endpoint, marketplace, collection time, and result count for every stage. Those fields make later comparisons safer.

function stageReport(endpoint, rows) {
  return {
    endpoint,
    recordCount: rows.length,
    records: rows,
  };
}

async function buildReport(asin, country = "US") {
  const raw = await collectResearch(asin, country);

  return {
    schemaVersion: 1,
    asin,
    country,
    collectedAt: new Date().toISOString(),
    traffic: normalizeTraffic(raw.traffic),
    keywords: stageReport("amazon-asin-keywords", normalizeRows(raw.keywords)),
    competitors: stageReport("amazon-related-asins", normalizeRows(raw.related)),
    niches: stageReport("amazon-niche-info-by-asin", normalizeRows(raw.niches)),
  };
}

const report = await buildReport(process.env.AMAZON_ASIN, "US");
console.log(JSON.stringify(report, null, 2));
Enter fullscreen mode Exit fullscreen mode

Do not rank competitors solely because the related-ASIN endpoint returned them. Review product function, price band, variation structure, and customer need before treating a product as a direct competitor.

Handle Partial Failures

Research pipelines are more useful when one failed stage does not erase three successful stages. Wrap each stage independently and expose the error without leaking credentials or full request headers.

async function capture(name, task) {
  try {
    return { name, ok: true, data: await task() };
  } catch (error) {
    return { name, ok: false, error: String(error.message ?? error) };
  }
}

const asin = process.env.AMAZON_ASIN;

const stages = [];
stages.push(await capture("traffic", () => callSkill(
  "amazon-asin-traffic-summary",
  { searchValue: asin, country: "US", last7d: true, pageNum: 1, pageSize: 10 },
)));
stages.push(await capture("keywords", () => callSkill(
  "amazon-asin-keywords",
  { asin, country: "US", pageNum: 1, pageSize: 100, desc: true },
)));

console.log(JSON.stringify({ asin, collectedAt: new Date().toISOString(), stages }, null, 2));
Enter fullscreen mode Exit fullscreen mode

Persist the successful stage outputs with a run identifier. Retry failed stages later instead of resubmitting the complete pipeline blindly.

Evidence Boundaries

Several safeguards matter before the report influences sourcing or ad decisions:

  • Exposure scores are composite research signals, not verified sessions, clicks, orders, or revenue.
  • Keyword volume, rank, conversion, sales, and opportunity fields may be estimated or modeled unless the documentation explicitly states otherwise.
  • Related products may include substitutes, accessories, bundles, or adjacent solutions.
  • Source, freshness, coverage, and rate-limit details are not documented for every endpoint.
  • A missing optional field should remain missing.

The pipeline should support a decision, not hide uncertainty behind one combined score.

Next Step

Nexscope Amazon Data API provides the traffic, keyword, competitor, niche, review, price, sales, and product-data endpoints used to build research workflows through REST API or MCP.

Explore Amazon Data API →

Sources

Disclosure: This article was prepared with AI-assisted editing using the current published API documentation as its technical source of truth.

Top comments (0)