Use Etsy Product Listings & Shop Research Scraper with Python
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.
1. Choose the data you need
Review the published README for supported collection modes, examples, and limits. The recipe below uses the current published input definition.
2. Prepare a source-based input
This JSON contains published prefills and required-field defaults. Review every value for your task before running it; it is not a promise of successful collection.
{
"country": "US",
"freeShippingOnly": false,
"includeDetails": false,
"maxItems": 1,
"maxPrice": 0,
"minPrice": 0,
"searchQueries": [
"handmade jewelry"
],
"sortBy": "",
"starSellerOnly": false
}
Download input.example.json and save your reviewed copy as input.json.
| 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). |
3. Start an Actor run
Set APIFY_TOKEN in your environment. Python 3.10 or newer is sufficient; this recipe uses no third-party packages.
Execution may incur charges. Review current pricing on Apify. The request asks for a 120-second timeout and a charge limit; these settings are subject to Apify’s pricing and do not guarantee an all-inclusive spending cap.
import json
import os
import urllib.request
# Save the schema-derived example as input.json; review and edit it first.
with open("input.json", encoding="utf-8") as stream:
actor_input = json.load(stream)
token = os.environ["APIFY_TOKEN"]
request = urllib.request.Request(
"https://api.apify.com/v2/acts/B47SmyQ83rDLAqzH1/runs?timeout=120&maxTotalChargeUsd=0.50",
data=json.dumps(actor_input).encode("utf-8"),
headers={"Authorization": "Bearer " + token, "Content-Type": "application/json"},
method="POST",
)
# This starts a potentially billable run. Do not automatically retry this POST.
with urllib.request.urlopen(request, timeout=40) as response:
run = json.load(response)["data"]
print("Run ID:", run["id"])
print("Status:", run["status"])
# Inspect and download results in Apify Console once the run succeeds.
4. Inspect the actual results
Open the run in Apify Console and wait for a successful completion. Use the published output schema to locate results and the dataset schema to understand declared views. Missing or optional record fields must not be treated as guaranteed values.
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.
Questions before you run
Which inputs are required?
No top-level fields are marked required in the published schema. Conditional requirements can still apply; check the README.
What does this guide verify?
The fields, prefills, and schema links are rendered from the published build. This is a usage recipe, not a paid live extraction test. Source availability and collection results can vary.
What if the run request times out?
Check Apify Console before retrying. The first request may already have created a run. Do not automatically repeat a run-creation POST.
Generated from source build I2vzZsFaC2LGvOXoY and its README and schemas. Source changes regenerate this guide; no third-party editorial claims are inferred.