# Etsy Scraper — Search, Categories, Prices, Sales & Ads (`automation-lab/etsy-scraper`) Actor

Scrape Etsy search results and category pages: titles, prices, sale discounts, shops, ratings, review counts, free shipping and Etsy Ads flags. Several keywords per run, large searches split by price. From $0.0027 per listing.

- **URL**: https://apify.com/automation-lab/etsy-scraper.md
- **Developed by:** [Automation Lab](https://apify.com/automation-lab) (community)
- **Categories:** E-commerce, Lead generation
- **Features:** Limited permissions, Multi-tenant, Server interface
- **Stats:** 1,252 total users, 166 monthly users, 99.6% runs succeeded, 7 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $1.95 / 1,000 product scrapeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Etsy Scraper

Scrape [Etsy](https://www.etsy.com) product listings for handmade, vintage, and creator-marketplace research. Search by keyword or paste category URLs, and get titles, prices, sale discounts, shops, exact ratings, review counts, free shipping and Etsy Ads flags — no API keys, no login, no manual copying. Prices start at $0.0027 per listing on the Starter plan.

This actor searches Etsy through query-specific market URLs, bypassing DataDome anti-bot protection that blocks standard `/search` endpoints. It uses lightweight HTTP requests with Cheerio (no browser) and parses structured data and listing cards embedded in Etsy pages, giving you clean, structured product data in seconds.

### What does Etsy Scraper do?

**Etsy Scraper** extracts product listing data from [Etsy](https://www.etsy.com) search results and category pages. Use it for handmade and vintage marketplace price monitoring, competitor listing research, ad tracking and niche assortment tracking. You provide one or more search queries (like `handmade leather wallet` or `vintage engagement ring`) or Etsy category URLs, and the scraper returns structured data for matching products — ready to export in JSON, CSV, or Excel.

🔍 **Search many Etsy queries in one run** — from `boho wedding decor` to `custom pet portrait`, or paste `/c/` category and `/market/` URLs
📦 **Large searches, no duplicates** — Etsy shows about 200 listings per search view, so bigger requests are read across price ranges, reaching 1,500+ listings for a popular search
📣 **See who is advertising** — `isAd` marks paid Etsy Ads placements on each row
🏷️ **Filter by price, shipping, and sale status** — you only pay for rows that match
⚡ **Fast and lightweight** — HTTP-only with Cheerio, no browser overhead
🛡️ **Bypasses DataDome** — uses query-specific market URL patterns that avoid anti-bot blocks
💰 **Pay per result** — from $0.0027 per product on the Starter plan, no monthly subscription

### Who is it for?

**🛒 Handcraft sellers and Etsy shop owners**
You're running your own Etsy shop and need competitive intelligence to price and position your products.

- Monitor competitor pricing in your niche (jewelry, decor, apparel, etc.)
- Track which keywords and product titles competitors are ranking for
- Identify pricing sweet spots and gaps in the market

**📊 Dropshippers and product sourcers**
You're building a store and looking for trending handmade or vintage products to resell.

- Find top-selling products by scraping with `sort: recently_listed` or `sort: most_relevant`
- Identify prolific shops to approach as suppliers
- Track which product niches have the best sale-to-stock ratio

**📈 Market researchers and competitive analysts**
You're studying Etsy as a handmade/vintage marketplace — pricing trends, niche demand, or market saturation.

- Extract pricing distributions across Etsy niches and specific search terms
- Measure listing density and competition for handmade, vintage, digital, or craft-supply searches
- Build time-series datasets by scheduling daily runs

**🤖 Data engineers and developers**
You need Etsy product data piped into your application, database, or analytics pipeline.

- Integrate via Apify API with Node.js, Python, or cURL
- Schedule automated daily or weekly runs without writing any scraping code
- Feed results into Google Sheets, Airtable, Notion, or any webhook endpoint

### Why use Etsy Scraper?

🚫 **No Etsy API limitations** — Etsy's official API requires OAuth approval, has strict rate limits, and restricts access to search results. This scraper extracts the same data directly from public pages with no API key needed.

⚙️ **No browser required** — unlike Playwright-based scrapers, Etsy Scraper uses pure HTTP requests with Cheerio parsing. Lower compute costs, faster execution, and higher reliability.

🛡️ **DataDome bypass built in** — Etsy's `/search` endpoint is protected by DataDome bot detection. This scraper routes through query-specific `/market/{query}` paths that return product data without triggering blocks.

📋 **Structured extraction** — product data comes from Google-structured LD+JSON and Etsy's listing cards together, so a page yields every listing it shows.

🔄 **Automatic retry and rotation** — failed requests are retried with fresh residential proxy sessions, and a stalled request is cut off instead of holding up the run.

📝 **An honest result** — every run ends with a status message such as "Got 100 of 100 products", or how many you got and why it stopped.

🌐 **Small footprint** — an HTTP-only actor with minimal memory use, keeping runs fast.

💵 **Transparent pay-per-result pricing** — you pay only for products actually delivered, plus a tiny startup fee charged with the first row. A search with no results costs nothing.

### What data can you extract from Etsy?

Each scraped product includes base fields covering listing identity, pricing, seller info, shipping status, and product type. Enable `enrichListingDetails` to append nullable listing-page intelligence fields:

| Field | Type | Description |
|-------|------|-------------|
| `listingId` | String | Unique Etsy listing identifier |
| `name` | String | Product title as shown on Etsy |
| `url` | String | Direct link to the product listing |
| `imageUrl` | String | Main product image URL |
| `shop` | String | Seller shop name |
| `shopId` | String | Unique shop identifier |
| `price` | String | Current listing price in the selected currency |
| `originalPrice` | String | Pre-sale price (if discounted), else `null` |
| `discountPercent` | Number/null | Sale discount shown on the card, for example `25` |
| `currency` | String | Price currency code (e.g., USD, EUR, AUD) |
| `onSale` | Boolean | Whether the item is currently on sale |
| `freeShipping` | Boolean | Whether free shipping is offered |
| `shipsFrom` | String/null | Origin shown on the card ("Ships from GA"), when Etsy shows it |
| `isAd` | Boolean | Whether the card is a paid Etsy Ads placement |
| `rating` | Number | Exact star rating shown on the card (for example `4.84`), `null` if unrated |
| `reviewCount` | Number/null | Review count shown beside the search-card rating, `null` when unavailable |
| `availability` | String | Stock status (e.g., "In Stock") |
| `isDigitalDownload` | Boolean | Whether the listing is a digital download (e.g., printable, STL, pattern) |
| `hasVariations` | Boolean | Whether the listing appears to have selectable variants |
| `priceMayBeMinimumVariant` | Boolean | Whether the shown price appears to be the lowest variant price (for example Etsy's "from $12" cue) |
| `variantDetectionStatus` | String | `listing_page` when confirmed from the listing page, otherwise `search_card_fallback` |
| `detailEnrichmentStatus` | String | `enriched` or `unavailable`; present only when detail enrichment is enabled |
| `sellerSales` | Number/null | Public seller sales count when available |
| `sellerReviewCount` | Number/null | Public seller review count when available |
| `inCartCount` | Number/null | Current public in-cart social-proof count when available |
| `boughtInLast24Hours` | Number/null | Public recent-purchase count when available |
| `tags` | Array/null | Listing tags when available |
| `materials` | Array/null | Listed materials when available |
| `shipping` | String/null | Public delivery/shipping text when available |
| `personalization` | String/null | Personalization instructions when available |
| `position` | Number | Result position in search results |
| `query` | String | The search query or category that produced this listing |
| `page` | Number | Results page this listing came from (within its price range on large searches) |
| `scrapedAt` | String | ISO 8601 timestamp of extraction |

### How much does it cost to scrape Etsy listings?

Etsy Scraper uses **pay-per-event** pricing — you pay only for what you scrape. No monthly subscription. All platform costs are included.

For large scrapes, set **Maximum charge per run** in Apify before starting. The actor checks the limit before every charge and stops once it is reached. The per-run fee is charged with the first delivered row, so a search with no results costs nothing.

| | FREE | BRONZE | SILVER | GOLD | PLATINUM | DIAMOND |
|---|---|---|---|---|---|---|
| **Per run** | $0.005 | $0.00475 | $0.00425 | $0.00375 | $0.003 | $0.0025 |
| **Per product** | $0.003 | $0.0027 | $0.0024 | $0.00195 | $0.0015 | $0.0012 |
| **1,000 products** | $3.005 | $2.705 | $2.405 | $1.954 | $1.503 | $1.203 |

Tier discounts are applied automatically based on your Apify subscription plan. Higher tiers receive lower per-product rates.

From November 22, 2026 the price will be $0.00475 per run on every plan, and per product $0.0027 on FREE and BRONZE, $0.00243 on SILVER and $0.00216 on GOLD, PLATINUM and DIAMOND.

**Cost examples (BRONZE plan):**

| Use case | Products | Cost |
|---|---|---|
| Quick niche check | 20 | ~$0.06 |
| Single niche query | 64 | ~$0.18 |
| Competitor analysis | 200 | ~$0.55 |
| Market research snapshot | 500 | ~$1.36 |
| Large-scale extraction | 1,000 | ~$2.71 |

### How to scrape Etsy products step by step

1. **Go to [Etsy Scraper](https://apify.com/automation-lab/etsy-scraper) on Apify Store** and click **"Try for free"**. Sign up for an Apify account if you don't have one — the free tier includes $5 of monthly usage.

2. **Enter your search query** in the "Search Query" field. Use the same terms you'd type into Etsy's search bar, for example: `handmade leather journal`, `vintage gold ring`, or `custom pet portrait`.

3. **Use a specific query** that includes the niche you need, such as `handmade silver necklace` or `personalized wedding gift`. Etsy market pages use the query itself for relevance.

4. **Set the maximum number of products** you want per search. The default is 64. Etsy shows about 200 listings per search view, so large requests are read across price ranges and reach 1,500+ for a popular search; the run's status message tells you how many you got.

5. **Apply optional filters** — set a price range with `minPrice` / `maxPrice`, restrict to free shipping only, or filter to on-sale items only. Choose a sort order: Most Relevant, Lowest Price, Highest Price, or Recently Listed. On large requests the sort holds within each price range, not across the whole run: Etsy sorts about 100 listings per view, so a Highest Price run steps down through price ranges, and a price sort delivers about 100 listings per range.

6. **Configure proxy settings.** Residential proxy is pre-selected and recommended. Etsy prices follow the proxy's country, so when you pick another currency the scraper exits from a country that uses it (for example Germany for EUR) and the log says so.

7. **Click "Start"** and wait for the run to complete. A typical 64-product run finishes in under 30 seconds.

8. **Download your data** from the Dataset tab in JSON, CSV, Excel, or other formats. You can also connect results directly to Google Sheets, Slack, Airtable, or any webhook endpoint.

**Example input for a basic search:**

```json
{
    "searchQuery": "handmade necklace",
    "maxItems": 100,
    "sort": "most_relevant"
}
```

**Example input for sale + free shipping filter:**

```json
{
    "searchQuery": "vintage leather bag",
    "maxItems": 200,
    "sort": "lowest_price",
    "onSale": true,
    "freeShipping": true,
    "maxPrice": 80,
    "enrichListingDetails": true
}
```

### Input parameters

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `searchQuery` | String | One of the three | — | What to search for on Etsy (e.g., `handmade necklace`) |
| `searchQueries` | Array | One of the three | — | Several searches in one run. A listing found twice is saved and charged once |
| `startUrls` | Array | One of the three | — | Etsy category (`/c/jewelry/necklaces`), market (`/market/ceramic_mug`) or search URLs. Single listing and shop URLs are not supported |
| `maxItems` | Integer | No | 64 | Maximum products per search or URL (1–5,000). Etsy's own limits may end a search sooner |
| `sort` | String | No | `most_relevant` | Sort order: `most_relevant`, `lowest_price`, `highest_price`, `recently_listed` |
| `minPrice` | Number | No | — | Minimum price filter in the selected currency |
| `maxPrice` | Number | No | — | Maximum price filter in the selected currency |
| `freeShipping` | Boolean | No | `false` | Only return products with free shipping |
| `onSale` | Boolean | No | `false` | Only return products currently on sale |
| `currency` | String | No | `USD` | ISO 4217 currency code for prices (e.g., `USD`, `EUR`, `GBP`, `CAD`, `AUD`). All prices in results will be in this currency: listings Etsy prices in another one are skipped, and a run short for that reason says so. With a residential proxy, the country follows this currency. |
| `locale` | String | No | `en-US` | BCP 47 locale code for the language of results (e.g., `en-US`, `de-DE`, `fr-FR`). Affects listing titles and labels. |
| `excludeDigitalDownloads` | Boolean | No | `false` | Remove listings Etsy's search card marks as digital or instant downloads. Combine with `excludeKeywords`, because not every card carries the mark. |
| `excludeKeywords` | Array | No | — | Skip any listing whose title contains one of these keywords (case-insensitive). Example: `["STL", "digital", "download"]`. Applied before `includeKeywords`. |
| `includeKeywords` | Array | No | — | Only keep listings whose title contains at least one of these keywords (case-insensitive). Example: `["handmade", "vintage"]`. Applied after `excludeKeywords`. |
| `enrichListingDetails` | Boolean | No | `false` | Try each listing page for seller sales, cart and recent-purchase signals, tags, materials, shipping and personalization. Etsy blocks most listing pages, so expect many `null` values and a slower run |
| `proxyConfiguration` | Object | No | Residential (prefilled) | Proxy settings. Residential proxy strongly recommended for Etsy |

### Output example

Each product in the dataset looks like this:

```json
{
    "listingId": "1284957302",
    "name": "Handmade Gold Filled Necklace, Dainty Chain Necklace, Gift for Her",
    "url": "https://www.etsy.com/listing/1284957302/handmade-gold-filled-necklace-dainty",
    "imageUrl": "https://i.etsystatic.com/12345678/r/il/abc123/4567890123/il_300x300.4567890123_example.jpg",
    "shop": "GoldenChainCo",
    "shopId": "34521890",
    "price": "24.99",
    "originalPrice": null,
    "discountPercent": null,
    "currency": "USD",
    "onSale": false,
    "freeShipping": true,
    "shipsFrom": null,
    "isAd": false,
    "rating": 4.84,
    "reviewCount": 1234,
    "availability": "In Stock",
    "isDigitalDownload": false,
    "hasVariations": true,
    "priceMayBeMinimumVariant": true,
    "variantDetectionStatus": "search_card_fallback",
    "position": 1,
    "query": "handmade necklace",
    "page": 1,
    "scrapedAt": "2026-03-22T14:30:00.000Z"
}
```

**Sale item with original price:**

```json
{
    "listingId": "4428738695",
    "name": "Orchid Pearl Necklace, Real Orchid Pendant, Handmade Botanical Jewellery",
    "url": "https://www.etsy.com/listing/4428738695/orchid-pearl-necklace-real-orchid",
    "imageUrl": "https://i.etsystatic.com/63062527/r/il/d7e81d/7578438711/il_fullxfull.7578438711_7a2q.jpg",
    "shop": "ZivaJewelleryArt",
    "shopId": "63062527",
    "price": "49.64",
    "originalPrice": "62.05",
    "discountPercent": 20,
    "currency": "AUD",
    "onSale": true,
    "freeShipping": false,
    "shipsFrom": null,
    "isAd": true,
    "rating": 4.97,
    "reviewCount": 412,
    "availability": "In Stock",
    "isDigitalDownload": false,
    "hasVariations": false,
    "priceMayBeMinimumVariant": false,
    "variantDetectionStatus": "search_card_fallback",
    "position": 3,
    "query": "handmade necklace",
    "page": 1,
    "scrapedAt": "2026-03-23T00:50:47.547Z"
}
```

### Tips for best results

🏷️ **Use a specific niche query** — the scraper uses Etsy's `/market/` URL, where the query determines relevance. `vintage leather messenger bag` produces a more focused dataset than `bag`, and available result depth varies by query.

🔑 **Use specific search terms** — `vintage leather messenger bag` returns far more relevant results than just `bag`. Etsy's search engine rewards specificity with better product matches.

💰 **Combine price filters with sort order** — to find the cheapest items in a niche, set `sort: lowest_price` and `maxPrice: 50`. This is the most effective setup for competitive pricing analysis.

📦 **Filter free shipping for buyer-perspective research** — many Etsy shoppers filter for free shipping. Use the `freeShipping: true` filter to see exactly what those buyers see — useful for optimizing your own listings.

🔄 **Schedule daily runs for price monitoring** — use Apify Schedules to run this scraper automatically. Export to Google Sheets and track price changes over time in any niche.

⚠️ **Start small to validate your query** — run 20–100 products first to confirm the search term returns what you expect. Then scale up once you're satisfied with the data quality.

🌐 **Keep the residential proxy** — Etsy blocks datacenter and shared proxy IPs. The actor is pre-configured with the `RESIDENTIAL` proxy group, which is the reliable route.

📅 **Use `recently_listed` sort for trend spotting** — set sort to `recently_listed` to catch new products for a query as soon as they appear. Combine with a scheduled run to monitor emerging trends.

### Integrations

Etsy Scraper works with Apify's full integration ecosystem. Here are concrete workflow patterns:

📊 **Google Sheets price tracker** — connect the dataset output to Google Sheets via Apify's native integration. Run the scraper on a daily schedule to build a rolling price history for any Etsy niche. Spot price trends without touching a spreadsheet.

💬 **Slack alerts for new listings** — combine `sort: recently_listed` with a Slack notification. Get automatically pinged when new products appear for your target query — useful for dropshippers and sourcing teams.

📧 **Email reports to your team** — use Apify's email integration to send a daily CSV export of competitor product data directly to stakeholders — no manual downloads.

🗄️ **Database sync** — push results to PostgreSQL, MySQL, MongoDB, or Airtable via Apify webhooks. Build a product intelligence database that updates on a schedule.

📁 **Amazon S3 / Google Cloud Storage** — archive datasets automatically to cloud storage for long-term trend analysis or compliance.

🔗 **Zapier and Make** — trigger downstream workflows in 5,000+ apps. Examples: automatically add new Etsy products to a Notion database, update a CRM with competitor listings, or push data to a Slack channel on schedule.

🐍 **Python and Node.js** — use the Apify SDK to call this actor programmatically and process results in your own pipeline (see API usage below).

### Using the Apify API

You can call Etsy Scraper programmatically using the Apify API. Here are examples in Node.js, Python, and cURL.

#### Node.js

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });

const run = await client.actor('automation-lab/etsy-scraper').call({
    searchQuery: 'handmade ceramic mug',
    maxItems: 100,
    sort: 'most_relevant',
    freeShipping: true,
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(`Found ${items.length} products`);
items.forEach((item) => {
    console.log(`${item.name} — $${item.price} by ${item.shop}`);
});
```

#### Python

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")

run = client.actor("automation-lab/etsy-scraper").call(run_input={
    "searchQuery": "vintage engagement ring",
    "maxItems": 200,
    "sort": "lowest_price",
    "minPrice": 50,
    "maxPrice": 500,
})

dataset = client.dataset(run["defaultDatasetId"]).list_items().items
for product in dataset:
    print(f"{product['name']} — ${product['price']} ({product['shop']})")
```

#### cURL

```bash
curl -X POST "https://api.apify.com/v2/acts/automation-lab~etsy-scraper/runs?token=YOUR_APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "searchQuery": "custom wedding invitation",
    "maxItems": 50,
    "sort": "most_relevant"
  }'
```

Retrieve results after the run completes:

```bash
curl "https://api.apify.com/v2/datasets/DATASET_ID/items?token=YOUR_APIFY_TOKEN&format=json"
```

### Use with AI agents via MCP

Etsy Scraper is available as a tool for AI assistants that support the [Model Context Protocol (MCP)](https://docs.apify.com/platform/integrations/mcp).

Add the Apify MCP server to your AI client — this gives you access to all Apify actors, including this one:

#### Setup for Claude Code

```bash
claude mcp add --transport http apify "https://mcp.apify.com?tools=automation-lab/etsy-scraper"
```

#### Setup for Claude Desktop, Cursor, or VS Code

Add this to your MCP config file:

```json
{
    "mcpServers": {
        "apify": {
            "url": "https://mcp.apify.com?tools=automation-lab/etsy-scraper"
        }
    }
}
```

Your AI assistant will use OAuth to authenticate with your Apify account on first use.

#### Example prompts

Once connected, try asking your AI assistant:

- "Use automation-lab/etsy-scraper to find the 50 most relevant handmade silver rings under $100 with free shipping and export results as a table."
- "Search Etsy for vintage denim jackets, sorted by lowest price, and summarize the price range and top shops."
- "Find the 20 most recently listed custom wedding invitations on Etsy and list each product name, shop, and price."

Learn more in the [Apify MCP documentation](https://docs.apify.com/platform/integrations/mcp).

### Is it legal to scrape Etsy?

Etsy Scraper accesses only publicly available product listing data from Etsy search results — the same information any visitor sees when browsing [etsy.com](https://www.etsy.com). No login, no authentication, and no private data access is involved.

Scraping publicly available web data is generally legal based on the 2022 US Ninth Circuit ruling in *hiQ Labs v. LinkedIn*, which established that scraping public data does not violate the Computer Fraud and Abuse Act (CFAA). The European Court of Justice has similarly held that publicly available data can be lawfully collected.

However, you should:

⚖️ **Review Etsy's Terms of Service** before scraping at scale — while public data extraction is broadly legal, Etsy's ToS may restrict automated access. Compliance with ToS is your responsibility.

🔒 **Never collect personal data without lawful basis** — this actor extracts only product and shop data, not buyer or payment information.

📏 **Be respectful with volume** — the scraper reads only a few pages at a time. Avoid running dozens of concurrent large-scale scrapes.

🏢 **Consult legal counsel** if you plan to use scraped data commercially at scale, especially across jurisdictions with varying data protection regulations.

For more details, see Apify's guide on [web scraping legality](https://blog.apify.com/is-web-scraping-legal/).

### Data handling

- **What is collected:** the search words and URLs you enter, and public listing data from Etsy, including shop names and IDs. No login, buyer data or contact details are used.
- **Who sees what:** Etsy sees the requests, through Apify's proxy network. No AI service receives your input or results.
- **Storage and deletion:** your input, dataset and run logs stay in your Apify account under its retention settings, and you can delete them there. The Actor keeps nothing between runs. Do not put secrets in search words or URLs.
- **Error reports:** failed operations send sanitized diagnostic input, exceptions and actor/build/run IDs to our private GlitchTip service for repair; secret fields and URL queries are removed and reports are retained for 30 days.
- **Not affiliated:** this Actor is not affiliated with or endorsed by Etsy.
- **Support:** use the Issues tab on this Actor's page.

### FAQ

**Why did the run return fewer products than `maxItems`?**
Etsy controls how many listings it exposes for each query: about 200 per search view, and fewer for narrow searches. The scraper reads across price ranges to go further, reaching 1,500+ on a popular search. Read the run's status message for what you got and why it stopped. Filters also reduce the count: when they match too few listings, the run stops rather than reading every page.

**Why are results not in strict price order across a large run?**
Etsy sorts about 100 listings per view and then serves unsorted ones, which the scraper does not read in a price sort. Large requests continue in the next price range, so the order holds within a range; a few rows can step back up where a new range starts. `recently_listed` has the same limit.

**Why does a run with a non-USD currency return nothing?**
Etsy prices listings by where the request comes from, so a residential proxy normally follows your currency automatically. If rows are still skipped (no proxy, a non-residential proxy, or a country that uses another currency), the status message names the cause: use a RESIDENTIAL proxy with its country empty or set to a country that uses your currency.

**Why does a search for "wall art" read a page called `wall_arts`?**
Etsy has a results page for only one wording of many searches. When your exact wording has none, the scraper tries its singular or plural form and then the reversed word order. If none exist, you get a clear status message and no charge.

**Which rows does `isAd` mark?**
Listings that Etsy shows as "Ad by Etsy seller" on the results page. Ads repeat across pages; each listing is saved once.

**What happened to the old `category` input?**
It was removed from the input form because Etsy's working market route cannot apply it and the field did not change results. Existing saved integrations that still send `category` remain query-based; remove that property and include the niche in `searchQuery` instead.

**Can I scrape individual Etsy product detail pages or whole shops?**
Not reliably. Etsy protects listing and shop pages much more aggressively than search pages. The optional `enrichListingDetails` setting tries listing pages, but most of them are blocked, so those fields are often `null`. Rows are charged the same with or without it.

**Why do some products have `null` for `rating` or `reviewCount`?**
Products that have not yet received reviews do not display rating signals on Etsy. The scraper returns `null` instead of a misleading zero. Etsy can also omit a review count even when a rating is visible, so `reviewCount` independently remains `null` whenever the search card does not expose it.

**How reliable are `hasVariations` and `priceMayBeMinimumVariant`?**
The scraper first tries to confirm variants from the listing page. Etsy sometimes blocks listing-detail pages more aggressively than search pages, so the scraper falls back to search-card signals such as Etsy's "from $12" minimum-price label and title cues like "choose", "customizable", "colors", or "sizes". `variantDetectionStatus` tells you which source was used. The fields are always boolean so `false` means "no variant signal found" rather than an ambiguous `null`.

**The scraper returned "blocked" warnings. What should I do?**
Ensure you're using residential proxies (the default configuration). If you run with no proxy or a datacenter proxy and Etsy blocks it, result pages switch to a residential proxy for the rest of the run and the log says so. A currency other than USD always reads result pages through a residential proxy, because Etsy prices follow the exit country. If blocks persist, try reducing `maxItems` or waiting a few minutes before retrying. Etsy's anti-bot detection can be more aggressive during peak traffic hours. If Etsy blocks every results page, the run fails with a clear message and no charge. The scraper automatically retries each request up to 5 times with fresh proxy sessions — occasional blocks are handled transparently.

**How is this different from the official Etsy API?**
The official Etsy API requires an approved OAuth application, enforces strict rate limits, and does not expose search result pages. This scraper extracts the same data any browser visitor sees from public search pages, with no API key required, no rate limit approvals, and no OAuth setup.

**How often can I run this scraper?**
There's no run frequency limit from the scraper side. Use Apify Schedules to run hourly, daily, or weekly. For price monitoring, daily runs are typically sufficient and keep costs low. You can also trigger runs via API as part of a larger pipeline.

### Related actors

🛒 [Amazon Scraper](https://apify.com/automation-lab/amazon-scraper) — Extract product data, prices, ratings, and seller info from Amazon search results

🏷️ [eBay Scraper](https://apify.com/automation-lab/ebay-scraper) — Scrape eBay product listings, prices, bids, and seller information

🛒 [Walmart Scraper](https://apify.com/automation-lab/walmart-scraper) — Scrape Walmart product data, prices, and availability

📋 [Craigslist Scraper](https://apify.com/automation-lab/craigslist-scraper) — Scrape Craigslist listings by keyword and city

🛍️ [Shopify Store Scraper](https://apify.com/automation-lab/shopify-scraper) — Monitor direct-to-consumer Shopify competitor catalogs, prices, availability, and reviews

👗 [Poshmark Scraper](https://apify.com/automation-lab/poshmark-scraper) — Extract fashion and resale listings from Poshmark

💻 [Newegg Scraper](https://apify.com/automation-lab/newegg-scraper) — Scrape electronics and tech products from Newegg

🔎 [Google Shopping Scraper](https://apify.com/automation-lab/google-shopping-scraper) — Scrape Google Shopping results for cross-marketplace price comparison

🎯 [Fiverr Scraper](https://apify.com/automation-lab/fiverr-scraper) — Extract Fiverr gig listings, seller info, and pricing for service marketplace research

### Server API: search Etsy products over HTTP

Etsy Scraper is also a Server Actor. Besides normal runs, it answers short HTTP requests directly, without starting a run for each search. Copy the server URL from the Actor's **Server** tab (do not build it from the Actor name) and authenticate with your Apify token. Use normal Actor runs for many searches, more than 48 products, listing detail enrichment, keyword filters and a custom proxy.

```bash
curl -sS "$SERVER_URL/search?query=ceramic%20mug&maxItems=20" -H "Authorization: Bearer $APIFY_TOKEN"
curl -sS "$SERVER_URL/search?query=leather%20wallet&sort=lowest_price&maxPrice=50&freeShipping=true" -H "Authorization: Bearer $APIFY_TOKEN"
```

| Method and path | Input | Output and charge |
| --- | --- | --- |
| `GET /` | None | `ready: true`, the routes and limits; no scraping or charge |
| `GET /search` | Query `query` (search words, 1–200 characters) or `url` (an Etsy search, `/market/` or `/c/` category URL), not both; optional `maxItems` 1–48 (default 20), `sort` (`most_relevant`, `lowest_price`, `highest_price`, `recently_listed`), `minPrice`, `maxPrice`, `currency` (such as `USD`, `EUR`, `GBP`; default `USD`), `freeShipping`, `onSale`, `excludeDigitalDownloads` (`true`/`false`) | `{ok,count,results}` with the same product records as a normal run's dataset items; one Product scraped event (`product-scraped`) per returned product; no start fee |

A search with no matching products returns an error and is free. The server reads Etsy directly and moves to a residential proxy when Etsy blocks it, as a normal run does without a proxy input. Each request has a 60-second deadline and a 512 KiB response cap, and one server works on at most six requests at once. Nothing is cached. Errors use `{ "ok": false, "error": { "code": "...", "message": "...", "retryable": false } }`: 400 `INVALID_INPUT`; 402 spending limit; 404 `NO_RESULTS` or unknown route; 405 wrong method; 413 `RESPONSE_TOO_LARGE`; 502 `BLOCKED`, `UPSTREAM_ERROR` or charging failure; 503 server busy or stopping; 504 `TIMEOUT`. Failed requests are free. Retry only when `retryable` is true. The Server tab shows the full OpenAPI description.

# Changelog

This Actor's version history is a separate document: https://apify.com/automation-lab/etsy-scraper/changelog.md

# Actor input Schema

## `searchQuery` (type: `string`):

What to search for on Etsy (e.g., 'handmade jewelry', 'vintage dress', 'custom gift'). To run several searches at once, use Search Queries.

## `searchQueries` (type: `array`):

Several searches in one run, each with its own Max Products. A listing found by more than one search is saved and charged once. Use this instead of, or together with, Search Query.

## `startUrls` (type: `array`):

Etsy category URLs (https://www.etsy.com/c/jewelry/necklaces), market URLs (https://www.etsy.com/market/ceramic_mug) or search URLs. Single listing and shop URLs are not supported.

## `maxItems` (type: `integer`):

Maximum products per search query or start URL. Etsy shows about 200 listings for one search view, so larger requests are read across price ranges, which reaches 1,500+ listings for a popular search. On large requests a sort holds within each price range, not across the whole run. When Etsy has fewer, the run's status message says how many you got and why.

## `sort` (type: `string`):

How to sort search results.

## `minPrice` (type: `number`):

Minimum price (in the selected currency). Listings below it are not returned.

## `maxPrice` (type: `number`):

Maximum price (in the selected currency). Listings above it are not returned.

## `freeShipping` (type: `boolean`):

Only return listings whose search card shows free shipping.

## `onSale` (type: `boolean`):

Only return listings that show a sale price.

## `currency` (type: `string`):

ISO 4217 currency code to use for prices (e.g. USD, EUR, GBP, CAD, AUD). Etsy converts prices to this currency, and listings it still prices in another one are skipped. With a residential proxy, the country follows this currency.

## `locale` (type: `string`):

BCP 47 locale code for the language of results (e.g. en-US, en-GB, de-DE, fr-FR). Affects listing titles and labels returned by Etsy.

## `excludeDigitalDownloads` (type: `boolean`):

Remove listings that Etsy's search card marks as digital or instant downloads (STL files, printables, patterns, etc.). Combine with Exclude Keywords, because not every card carries the mark.

## `excludeKeywords` (type: `array`):

Skip any listing whose title contains one of these keywords (case-insensitive). Useful for removing digital or unwanted items. Example: \["STL", "digital", "download", "print file"].

## `includeKeywords` (type: `array`):

Only keep listings whose title contains at least one of these keywords (case-insensitive). Applied after excludeKeywords. Example: \["3D printed", "articulated", "flexi"].

## `enrichListingDetails` (type: `boolean`):

Try each listing page for seller sales, cart and recent-purchase signals, tags, materials, shipping and personalization. Etsy blocks most listing pages, so many rows come back with null values here and the run is slower. Rows are charged the same either way.

## `proxyConfiguration` (type: `object`):

Proxy settings. Residential proxies recommended for Etsy.

## Actor input object example

```json
{
  "searchQuery": "handmade necklace",
  "searchQueries": [
    "ceramic mug",
    "leather wallet"
  ],
  "startUrls": [
    {
      "url": "https://www.etsy.com/c/jewelry/necklaces"
    }
  ],
  "maxItems": 20,
  "sort": "most_relevant",
  "minPrice": 10,
  "maxPrice": 100,
  "freeShipping": false,
  "onSale": false,
  "currency": "USD",
  "locale": "en-US",
  "excludeDigitalDownloads": false,
  "excludeKeywords": [
    "digital",
    "download"
  ],
  "includeKeywords": [
    "handmade"
  ],
  "enrichListingDetails": false,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}
```

# Actor output Schema

## `overview` (type: `string`):

Etsy listings emitted by this run.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "searchQuery": "handmade necklace",
    "maxItems": 20,
    "excludeKeywords": [
        "digital",
        "download"
    ],
    "includeKeywords": [
        "handmade"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "US"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("automation-lab/etsy-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "searchQuery": "handmade necklace",
    "maxItems": 20,
    "excludeKeywords": [
        "digital",
        "download",
    ],
    "includeKeywords": ["handmade"],
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "US",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("automation-lab/etsy-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "searchQuery": "handmade necklace",
  "maxItems": 20,
  "excludeKeywords": [
    "digital",
    "download"
  ],
  "includeKeywords": [
    "handmade"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}' |
apify call automation-lab/etsy-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,automation-lab/etsy-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/DpTUzMvbQpfdogIyK/builds/gE1cyd897Le7dSXOs/openapi.json
