Etsy Product Listings & Shop Research Scraper
Collect Etsy product listings from keyword searches, category pages, and individual shops, with prices, ratings, seller badges, and optional full product detail across ten regional markets.
Input schema
Input fields, types, required flags, descriptions, defaults, and prefills come from the published definition. Prefill examples are distinct from defaults.
| Field | Type | Required | Description |
|---|---|---|---|
startUrls | array | optional | Etsy market URLs such as https://www.etsy.com/market/handmade_jewelry are the reliable route. Category URLs such as https://www.etsy.com/c/jewelry also work and expose more cards per page. Legacy /search?q= URLs are converted to the matching market URL automatically. Shop and individual listing URLs are usually blocked by Etsy's DataDome protection; they are accepted for backward compatibility and reported as blocked instead of silently returning nothing. |
searchQueries | array | optional | Keywords to search on Etsy. Each query is mapped to Etsy's market page (https://www.etsy.com/market/<slug>), the route that works without a browser or login. If Etsy redirects a keyword to a category page, the actor follows that category route. Each query is scraped as a separate task. |
maxItems | integer | optional | Maximum number of listings to collect per URL or search query. A keyword market exposes at most 25 pages (about 200 listings); category URLs can expose more pages. Larger values return what Etsy exposes and report a partial result instead of pretending the request was complete. |
Show all input fields
| Field | Type | Required | Description |
|---|---|---|---|
startUrls | array | optional | Etsy market URLs such as https://www.etsy.com/market/handmade_jewelry are the reliable route. Category URLs such as https://www.etsy.com/c/jewelry also work and expose more cards per page. Legacy /search?q= URLs are converted to the matching market URL automatically. Shop and individual listing URLs are usually blocked by Etsy's DataDome protection; they are accepted for backward compatibility and reported as blocked instead of silently returning nothing. |
searchQueries | array | optional | Keywords to search on Etsy. Each query is mapped to Etsy's market page (https://www.etsy.com/market/<slug>), the route that works without a browser or login. If Etsy redirects a keyword to a category page, the actor follows that category route. Each query is scraped as a separate task. |
maxItems | integer | optional | Maximum number of listings to collect per URL or search query. A keyword market exposes at most 25 pages (about 200 listings); category URLs can expose more pages. Larger values return what Etsy exposes and report a partial result instead of pretending the request was complete. |
includeDetails | boolean | optional | Attempts to open each listing page for description, materials, tags, variations, images, and seller details. Etsy often blocks listing pages with DataDome. When that happens, rows are still returned with detailsStatus set to 'blocked' and the unavailable detail fields omitted; they are never filled with placeholders. |
sortBy | string | optional | Server-side Etsy ordering for market and category pages. |
minPrice | number | optional | Drop listings below this price in the selected market currency. Etsy does not expose a reliable server-side price filter on market pages, so this is applied to fetched cards. 0 disables the lower bound. |
maxPrice | number | optional | Drop listings above this price in the selected market currency. 0 disables the upper bound. |
freeShippingOnly | boolean | optional | Use Etsy's free-shipping market filter. |
starSellerOnly | boolean | optional | Use Etsy's Star Seller market filter. |
country | string | optional | Target Etsy market. Sets the market URL locale and request language, so prices and ranking match the selected region (for example Germany returns EUR and Japan returns JPY). |
Download the published input schema
View complete JSON
{
"properties": {
"country": {
"default": "US",
"description": "Target Etsy market. Sets the market URL locale and request language, so prices and ranking match the selected region (for example Germany returns EUR and Japan returns JPY).",
"editor": "select",
"enum": [
"US",
"DE",
"GB",
"FR",
"AU",
"CA",
"IT",
"ES",
"NL",
"JP"
],
"enumTitles": [
"United States (USD)",
"Germany (EUR)",
"United Kingdom (GBP)",
"France (EUR)",
"Australia (AUD)",
"Canada (CAD)",
"Italy (EUR)",
"Spain (EUR)",
"Netherlands (EUR)",
"Japan (JPY)"
],
"prefill": "US",
"title": "Country / Market",
"type": "string"
},
"freeShippingOnly": {
"default": false,
"description": "Use Etsy's free-shipping market filter.",
"prefill": false,
"title": "Free shipping only",
"type": "boolean"
},
"includeDetails": {
"default": false,
"description": "Attempts to open each listing page for description, materials, tags, variations, images, and seller details. Etsy often blocks listing pages with DataDome. When that happens, rows are still returned with detailsStatus set to 'blocked' and the unavailable detail fields omitted; they are never filled with placeholders.",
"prefill": false,
"title": "Include Listing Details",
"type": "boolean"
},
"maxItems": {
"default": 100,
"description": "Maximum number of listings to collect per URL or search query. A keyword market exposes at most 25 pages (about 200 listings); category URLs can expose more pages. Larger values return what Etsy exposes and report a partial result instead of pretending the request was complete.",
"editor": "number",
"maximum": 1000,
"minimum": 1,
"prefill": 1,
"title": "Max Items",
"type": "integer"
},
"maxPrice": {
"default": 0,
"description": "Drop listings above this price in the selected market currency. 0 disables the upper bound.",
"editor": "number",
"maximum": 1000000,
"minimum": 0,
"prefill": 0,
"title": "Maximum price",
"type": "number"
},
"minPrice": {
"default": 0,
"description": "Drop listings below this price in the selected market currency. Etsy does not expose a reliable server-side price filter on market pages, so this is applied to fetched cards. 0 disables the lower bound.",
"editor": "number",
"maximum": 1000000,
"minimum": 0,
"prefill": 0,
"title": "Minimum price",
"type": "number"
},
"searchQueries": {
"description": "Keywords to search on Etsy. Each query is mapped to Etsy's market page (https://www.etsy.com/market/<slug>), the route that works without a browser or login. If Etsy redirects a keyword to a category page, the actor follows that category route. Each query is scraped as a separate task.",
"editor": "stringList",
"prefill": [
"handmade jewelry"
],
"title": "Search Queries",
"type": "array"
},
"sortBy": {
"default": "",
"description": "Server-side Etsy ordering for market and category pages.",
"editor": "select",
"enum": [
"",
"most_relevant",
"price_asc",
"price_desc",
"date_desc",
"highest_reviews"
],
"enumTitles": [
"Most relevant",
"Price: low to high",
"Price: high to low",
"Newest",
"Most reviewed"
],
"prefill": "",
"title": "Sort results by",
"type": "string"
},
"starSellerOnly": {
"default": false,
"description": "Use Etsy's Star Seller market filter.",
"prefill": false,
"title": "Star Seller only",
"type": "boolean"
},
"startUrls": {
"description": "Etsy market URLs such as https://www.etsy.com/market/handmade_jewelry are the reliable route. Category URLs such as https://www.etsy.com/c/jewelry also work and expose more cards per page. Legacy /search?q= URLs are converted to the matching market URL automatically. Shop and individual listing URLs are usually blocked by Etsy's DataDome protection; they are accepted for backward compatibility and reported as blocked instead of silently returning nothing.",
"editor": "requestListSources",
"title": "Etsy URLs",
"type": "array"
}
},
"required": [],
"schemaVersion": 1,
"title": "Etsy Scraper Input",
"type": "object"
}
Output schema
Published result links and storage locations. This schema is not an exhaustive contract for every field in a dataset record.
| Field | Type | Required | Description |
|---|---|---|---|
listings | string | optional | Dataset containing all scraped Etsy product listings. |
Download the published output schema
View complete JSON
{
"actorOutputSchemaVersion": 1,
"description": "Dataset of Etsy listing records collected from keyword market and category pages, with ISO currency codes, numeric prices, shop-level ratings, badges, provenance fields, and optional listing detail.",
"properties": {
"listings": {
"description": "Dataset containing all scraped Etsy product listings.",
"template": "{{links.apiDefaultDatasetUrl}}/items",
"title": "Etsy Listings",
"type": "string"
}
},
"title": "Etsy Product Listings & Shop Research Scraper Output"
}
Dataset schema
Published dataset views and any declared field definitions. A view can select only a subset of the returned record fields.
Download the published dataset schema
View complete JSON
{
"actorSpecification": 1,
"views": {
"overview": {
"display": {
"component": "table",
"properties": {
"currencyCode": {
"format": "text",
"label": "Currency"
},
"isAd": {
"format": "boolean",
"label": "Ad"
},
"priceValue": {
"format": "number",
"label": "Price"
},
"rating": {
"format": "text",
"label": "Shop rating"
},
"reviewCount": {
"format": "text",
"label": "Shop reviews"
},
"shopName": {
"format": "text",
"label": "Shop"
},
"title": {
"format": "text",
"label": "Title"
},
"url": {
"format": "link",
"label": "URL"
}
}
},
"title": "Overview",
"transformation": {
"fields": [
"title",
"priceValue",
"currencyCode",
"shopName",
"rating",
"reviewCount",
"isAd",
"url"
]
}
}
}
}
Key-value schema
Published key-value store definitions, when provided by this build.
This definition is not present in the default published build. See the published README for available documentation.
Published README
Read the full published documentation
Etsy Product Listings & Shop Research Scraper
Collect Etsy listing data from Etsy's keyword market pages without a login, API key, or proxy setup. Give the actor a keyword or a market URL, choose one of ten regional markets, and get structured rows for pricing research, assortment tracking, and shop comparisons.
How it works
Etsy's keyword market pages (https://www.etsy.com/market/<slug>) are the most reliable public route. The actor converts every search query into that route, reads the page's JSON-LD ItemList and listing cards, and follows pages until it reaches your limit or the source ends. When Etsy redirects a keyword to a category page, the actor follows the redirect with a fresh request instead of treating the redirect as a dead end.
Category URLs such as https://www.etsy.com/c/jewelry are also supported directly and normally expose about 64 cards per page. Legacy /search?q= URLs are converted to the equivalent market URL. Shop and individual listing URLs are accepted for backward compatibility, but Etsy protects those with DataDome; the actor reports them as blocked instead of returning an empty result.
What you get per listing
Card-level fields:
listingId- numeric Etsy listing IDurl- listing page URLtitle- listing titleprice,originalPrice- plain decimal strings (no thousands separators)priceValue,originalPriceValue- numeric values for calculationscurrency- Etsy's display label or symbol (USD,EUR,GBP,JPY,AU$,CA$)currencyCode- ISO 4217 code (USD,EUR,GBP,JPY,AUD,CAD, ...)discountPercent- discount as a string, kept for compatibilitydiscountPercentValue- discount as an integer percentageimageUrl- primary card imageimageCount- number of images exposed in the card (usually 1)shopId- Etsy shop ID when the card exposes itshopName,shopUrl- shop name and URL (from the page's brand data or shop link)rating,reviewCount- shop-level rating and review count when the card shows themshopRating,shopReviewCount- numeric shop-level copies of the same valuesratingScope-shopwhen the supplied rating is shop-levellistingRating,listingReviewCount- listing-level rating only when the page's structured data suppliesaggregateRating(usually it does not)availability- stock status from the page's structured databadges- compatibility object withstarSeller,freeShipping,bestseller,etsyChoice,adisAd- present andtruefor sponsored cards (omitted for organic cards)freeShipping,starSeller,bestseller,etsyChoice- present andtruewhen the badge is visiblehasVariations- present andtruewhen variations are visiblepage- source market or category page numberposition- row position within the task's collected resultssourceQuery- keyword or slug that produced the rowsourceUrl- exact market or category page URL the row came fromrecordType-listingfor card recordsscrapedAt- ISO 8601 collection timestamp
Fields the page does not expose are omitted rather than emitted as empty strings, nulls, or placeholders. isAd, badge flags, and hasVariations are only present when true; absence means the page did not show that signal. rating is a shop-level rating on market cards, which is why ratingScope is shop.
When includeDetails is enabled
The actor attempts to open each listing page and add:
description,category,materials,listedDate,availabilityimages- all listing image URLsvariations- selectable optionstags- listing tagssellerName,sellerRating,sellerReviewCount,sellerLocationlistingRating,listingReviewCount- listing-level rating when the listing page supplies itdetailsStatus-enriched,blocked,not_found,partial, orno_url
Etsy frequently blocks listing pages with DataDome. When that happens, the row keeps its card data, detailsStatus is blocked, and no detail fields are invented. A circuit breaker stops detail attempts after three consecutive blocks so a site-wide block cannot stall the run.
Input
At least one of startUrls or searchQueries is required for a normal run. A truly empty input uses the documented handmade jewelry fallback for the scheduled test. Apify's validator cannot express "one of", so the actor enforces it.
| Field | Type | Default | Description |
|---|---|---|---|
startUrls |
array | - | Etsy market URLs are reliable; /c/ category URLs work and return more cards per page. Legacy /search?q= URLs are converted to market URLs. Shop URLs use the 10-item public RSS feed; listing URLs are reported as blocked. |
searchQueries |
array | - | Keywords mapped to Etsy market pages, with automatic category follow when Etsy redirects. Each query is a separate task. |
maxItems |
integer | 100 |
Maximum rows per URL or query (1-1,000). Keyword markets cap at 25 pages; categories can go deeper. |
includeDetails |
boolean | false |
Attempt listing-page enrichment and report detailsStatus honestly. |
sortBy |
select | any | most_relevant, price_asc, price_desc, date_desc, highest_reviews. |
minPrice |
number | - | Lower price bound in the market currency. Etsy min is used server-side and a local safeguard follows. |
maxPrice |
number | - | Upper price bound in the market currency. |
freeShippingOnly |
boolean | false |
Use Etsy's free_shipping=1 market filter. |
starSellerOnly |
boolean | false |
Use Etsy's is_star_seller=1 market filter. |
country |
string | US |
Market locale: US, DE, GB, FR, AU, CA, IT, ES, NL, JP. |
If no input is supplied, the actor uses handmade jewelry as a safe fallback so a scheduled test run still produces data. At least one of startUrls or searchQueries is normally provided.
Examples
Keyword search in the default US market:
{
"searchQueries": ["handmade jewelry"],
"maxItems": 50
}
Multi-page collection in Germany:
{
"searchQueries": ["keramik tasse"],
"country": "DE",
"maxItems": 120
}
Direct market URL:
{
"startUrls": [{ "url": "https://www.etsy.com/uk/market/wedding_ring" }],
"maxItems": 80
}
Category URL with 64 cards per page:
{
"startUrls": [{ "url": "https://www.etsy.com/c/jewelry/necklaces" }],
"maxItems": 200
}
Details requested, with honest fallback:
{
"searchQueries": ["personalized necklace"],
"includeDetails": true,
"maxItems": 20
}
Pagination, deduplication, and limits
- Pages are followed until
maxItemsis reached, no new listing IDs appear for two consecutive pages, or Etsy's explicitmax_page_exceededmarker is hit. - A short page is not treated as the end. Etsy market pages normally show about eight cards, and category pages about 64; the old "fewer than 64 cards means stop" shortcut would have stopped after page one.
- Rows are deduplicated by listing ID across pages and across tasks; sponsored cards can repeat.
- Etsy serves at most 25 market pages per query (roughly 200 listings). Larger
maxItemsvalues return what exists and set a partial status message instead of pretending the request was complete. - Category pages can be deeper, but DataDome may rate-limit very deep or rapid page walks. The actor retries, then reports a partial result with the reason.
- Some keywords have no market page, return 404, or redirect to a category page. The actor tries alternate slug spellings, follows a working category redirect, and reports the specific reason for any task that still fails.
FAQ
Do I need an Etsy account, API key, or proxy? No. The actor reads public market pages and works without credentials or proxy configuration. It chooses its proxy automatically and only after a direct request is blocked.
Why do I sometimes get fewer rows than maxItems?
Etsy's market route exposes up to 25 pages per query. The actor reports the ceiling and returns the available rows. Shop RSS is limited to 10 recent items. Category pages can go deeper but may rate-limit.
Which filters and sorts work?
sortBy, freeShippingOnly, starSellerOnly, minPrice and maxPrice are supported. Sorting and the two badges are server-side; price bounds use Etsy's min/max parameters and a local safeguard. Views, favorites, carts and bought-in-last-24h are not present on market/category pages; listing pages expose some of them only through a browser, so they are not shipped.
What happens with shop and listing URLs?
Shop URLs use Etsy's public shop RSS feed, which returns the 10 most recent listings with title, description, image and price. It is honest about being a recent-items feed and does not paginate. Listing detail pages remain DataDome-blocked for plain HTTP; browser/proxy enrichment is not used in production because it is slow and unreliable. Keyword market pages and /c/ category pages are the reliable bulk routes.
Why is ratingScope set to shop?
The rating shown on an Etsy listing card is the shop's aggregate rating. This was verified by finding the same shop's separate listings carrying an identical rating and review count. Listing-level ratings are only claimed through listingRating when structured data on the page supplies them, or through includeDetails when the listing page is readable.
Why are some shop names or ratings missing? Etsy's card markup does not always expose a shop name or rating. The actor fills them from the page's JSON-LD brand and visible rating controls, and omits them when the page genuinely does not show them. Missing fields are never replaced with placeholders.
Are prices strings or numbers?
Both are provided. price and originalPrice remain decimal strings for backward compatibility; priceValue and originalPriceValue are numeric for calculations.
Why do output listing/shop URLs return 403? Etsy's DataDome protection blocks clean clients even for URLs the actor collected from public market/RSS pages. The URLs are valid for a browser; this is an Etsy anti-bot caveat, not a broken URL. Image URLs were verified HTTP 200.
Is this affiliated with Etsy? No. This is an independent third-party actor that reads publicly visible pages and is not affiliated with or endorsed by Etsy, Inc.
Python quickstart
Use a schema-derived input and the Python standard library to start a run. An Apify account and token are required for execution.
Source build I2vzZsFaC2LGvOXoY. Documentation is regenerated from the default published build; private drafts are not included.