# Etsy Scraper | 💵$3 per 1,000 listings (`webdata_labs/etsy-scraper`) Actor

\[💵 $3 / 1K] Etsy search listings with the sponsored/organic split as a real field, plus typed sale price, original price and discount percent in one pinned currency. Batch many keywords, deduplicated across them. No proxy setup, no captcha.

- **URL**: https://apify.com/webdata\_labs/etsy-scraper.md
- **Developed by:** [WebData Labs](https://apify.com/webdata_labs) (community)
- **Categories:** E-commerce, Automation, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.40 / 1,000 listing 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/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

**Scrape Etsy search results into typed rows: sale price, original price, discount percent, shop rating, and a real sponsored-vs-organic flag on every listing.**

Etsy search is a wall of cards where paid placements sit next to organic results with nothing but a small "Ad from shop" label to tell them apart, and every price is a string with a currency symbol glued to it. Most Etsy scrapers hand you that mess back: one `price` field with "€360.83" in it, no idea which rows Etsy was paid to show you, and a currency that silently changes with the exit country. This Actor returns the numbers already separated and typed, in one currency you pin for the whole run, with `isAd` on every row so you can measure how much of a keyword is bought rather than earned.

### ✅ What you get / ❌ what this isn't

| ✅ What you get | ❌ What this isn't |
|---|---|
| `isAd` on every row, so you can compute ad density per keyword | Not a dump that quietly mixes paid placements into "top results" |
| `price`, `originalPrice`, `discountPercent` as separate numbers | Not one string field with a currency symbol you have to strip |
| One currency for the whole run, pinned by input | Not whatever currency Etsy guessed from the exit IP |
| Sorts that Etsy actually applies (`price_asc`, `price_desc`, `date_desc`) | Not an enum with values Etsy silently ignores while returning relevance order |
| Many keywords per run, deduplicated across them | Not one query per run, billing you twice for listings two keywords share |
| Rows streamed and charged as each page is parsed | Not a run that buffers everything and delivers nothing if it times out |

### 🔎 Why use this Actor

- **Sponsored split as a first-class field.** Etsy fills roughly a third of a search page with paid slots. `isAd` plus `position` and `page` lets you measure ad density for a keyword, or drop paid rows entirely with one checkbox.
- **Price economics, not price strings.** A discounted listing arrives as `price`, `originalPrice` and `discountPercent`, so you can sort a niche by real discount depth without parsing anything.
- **One pinned currency.** Etsy converts prices for you when the run declares its currency and destination country, so a US run and a German run are directly comparable.
- **Verified filters only.** Every sort and filter offered here was tested against live Etsy and shown to change the result set. Etsy accepts some plausible-looking sort values and ignores them; those are not in the input schema.
- **Keyword batching with cross-query dedup.** Run twenty related keywords in one job; a listing that ranks for three of them is returned once and charged once.
- **Streamed delivery.** Every row is pushed and charged the moment its page is parsed, so an aborted run still leaves you with everything collected up to that point.

### 👥 Who it's for

Etsy sellers, print-on-demand and dropshipping operators, and marketplace analysts who need a niche's real price and competition picture rather than a screenshot of page one.

- Price a new product against the listings that actually rank for its keyword.
- Measure what share of a keyword is paid placement before deciding to buy Etsy Ads.
- Track discount depth across a category to time a promotion.
- Build a shop shortlist from ratings and bestseller badges in a niche.
- Feed a pricing model with a clean, single-currency table instead of scraped strings.

### Example tasks

- [Scrape Etsy listings for any keyword](https://apify.com/webdata_labs/etsy-scraper/examples/etsy-listings-scraper)
- [Track Etsy prices and discounts in a niche](https://apify.com/webdata_labs/etsy-scraper/examples/etsy-price-and-discount-tracker)
- [See how much of an Etsy keyword is paid ads](https://apify.com/webdata_labs/etsy-scraper/examples/etsy-sponsored-vs-organic-listings)
- [Find top-rated Etsy shops in a niche](https://apify.com/webdata_labs/etsy-scraper/examples/etsy-top-rated-shops-by-keyword)
- [Scrape Etsy listings with UK prices](https://apify.com/webdata_labs/etsy-scraper/examples/etsy-uk-listings-and-prices)
- [Monitor the newest Etsy listings for a keyword](https://apify.com/webdata_labs/etsy-scraper/examples/etsy-newest-listings-monitor)

### ⚙️ How to scrape Etsy search results

1. Open the Actor and click **Try for free**.
2. Put one or more terms in **Search queries**. They are scraped in order and deduplicated against each other.
3. Set **Max listings** (default 100, `0` for no cap) so your first run stays cheap.
4. Optionally set a **price band**, **sort order**, **free shipping only** or **on sale only**.
5. Pick a **currency** and **ship to country** if you want something other than USD and the United States.
6. Click **Start**, then read results in the run's **Output** tab or export CSV, JSON or Excel from the **Dataset** tab.

### 📥 Input

```json
{
  "searchQueries": ["handmade necklace", "silver ring"],
  "maxItems": 200,
  "sort": "price_asc",
  "minPrice": 20,
  "maxPrice": 200,
  "freeShippingOnly": false,
  "onSaleOnly": true,
  "includeAds": true,
  "currency": "USD",
  "country": "US"
}
```

- `searchQueries` - list of Etsy search terms. Required. Deduplicated case-insensitively; results are deduplicated across terms.
- `maxItems` - stop after this many unique listings across all queries. Default `100`, `0` means no cap.
- `sort` - `relevance` (default), `price_asc`, `price_desc`, or `newest`.
- `minPrice` / `maxPrice` - price band in the currency you selected. Optional.
- `freeShippingOnly` - keep only listings that ship free to the destination country. Default `false`.
- `onSaleOnly` - keep only discounted listings. Default `false`.
- `includeAds` - keep sponsored placements with `isAd: true`. Default `true`; turn off for organic only.
- `currency` - ISO code every price is returned in. Default `USD`.
- `country` - ISO destination country results are ranked and priced for. Default `US`.

### 📤 Output

| title | price | originalPrice | discountPercent | currency | shopName | shopRating | isAd |
|---|---|---|---|---|---|---|---|
| Baroque Pearl Necklace with Gold Chain | 360.83 | 451.03 | 20.0 | USD | Bayemeyc | 4.8 | true |
| Navy Beaded Necklace: Blue Glass Beads | 14.00 | | | USD | AccessoriesByAirita | 4.9 | false |
| Silver Samara Maple Seed Necklace | 52.50 | 70.00 | 25.0 | USD | LUKATO | 5.0 | false |

Every row also carries `listingId`, `url` (tracking parameters stripped), `imageUrl` and `videoUrl` where the listing has one, `shopId` and `shopUrl`, `shopReviewCount` with the `shopReviewCountText` it was derived from, the badges `isBestseller`, `isEtsysPick`, `isPopularNow`, `isStarSeller`, `hasFreeShipping` and `isLimitedStock`, the locale-relative `shipsFromLabel`, plus `searchQuery`, `page`, `position`, `resultsForQuery` (Etsy's own total hit count for the term) and `scrapedAt`. The run's `OUTPUT` record holds a summary: listings collected, sponsored versus organic split, pages fetched, and per-query result totals.

### 💵 How much does it cost?

You pay **$3 per 1,000 listings** delivered, and nothing for pages that return no new listing. A default run of 100 listings costs about $0.30. A keyword typically returns 50 to 60 listings per page, so a 1,000-listing job is roughly 17 to 20 pages. Sponsored rows are charged like any other row; turn `includeAds` off if you only want to pay for organic results.

### 🔁 Run it on the Apify platform

Schedule the same queries daily or weekly to build a price history, call the Actor from the API or any Apify client, and push results straight into Google Sheets, Make, Zapier, a webhook or your own database. The dataset exports as CSV, JSON, XML or Excel.

### ⚠️ Limits and caveats

- **Large shop review counts are rounded by Etsy on search cards.** Under about a thousand you get the exact number (556); above it Etsy prints "18.8k" and `shopReviewCount` expands that to 18,800. `shopReviewCountText` always carries what Etsy actually printed, so you can tell the two apart.
- **Etsy serves more than one card layout.** The Actor handles the layouts seen in the US, UK and EU, but Etsy tests these constantly and badge availability varies between them: a page that shows "Bestseller" to one shopper may show "Etsy's Pick" to another.
- **Sponsored listings only appear among the server-rendered slots.** Etsy does not include paid placements in the lazy-loaded part of a page, so ad density is measured over the slots where ads can appear, not over the whole page.
- **`shipsFromLabel` is locale-relative.** Etsy renders "Ships from nearby" for a shopper close to the seller and a country code otherwise, so it is a hint rather than a country field.
- **No listing detail data.** Materials, tags, full descriptions, view counts and stock quantity live on the listing page and are not collected here. This Actor covers the search grid.
- **Etsy keeps serving pages past the end of a result set.** The Actor stops a query when a page returns no listing it has not already seen, which is the only honest end-of-results signal Etsy gives. You are never charged for a repeat.
- **Some broad, high-intent keywords are capped by Etsy at roughly one page.** Measured on "personalized gift": Etsy claims 1.1 million results but serves the identical listings for `page=2`, `3`, `4` and `5`, with or without a pagination referrer. The Actor detects this and stops, so such a query returns about 50 listings no matter what you set `maxItems` to. Narrower keywords ("ceramic mug", "leather journal", "silver ring") paginate normally. If a query returns far fewer rows than you asked for, split it into more specific terms.
- **Result totals are Etsy's own estimate.** `resultsForQuery` moves by a few hits between requests for the same term; it is a scale indicator, not a count you can reconcile against.

### 🧩 Related Actors

- **eBay Listings Scraper** - price the same product against eBay's active listings.
- **Poshmark Sold Comps Scraper** - what comparable items actually sold for, not what they are asking.
- **Mercari Sold Comps Scraper** - sold-price comparables for the resale side of a niche.
- **Google Shopping Scraper** - check whether a handmade niche is being undercut by mass-market sellers.

### ❓ FAQ

**Do I pay for sponsored listings?** Yes, they are charged like any other row. Set `includeAds` to `false` and you will neither collect nor pay for them.

**Am I charged for a query that returns nothing?** No. Charging happens per delivered listing, so an empty or exhausted query costs nothing.

**Can I get prices in my own currency?** Yes. Set `currency` and `country` and Etsy converts and ranks results as if a shopper there ran the search.

**Why does the same keyword return a slightly different total each run?** Etsy's hit count is an estimate that drifts between requests. The listings themselves are stable; the headline total is not.

**Do I need a proxy or a captcha solver?** No. Session handling is internal to the Actor and there is nothing to configure.

**How many listings can I get for one keyword?** Etsy paginates deeply and the Actor walks until a page adds no new listing. In testing, a broad keyword kept returning fresh listings well past 100 pages.

### 🛠️ Support

Something wrong? Open an issue on the Actor's **Issues** tab with the run URL, the input you used, and what you expected. That is the fastest path to a fix.

### ⭐ Rate this Actor

If this saved you an afternoon, please leave a rating on the **Reviews** tab. Review count is the main trust signal buyers use on the Store, and reviews tell me which fields to add next. If something is broken, please open an issue first so it can be fixed before you rate.

# Actor input Schema

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

One or more Etsy search terms. Every term is scraped and the results are deduplicated across terms, so overlapping keywords never bill you twice for the same listing.

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

Stop after this many unique listings across all queries. Set 0 for no cap. The default keeps a first run cheap and fast.

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

How Etsy orders the results. Etsy applies price and recency sorts to organic listings only; sponsored slots keep their own order.

## `minPrice` (type: `integer`):

Lowest price to include, in the currency selected below. Leave empty for no minimum.

## `maxPrice` (type: `integer`):

Highest price to include, in the currency selected below. Leave empty for no maximum.

## `freeShippingOnly` (type: `boolean`):

Keep only listings that ship free to the destination country.

## `onSaleOnly` (type: `boolean`):

Keep only listings with a discount against their original price.

## `includeAds` (type: `boolean`):

Etsy mixes paid placements into every search page. Leave this on to collect them with an `isAd` flag so you can measure ad density, or turn it off for organic results only.

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

ISO currency code every price is returned in. Etsy converts for you, so a run is comparable across shops and countries.

## `country` (type: `string`):

ISO country code results are ranked and priced for, as if a shopper in that country ran the search.

## Actor input object example

```json
{
  "searchQueries": [
    "handmade necklace",
    "ceramic mug"
  ],
  "maxItems": 100,
  "sort": "relevance",
  "minPrice": 20,
  "maxPrice": 200,
  "freeShippingOnly": false,
  "onSaleOnly": false,
  "includeAds": true,
  "currency": "USD",
  "country": "US"
}
```

# Actor output Schema

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

No description

## `pricing` (type: `string`):

No description

## `ads` (type: `string`):

No description

## `shops` (type: `string`):

No description

## `runSummary` (type: `string`):

No description

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

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

# Run the Actor and wait for it to finish
run = client.actor("webdata_labs/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 '{
  "searchQueries": [
    "handmade necklace"
  ],
  "maxItems": 100
}' |
apify call webdata_labs/etsy-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,webdata_labs/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/MR3dBioFTKxq4pjK0/builds/cCjrWhu1fVVmFouto/openapi.json
