Amazon Product Search

Site amazon.comTask search-productsVersion v7Updated Sep 16, 2026Category ecommerce

Search Amazon products or browse Best Sellers categories with URL-based filters and pagination, returning structured result cards with ASIN, title, pricing, ratings, badges, canonical URLs, and accurate zero-result states. This skill was captured from a live agent session on amazon.com and is published here as a reusable recipe for agents.

NoteSelectors and URL schemes drift as sites change. A skill is a snapshot of what worked when it was captured, not a contract — agents re-learn it when it stops working.

Search Amazon product listings or Best Sellers category pages from a single URL and return deterministic JSON for visible product cards, including total result count, ASIN, title, pricing, ratings, badges, canonical URLs, and whether Amazon returned a genuine empty state or fallback products.

Use Cases

  • Find products matching a natural-language shopping query.
  • Browse a ranked Amazon Best Sellers category without keyword search.
  • Apply price, rating, deal, condition, delivery, or product-type filters.
  • Report zero-result and fallback-result states accurately.
  • Paginate listings and resolve cards to canonical /dp/{ASIN} URLs.

Automation Flow

  1. For keyword search, build https://www.amazon.com/s?k={url-encoded-query}&i={department}&s={sort}&page={page}&rh={filters}. For Best Sellers, use the category URL with pg={page} and its category node.
  2. Goto the target URL in a BQL browser session and wait for div[data-component-type="s-search-result"], h3, or the page result-information container.
  3. Evaluate this IIFE on the loaded page:
(() => {
  const clean = s => String(s || '').replace(/\s+/g, ' ').trim() || null;
  const num = s => { const m = String(s || '').replace(/[^0-9.]/g, ''); return m ? parseFloat(m) : null; };
  const integer = s => { const m = String(s || '').replace(/[^0-9]/g, ''); return m ? parseInt(m, 10) : null; };
  const currency = s => /£/.test(s) ? 'GBP' : /€/.test(s) ? 'EUR' : /¥/.test(s) ? 'JPY' : 'USD';
  const searchCards = [...document.querySelectorAll('div[data-component-type="s-search-result"][data-asin]')];
  const categoryCards = [...document.querySelectorAll('.p13n-sc-uncoverable-faceout')];
  const cards = searchCards.length ? searchCards : categoryCards;
  const seen = new Set();
  const results = cards.map((card, index) => {
    const link = card.querySelector('a[href*="/dp/"],a[href*="/gp/product/"]');
    const asin = card.dataset.asin || card.getAttribute('data-asin') || link?.href.match(/\/(?:dp|gp\/product)\/([A-Z0-9]{10})/i)?.[1] || null;
    if (!asin || seen.has(asin)) return null;
    seen.add(asin);
    const text = clean(card.innerText) || '';
    const image = card.querySelector('img.s-image,img');
    const heading = card.querySelector('h2,.p13n-sc-truncate,[class*="truncate"]');
    const current = card.querySelector('.a-price:not(.a-text-price) .a-offscreen,.p13n-sc-price');
    const list = card.querySelector('.a-price.a-text-price .a-offscreen,[data-a-strike="true"] .a-offscreen');
    const rating = card.querySelector('.a-icon-alt');
    let reviewCount = null;
    for (const el of card.querySelectorAll('[aria-label]')) {
      const label = el.getAttribute('aria-label') || '';
      if (/^[\d,]+\s+ratings?$/i.test(label)) { reviewCount = integer(label); break; }
    }
    const price = num(current?.textContent), listPrice = num(list?.textContent);
    const title = clean(heading?.innerText) || clean((image?.alt || '').replace(/^Sponsored Ad\s*-\s*/i, ''));
    const sponsored = !!card.querySelector('.puis-sponsored-label-text,.s-sponsored-label-text,[aria-label*="Sponsored"],[data-component-type="sp-sponsored-result"]');
    return {
      asin, rank: searchCards.length ? null : index + 1, title,
      imageUrl: image ? (image.currentSrc || image.src || null) : null,
      price: price == null ? null : { formatted: clean(current.textContent), raw: price, currency: currency(current.textContent) },
      listPrice: listPrice == null ? null : { formatted: clean(list.textContent), raw: listPrice, currency: currency(list.textContent) },
      discountPercent: price != null && listPrice != null && listPrice > price ? Math.round((1 - price / listPrice) * 100) : null,
      rating: { stars: rating ? num(rating.textContent.split(' ')[0]) : null, reviewCount },
      primeEligible: !!card.querySelector('[data-cy="delivery-recipe"] .prime-brand-color,i.a-icon-prime') || /\bPrime\b/i.test(text),
      todaysDeal: /Today's Deal|Deal of the Day|Lightning Deal/i.test(text), sponsored,
      badges: [/Amazon's Choice/i.test(text) && "Amazon's Choice", /Best Seller/i.test(text) && 'Best Seller', /Climate Pledge Friendly/i.test(text) && 'Climate Pledge Friendly', /Today's Deal|Deal of the Day|Lightning Deal/i.test(text) && "Today's Deal"].filter(Boolean),
      url: `https://www.amazon.com/dp/${asin}`
    };
  }).filter(Boolean);
  const header = document.querySelector('[data-component-type="s-result-info-bar"],.s-breadcrumb');
  const headerText = clean(header?.innerText) || '';
  const match = headerText.match(/of\s+(over\s+)?([\d,]+)\s+results/i) || headerText.match(/([\d,]+)\s+results/i);
  const bodyText = clean(document.body?.innerText) || '';
  const noResults = /no results for|did not match any products|we couldn't find any results/i.test(bodyText) || (!!document.querySelector('h3') && results.length === 0);
  const fallback = noResults && results.length > 0;
  return { url: location.href, totalResultCount: match ? parseInt(match[match.length - 1].replace(/,/g, ''), 10) : null, resultCount: results.length, noResults, fallback, results };
})()
  1. For multiple pages, request each page={N} search URL or category pg={N} URL and deduplicate results by ASIN. Treat noResults:true with results:[] as an empty organic result; treat fallback:true as a no-match state with unrelated fallback cards, not as matching products. For exact five-star requests, use the confirmed 4 Stars & Up URL facet and post-filter returned cards to rating.stars === 5, explicitly reporting that Amazon's facet itself is not exact.

Params

ParamWhat it doesExample value
queryURL-encoded search phrasestainless steel coffee makers
departmentDepartment alias or node encoded as i= or n: refinementelectronics or n:172282
category_urlDirect Best Sellers category URLhttps://www.amazon.com/Best-Sellers-Toys-Games-Board-Games/zgbs/toys-and-games/166225011/ref=zg_bs_pg_1_toys-and-games?_encoding=UTF8&pg=1
category_nodeOpaque Best Sellers category node166225011
sortSearch sort order via s=date-desc-rank
pageSearch or category page number3
filtersComma-joined rh filter fragmentsp_36:0-1,p_72:1248879011,p_n_condition-type:2224373011
brand_filterBrand refinementp_123:237204
rating_filterConfirmed 4 Stars & Up refinement; post-filter for exact five starsp_72:1248879011
condition_filterUsed-condition refinementp_n_condition-type:2224373011
min_price_centsLower price bound encoded in p_364000
max_price_centsUpper price bound encoded in p_361
deal_filterToday's Deals refinementp_n_deal_type:23566064011
noise_cancellation_filterActive Noise Cancellation refinementp_76:1249137011
free_shipping_filterFree Shipping by Amazon refinementp_n_g-1003195531111:23746030011
asinCanonical product identifier from a result cardB08G1ZBKGP
exclude_sponsoredPost-filter cards where sponsored is truetrue

Possible Friction Points

TriggerAction
First navigation returns Amazon's transient “Something went wrong” pageReload once; if it persists, visit https://www.amazon.com/ and retry the target URL.
Robot Check or CAPTCHA appears instead of result cardsStop extraction and return a blocked result; restart with a fresh stealthed browser session rather than solving the challenge.
Direct s=date-desc-rank results look newest but Amazon's visible sort control says FeaturedKeep the direct URL and return the results; report the visible-control mismatch.
Empty or invalid query shows sponsored fallback products and no organic cardsTreat zero matching data-component-type="s-search-result" cards as no organic results and set fallback:true when unrelated cards remain.
A narrow price filter produces an Electronics no-results message with all-department fallback productsReport zero matching Electronics results, exclude fallback products, and remove or widen p_36 only if a broader search is requested.
Used-condition, rating, department, or price filter links differ between searchesRead the desired facet anchor's live href under #s-refinements and copy its current rh fragment.
Amazon exposes only 4 Stars & Up for a five-star requestUse p_72:1248879011, then post-filter cards with rating.stars === 5 and state that the site facet was not exact.
Page 2 has no result cards after a filtered page 1Return the confirmed empty page state and do not infer products from page 1 or fallback content.
Best Sellers cards are not search-result cardsRun the .p13n-sc-uncoverable-faceout branch and resolve ASINs from /dp/ links or data-asin.
Result-card count is lower than the header totalRequest subsequent page={N} or category pg={N} URLs and deduplicate by ASIN.
Sponsored cards appear among filtered or fallback resultsExclude cards with sponsored:true; never treat a sponsored fallback card as an organic match.
No explicit Prime facet appears in #s-refinementsKeep confirmed refinements, extract primeEligible, and post-filter cards whose Prime indicator is false.
Whole-page snapshot or text extraction exceeds tool limitsUse the scoped DOM evaluator above instead of snapshotting the results page.

Call it

GET https://production-sfo.browserless.io/skills?token=TOKEN-HERE&domain=amazon.com&task=search-products