Purpose
Open an Etsy search-results page for a caller-provided query and capture a screenshot after the page and any anti-bot challenge have finished loading. Optionally extract visible listing cards from the same page, including enough results for caller-requested visual widgets such as the first 20 listings.
When to Use
Use when the caller provides an Etsy search query or search URL and wants the results page opened and screenshotted. Prefer this workflow over visiting Etsy's homepage and typing into the search box.
Workflow
- Build the direct search URL:
https://www.etsy.com/search?q={url-encoded-query}&instant_download=false. Preserve any caller-supplied Etsy search parameters when relevant; omitinstant_download=falsewhen an unfiltered search is requested. - In one browser interaction,
gotothe direct URL withwaitUntil: domcontentloaded, then solve a DataDome challenge if one is presented. Wait for the resulting navigation or page stabilization. - Capture a screenshot of the loaded Etsy search-results page. For a visual widget limited to
{limit}results, use the first{limit}extracted listing objects or cards, commonly{limit}=20. - If structured results are also requested, run this evaluator on the current page after challenge resolution and loading:
(() => {
const seen = new Set();
const text = el => el?.textContent?.replace(/\\s+/g, ' ').trim() || null;
return [...document.querySelectorAll('a[href*="/listing/"]')]
.map(a => {
const href = a.href;
const id = a.getAttribute('data-listing-id') ||
a.closest('[data-listing-id]')?.getAttribute('data-listing-id') ||
(href.match(/\\/listing\\/(\\d+)/) || [])[1] || null;
const card = a.closest('[data-listing-id], li, article, [role="listitem"]') || a;
const title = a.getAttribute('aria-label') ||
text(a.querySelector('h3, h2, [data-listing-title]')) || text(a);
const price = text(card.querySelector('[data-price], .currency-value, p[class*="price"], [aria-label*="price" i]'));
if (!id || seen.has(id)) return null;
seen.add(id);
return { id, title, price, url: href };
})
.filter(Boolean);
})()Site-Specific Gotchas
- Etsy may interpose a DataDome anti-bot challenge. Run the site's DataDome solver before taking the screenshot or extracting results; do not treat the challenge page as the search result.
- Search results are directly addressable at
/search?q={query}.instant_download=falseis an optional modifier that excludes instant-download results; omit it for an unfiltered search when appropriate. - Preserve caller-supplied parameters such as
refandas_prefixwhen they affect the requested Etsy search state. - Listing-card markup can vary by experiment and localization. The evaluator deduplicates by listing ID and falls back to the
/listing/{id}URL, but title and price selectors may be absent on some cards. - Wait for post-challenge navigation or stabilization before capturing the screenshot, since the initial DOM may only contain the challenge shell.
- A plain search URL does not guarantee Etsy's “Bestseller” designation or ranking; only report bestseller status when it is visibly present in the loaded card markup.
Expected Output
A screenshot of the loaded Etsy search-results page, plus an optional array of {id, title, price, url} objects when structured extraction is requested. When the caller requests a fixed count, return up to that many unique visible listings, such as the first 20 for a visual widget.