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
- 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 withpg={page}and its category node. - 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. - 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 };
})()- For multiple pages, request each
page={N}search URL or categorypg={N}URL and deduplicate results by ASIN. TreatnoResults:truewithresults:[]as an empty organic result; treatfallback:trueas 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 torating.stars === 5, explicitly reporting that Amazon's facet itself is not exact.
Params
| Param | What it does | Example value |
|---|---|---|
query | URL-encoded search phrase | stainless steel coffee makers |
department | Department alias or node encoded as i= or n: refinement | electronics or n:172282 |
category_url | Direct Best Sellers category URL | https://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_node | Opaque Best Sellers category node | 166225011 |
sort | Search sort order via s= | date-desc-rank |
page | Search or category page number | 3 |
filters | Comma-joined rh filter fragments | p_36:0-1,p_72:1248879011,p_n_condition-type:2224373011 |
brand_filter | Brand refinement | p_123:237204 |
rating_filter | Confirmed 4 Stars & Up refinement; post-filter for exact five stars | p_72:1248879011 |
condition_filter | Used-condition refinement | p_n_condition-type:2224373011 |
min_price_cents | Lower price bound encoded in p_36 | 4000 |
max_price_cents | Upper price bound encoded in p_36 | 1 |
deal_filter | Today's Deals refinement | p_n_deal_type:23566064011 |
noise_cancellation_filter | Active Noise Cancellation refinement | p_76:1249137011 |
free_shipping_filter | Free Shipping by Amazon refinement | p_n_g-1003195531111:23746030011 |
asin | Canonical product identifier from a result card | B08G1ZBKGP |
exclude_sponsored | Post-filter cards where sponsored is true | true |
Possible Friction Points
| Trigger | Action |
|---|---|
| First navigation returns Amazon's transient “Something went wrong” page | Reload once; if it persists, visit https://www.amazon.com/ and retry the target URL. |
| Robot Check or CAPTCHA appears instead of result cards | Stop 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 Featured | Keep the direct URL and return the results; report the visible-control mismatch. |
| Empty or invalid query shows sponsored fallback products and no organic cards | Treat 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 products | Report 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 searches | Read 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 request | Use 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 1 | Return the confirmed empty page state and do not infer products from page 1 or fallback content. |
| Best Sellers cards are not search-result cards | Run the .p13n-sc-uncoverable-faceout branch and resolve ASINs from /dp/ links or data-asin. |
| Result-card count is lower than the header total | Request subsequent page={N} or category pg={N} URLs and deduplicate by ASIN. |
| Sponsored cards appear among filtered or fallback results | Exclude cards with sponsored:true; never treat a sponsored fallback card as an organic match. |
No explicit Prime facet appears in #s-refinements | Keep confirmed refinements, extract primeEligible, and post-filter cards whose Prime indicator is false. |
| Whole-page snapshot or text extraction exceeds tool limits | Use the scoped DOM evaluator above instead of snapshotting the results page. |