1.What teams extract from Etsy
Decide your schema from the business question first. Price monitors need the current price, currency, any sale/original price, and variation-level pricing. Product researchers want title, description, materials, tags, category, and image URLs. Reputation and demand analysts pull the shop's review count, average rating, number of sales, and individual review text with dates.
Etsy prices are variation-dependent. a listing can have multiple options (size, color, personalization) with different prices. Capture the base price and the variation matrix separately so your dataset stays accurate rather than storing a single misleading number.
- Listing title, description, and category breadcrumb
- Current price, currency, and sale vs original price
- Variations (size, color, etc.) and per-variation price where shown
- Shop name, shop sales count, and shop-level rating
- Listing review count, average star rating, and individual reviews
- Materials, tags, processing time, and shipping origin
- Main image URL and gallery image URLs; "bestseller" / "star seller" badges
2.Etsy URL patterns
Listing pages are keyed by a numeric listing ID and are the most stable entry point. any slug variation redirects to the canonical listing URL, so the bare listing ID form is enough. Shop pages and search are useful for discovery but search triggers bot scoring the fastest, so use it sparingly and refresh individual listings on a schedule.
- Listing (bare ID): https://www.etsy.com/listing/1234567890
- Listing (with slug, redirects): https://www.etsy.com/listing/1234567890/handmade-mug
- Shop page: https://www.etsy.com/shop/<ShopName>
- Shop reviews: https://www.etsy.com/shop/<ShopName>/reviews
- Search (higher detection risk): https://www.etsy.com/search?q=ceramic+mug&page=2
3.Where the data lives in Etsy HTML
Check the application/ld+json Product schema block first. Etsy embeds structured data for its own SEO, and name, image, offers.price, offers.priceCurrency, and aggregateRating are frequently present. This is the most redesign-resistant source for price and rating, so use it as your primary and treat visible-DOM selectors as a fallback.
In the visible DOM, the price commonly renders inside a data-testid or data-buy-box price wrapper. anchor on data attributes and text rather than generated class names. Variations live in <select> elements or option buttons within the buy box. Reviews are paginated and often load additional pages via XHR, so deep review collection may need JavaScript rendering or capturing the review XHR responses.
4.Structured extraction with OmniScrape
The cleanest way to scrape an Etsy listing is to let OmniScrape return structured JSON. Send the listing URL with output_format: css_extractor and a css_selectors map. mode:auto tries the fast lane first and escalates to a browser only if Etsy's protection requires it, so you only pay for a browser when it is actually needed.
Always validate that required fields (price, title) come back non-empty. If price is empty, fall back to parsing the JSON-LD block from the raw HTML, which is more stable than any single DOM selector.
1234567891011121314151617181920212223import os, requests
resp = requests.post(
"https://api.omniscrape.io/v1/scrape",
headers={"X-API-Key": os.environ["OMNISCRAPE_KEY"]},
json={
"url": "https://www.etsy.com/listing/1234567890",
"mode": "auto",
"output_format": "css_extractor",
"proxy": "residential:us",
"css_selectors": {
"title": "h1[data-buy-box-listing-title]",
"price": "[data-buy-box-region='price'] .currency-value",
"shop": "a[href*='/shop/'] span",
"rating": "input[name='rating']",
"reviews": "[data-reviews-total-count]",
},
},
timeout=120,
)
body = resp.json()
data = body["data"]["css_extracted"] if body["success"] else {}
print(data)
5.JSON-LD fallback for price and rating
When selectors return empty. usually after an A/B test or redesign. parse the Product schema from the raw HTML. Request output_format:html, find the ld+json script with @type Product, and read offers.price and aggregateRating. This keeps your pipeline resilient when the visible DOM shifts.
12345678910111213141516171819import json, re, requests, os
resp = requests.post(
"https://api.omniscrape.io/v1/scrape",
headers={"X-API-Key": os.environ["OMNISCRAPE_KEY"]},
json={"url": "https://www.etsy.com/listing/1234567890", "mode": "auto",
"output_format": "html", "proxy": "residential:us"},
timeout=120,
)
html = resp.json()["data"]["content"]
for block in re.findall(r'<script type="application/ld\+json">(.*?)</script>', html, re.S):
try:
obj = json.loads(block)
except json.JSONDecodeError:
continue
if obj.get("@type") == "Product":
print("price:", obj.get("offers", {}).get("price"))
print("rating:", obj.get("aggregateRating", {}).get("ratingValue"))
6.Collecting reviews at depth
The first page of reviews is in the listing HTML, but older reviews load via pagination or XHR. For a handful of recent reviews, the rendered listing page is enough. For full review history, use Browser-as-a-Service to page through the reviews, or enable capture_xhr to grab the review fetch responses directly instead of parsing the DOM.
7.Handling Etsy's anti-bot and rate limits
Etsy scores requests on IP reputation, request cadence, and fingerprint, and it rate-limits search heavily. Datacenter IPs get challenged quickly. Route through residential IPs, match the storefront locale with the right proxy country (Etsy localizes price and currency), keep cadence reasonable, and refresh listings on a schedule instead of bursting search pages.
OmniScrape handles fingerprinting and IP rotation; you keep volume sane and back off on any block signal. See web scraping without getting blocked for the general approach.
8.Scrape Etsy responsibly
Collect public listing and shop data, not personal information about buyers or sellers beyond what is publicly displayed. Review Etsy's Terms of Use and robots.txt, rate-limit yourself, and cache results rather than re-fetching. Responsible collection keeps your access sustainable. OmniScrape provides infrastructure; compliant, ethical use is your responsibility.
Frequently asked questions
How do I scrape product data from Etsy?
Target the listing URL by its numeric ID, send it to OmniScrape with output_format:css_extractor and selectors for title, price, shop, and rating, and validate the fields. Fall back to the JSON-LD Product block for price and rating when selectors return empty after a layout change.
Why does my Etsy scraper get blocked?
Etsy detects automation via IP reputation, request cadence, and browser fingerprint, and it rate-limits search aggressively. Use residential proxies, match the storefront country, keep cadence human, and avoid bulk search scraping. OmniScrape's mode:auto handles the fingerprint and challenge layer for you.
How do I get Etsy prices for product variations?
Capture the base price plus the variation matrix separately. variations live in select/option elements in the buy box, and per-variation prices appear when an option is selected. For dynamic variation pricing, render the page with js_rendering or read the embedded listing JSON.
Can I scrape Etsy reviews?
Recent reviews are in the rendered listing HTML. For full history, use Browser-as-a-Service to page through reviews, or enable capture_xhr to collect the review fetch responses directly rather than parsing the DOM.
Does Etsy have an official API?
Yes, Etsy has an Open API for approved use cases, which is the supported path when it fits your needs. Reserve scraping for public competitor and market data the API does not expose, and always within Etsy's Terms of Use.
Related guides