# Etsy Scraper — Products, Prices, Variations & Shop Data (`ruslanzotkin/etsy-scraper`) Actor

💰 $1.50/1,000 (card) · $3.50/1,000 (full detail). Scrape Etsy listings, shops & full product pages: price, rating, reviews, favorites, variations, materials, all photos, category & shop metrics. Reliable browser-based detail, no API key. 🤖 MCP-ready: usable by AI agents via Apify's MCP server.

- **URL**: https://apify.com/ruslanzotkin/etsy-scraper.md
- **Developed by:** [Ruslan Zotkin](https://apify.com/ruslanzotkin) (community)
- **Categories:** E-commerce, Lead generation
- **Stats:** 17 total users, 7 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $1.50 / 1,000 listing (card)s

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Etsy Scraper — Products, Prices, Variations & Shop Metrics

Extract clean, structured data from **Etsy** at scale — search results, categories, shops, and full product pages. Get **prices, ratings, reviews, favorites, variations, materials, all photos, category, and shop metrics** without an API key, login, or manual copying.

Pay only for the results you get — **no monthly subscription**.

> 🏅 **Built for reliability.** Most Etsy scrapers fall back to lightweight HTTP requests for product pages and get blocked or return half-empty rows. This actor opens each listing in a **real browser session** that fully clears Etsy's anti-bot protection, so you get **complete data on every listing** — not "occasionally blocked".

***

### ⚡ Two modes — pick what you pay for

| Mode | Turn on | What you get | Best for |
|------|---------|--------------|----------|
| **Card (fast)** | default | Core fields from search/category results — title, price, rating, shop, image, sale status | Price monitoring, market research, large datasets |
| **Full details** | `Scrape full details = true` | Everything in Card **plus** description, variations, materials, all photos, favorites, category breadcrumb, shop sales & age, Star Seller | Deep product/competitor analysis, sourcing, sentiment |

You choose per run with a single checkbox. The two modes are billed as two separate events (see **Pricing**).

#### Popular ways to use it

- **Etsy shop scraper** — pass a shop URL in `shopUrls` (with full details) to export a seller's **entire catalog**.
- **Etsy product details scraper** — full per-listing data: **variations, materials, all photos, favorites**, category breadcrumb, shop metrics.
- **Etsy price & favorites monitor** — schedule it to track prices and **favorites** (a real demand signal) over time.
- **Scrape Etsy without the API** — no OAuth approval, no API key, no rate-limit waitlist.

***

### 🎯 Why choose this scraper

- ✅ **Reliable on product pages** — real-browser anti-bot bypass, not flaky HTTP. Complete rows, every time.
- ✅ **Clean, typed data** — prices are real **numbers** (`485`, not `"485"`), ready for analysis with no post-processing.
- ✅ **Most complete detail** — variations, materials, all images, favorites count, category path, shop sales & years on Etsy, Star Seller badge.
- ✅ **No API key, no login, no OAuth** — extracts the same public data any visitor sees.
- ✅ **Region & currency control** — pin the marketplace so prices don't drift (US, UK, DE, FR, and more).
- ✅ **Cost control** — hard `Max items` cap and a per-run charge limit, so a run can never surprise you.

***

### 📦 What data you get

#### Card-level (every mode)

| Field | Type | Description |
|-------|------|-------------|
| `listingId` | string | Unique Etsy listing ID |
| `title` | string | Product title |
| `url` | string | Listing URL |
| `price` | number | Current price (in the selected currency) |
| `currency` | string | ISO currency code (USD, EUR, GBP…) |
| `originalPrice` | number|null | Pre-sale price, if discounted |
| `isOnSale` | boolean | Whether the item is discounted |
| `freeShipping` | boolean | Free-shipping badge |
| `isAd` | boolean | Promoted (ad) listing |
| `image` | string | Main product image URL |
| `shopId` | string|null | Unique shop ID |
| `source` | string | `card` or `detail` |
| `scrapedAt` | string | ISO 8601 timestamp |

> ℹ️ Shop **name** and **URL** aren't shown on Etsy's search cards (only the shop **ID** is). Enable **Full details** to get `shopName`, `shopUrl`, and shop metrics. `availability` is also populated in detail mode.

#### Detail-level (Scrape full details = true)

| Field | Type | Description |
|-------|------|-------------|
| `description` | string | Full product description |
| `variations` | array | Options & values (e.g. Size, Color) |
| `materials` | array | Listed materials |
| `images` | array | **All** product photos (not just the first) |
| `priceMax` | number|null | Top of the price range when variations differ in price |
| `rating` | number|null | Average star rating |
| `reviewCount` | number|null | Number of reviews |
| `favorites` | number|null | How many shoppers favorited the listing (demand signal) |
| `category` | string|null | Category breadcrumb, e.g. `Jewelry < Rings < Engagement Rings` |
| `shopSales` | number|null | Total sales of the shop |
| `yearsOnEtsy` | number|null | How long the shop has been on Etsy |
| `starSeller` | boolean | Shop's Star Seller badge |
| `shopUrl` | string|null | Shop page URL |

***

### 🛠️ Input parameters

| Field | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| `search` | string\[] | No\* | — | Keywords to search on Etsy, e.g. `handmade ring` |
| `startUrls` | URL\[] | No\* | — | Etsy search/category URLs. With details on, also shop and listing URLs |
| `shopUrls` | string\[] | No\* | — | Scrape all products of a shop (requires full details) |
| `scrapeDetails` | boolean | No | `false` | Open each listing page for full detail fields |
| `maxItems` | integer | No | 100 | Max listings to return — caps your cost |
| `region` | enum | No | `US` | Marketplace localization: US, GB, CA, AU, DE, FR, IT, ES, NL, IE |
| `customCountry` | string | No | — | Advanced: any ISO-2 country code (e.g. SE, PL, JP) |
| `detailConcurrency` | integer | No | 3 | Parallel listing pages in detail mode (1–6). Higher = faster, needs more run memory |

\* Provide at least one of `search`, `startUrls`, or `shopUrls`.

#### Example — fast card-level search

```json
{
  "search": ["handmade ring"],
  "maxItems": 200,
  "region": "US"
}
```

#### Example — full details

```json
{
  "search": ["vintage engagement ring"],
  "scrapeDetails": true,
  "maxItems": 100,
  "region": "US"
}
```

#### Example — all products of a shop, with details

```json
{
  "shopUrls": ["https://www.etsy.com/shop/MoodyRoses"],
  "scrapeDetails": true,
  "maxItems": 500
}
```

***

### 📤 Example output (full details)

```json
{
  "listingId": "1887646984",
  "title": "PERIDOT GEMSTONE RING, Unique Artisan design handmade with solid 14k yellow gold",
  "url": "https://www.etsy.com/listing/1887646984",
  "price": 485,
  "priceMax": null,
  "currency": "USD",
  "originalPrice": null,
  "isOnSale": false,
  "availability": "InStock",
  "rating": 5,
  "reviewCount": 42,
  "favorites": 66,
  "description": "Handcrafted peridot ring in solid 14k gold...",
  "variations": [{ "name": "Ring Size", "options": ["5", "6", "7", "8"] }],
  "materials": ["14k gold", "peridot"],
  "images": [
    "https://i.etsystatic.com/.../il_794xN.1.jpg",
    "https://i.etsystatic.com/.../il_794xN.2.jpg"
  ],
  "category": "Jewelry < Rings < Wedding & Engagement < Engagement Rings",
  "shopName": "ArtisanStudioJewelry",
  "shopUrl": "https://www.etsy.com/shop/ArtisanStudioJewelry",
  "shopSales": 74,
  "yearsOnEtsy": 10,
  "starSeller": true,
  "source": "detail",
  "scrapedAt": "2026-06-10T12:00:00.000Z"
}
```

***

### 💵 Pricing

Transparent **pay-per-event** — you pay only for what you extract, all platform and proxy costs included. No monthly subscription, no hidden usage charges.

| Event | Price |
|-------|-------|
| Run start | **Free** |
| **Card listing** (`listing-scraped`) | **$1.50 / 1,000** |
| **Full detail listing** (`listing-detail-scraped`) | **$3.50 / 1,000** |

Real-world examples:

| Use case | Mode | Items | Approx. cost |
|----------|------|-------|--------------|
| Quick niche check | Card | 100 | $0.15 |
| Category snapshot | Card | 1,000 | $1.50 |
| Competitor deep-dive | Detail | 200 | $0.70 |
| Full product dataset | Detail | 1,000 | $3.50 |

> The Apify free tier includes $5/month of usage — enough to test thoroughly before scaling.

***

### 💡 Use cases

- **Price monitoring & repricing** — track competitor prices and discounts on a schedule.
- **Market & niche research** — measure listing density, price distributions, and demand (favorites, sales) across a category.
- **Dropshipping & sourcing** — find trending, high-favorite, high-sales products and reliable shops.
- **Competitor intelligence** — analyze top shops' catalogs, variations, materials, and positioning.
- **SEO & listing optimization** — study titles, categories, and attributes of top-ranking results.
- **Datasets for AI / analytics** — pipe clean structured product data into your warehouse or model.

***

### 🌍 Regions & localization

Prices, shipping, and availability are localized to the buyer country you choose, so values don't "drift" between runs. Validated: **US, GB, DE, FR**. Others (CA, AU, IT, ES, NL, IE) use the same mechanism. Need a country not listed? Use **Custom country code** with any ISO-2 code.

***

### 🔌 Integrations

Works with the full Apify ecosystem:

- **Google Sheets / Airtable** — auto-export for rolling price history.
- **Slack / email** — alerts on new listings or price drops.
- **Webhooks → PostgreSQL / MySQL / MongoDB** — build a product intelligence database.
- **Zapier & Make** — trigger downstream workflows in thousands of apps.
- **Apify API** (Node.js, Python, cURL) — call programmatically and pipe into your own code.

#### API example (Node.js)

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

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

const run = await client.actor('ruslanzotkin/etsy-scraper').call({
  search: ['handmade ceramic mug'],
  scrapeDetails: true,
  maxItems: 100,
  region: 'US',
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(`Got ${items.length} listings`);
```

#### Use with AI assistants (MCP)

Query Etsy data straight from Claude, Cursor, or any MCP-compatible AI — no manual runs. Add the Apify MCP server pointed at this actor:

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

Then ask in plain English: *"Find handmade rings under $50 on Etsy with their ratings and shops."*

***

### ❓ FAQ

**Do I need an Etsy account or API key?**
No. The scraper works without any login, API key, or OAuth.

**What's the difference between Card and Full details?**
Card mode reads search/category result pages — fast and cheap, with core fields. Full details opens each product page for the complete field set (variations, materials, all photos, favorites, category, shop metrics). Card is billed per `listing-scraped`, details per `listing-detail-scraped`.

**Is it reliable on product detail pages?**
Yes — this is the main differentiator. Detail pages are fetched through a real browser session that fully passes Etsy's DataDome anti-bot, so you get complete rows rather than the partial/blocked results common with HTTP-only scrapers.

**Why is a field sometimes `null`?**
A listing genuinely may not have that data (e.g. an unrated item has `rating: null`, a shop page may not display "years on Etsy"). We return `null` rather than a misleading value.

**Can I scrape a specific shop?**
Yes. Provide the shop URL in `shopUrls` with `scrapeDetails: true` to enumerate and extract its listings.

**Will prices be in my currency?**
Set `region` (or `customCountry`) to pin the marketplace. All prices in the run will be in that marketplace's currency.

**Is scraping Etsy legal?**
This actor extracts only **publicly available** product and shop data — the same anyone sees while browsing. It does not access private accounts or buyer personal data. Scraping public data is broadly lawful (see *hiQ v. LinkedIn*), but you are responsible for complying with Etsy's Terms and applicable laws (GDPR/CCPA) when using the data.

***

### ⚠️ Limitations

- **Per-listing sales counts aren't exposed by Etsy** — `shopSales` is the shop's total, which is what Etsy publishes (no scraper can give true per-listing sales).
- We deliberately **do not collect buyer personal data** (names, IDs) — by design, for privacy and compliance.
- Etsy occasionally changes page structure; if you spot a missing field, open an issue and it'll be fixed quickly.

***

### 🛟 Support

Found a bug or need a custom field? Open an issue in the **Issues** tab — please include the run ID so it can be reproduced and fixed fast.

# Actor input Schema

## `search` (type: `array`):

Keywords to search on Etsy (each becomes an Etsy market page). Example: "handmade ring".

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

Etsy URLs: search/category (e.g. /c/jewelry/rings, /market/handmade\_ring), or — with 'Scrape full details' enabled — shop URLs (/shop/Name) and individual listing URLs (/listing/123).

## `shopUrls` (type: `array`):

Etsy shop URLs to scrape all products from, e.g. https://www.etsy.com/shop/MyShop. Requires 'Scrape full details'.

## `scrapeDetails` (type: `boolean`):

When enabled, opens each listing page for the full field set: description, variations, materials, all images, favorites, category breadcrumb, shop sales & age, Star Seller, plus rating and review count. Also enables Shop URLs and individual listing URLs. Higher per-result price ($3.50 vs $1.50 per 1,000).

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

Maximum number of listings to return (caps your cost).

## `region` (type: `string`):

Country marketplace — controls the buyer-country localization (currency, shipping, availability). Validated: US, GB, DE, FR. Others supported via the same mechanism.

## `customCountry` (type: `string`):

Optional ISO-2 country code (e.g. SE, PL, JP) to target a country not in the list above. Overrides Region. May be untested.

## `detailConcurrency` (type: `integer`):

How many listing pages to scrape in parallel in full-details mode. Higher = faster but needs more run memory (use 4 GB+ for 3, 8 GB for 5–6). Only affects 'Scrape full details'.

## Actor input object example

```json
{
  "search": [
    "handmade ring"
  ],
  "scrapeDetails": false,
  "maxItems": 100,
  "region": "US",
  "detailConcurrency": 3
}
```

# Actor output Schema

## `listings` (type: `string`):

All scraped Etsy listings (card or full-detail) in the default dataset.

# 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 = {
    "search": [
        "handmade ring"
    ],
    "maxItems": 100
};

// Run the Actor and wait for it to finish
const run = await client.actor("ruslanzotkin/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 = {
    "search": ["handmade ring"],
    "maxItems": 100,
}

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

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

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

```

## CLI example

```bash
echo '{
  "search": [
    "handmade ring"
  ],
  "maxItems": 100
}' |
apify call ruslanzotkin/etsy-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=ruslanzotkin/etsy-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

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