# Etsy Scraper: Product Listings, Prices, Tags, Shop Sales (`arcadium/etsy-products`) Actor

Etsy product research: full listings for keywords or URLs, $7.20 per 1,000, 10% under the most used Etsy scraper's $8, whose input works as is. Price, sale price, favorites, rating, tags, materials, variations, shipping and shop sales in every row; review text $4.50 per 1,000. Free error rows.

- **URL**: https://apify.com/arcadium/etsy-products.md
- **Developed by:** [Arcadium Data](https://apify.com/arcadium) (community)
- **Categories:** E-commerce
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.40 / 1,000 listings

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: Product Listings, Prices, Tags, Shop Sales

Research a niche before you open a shop, price your products against the market, or track the tags and best sellers of competing shops. Give it keywords, Etsy category links or listing URLs and it returns one JSON row per listing: price and sale price, favorites, the listing's own rating and review count, tags, materials, variations, shipping and delivery estimate, and the shop's sales count and rating. Review text is optional. $7.20 per 1,000 listings and $4.50 per 1,000 reviews; you pay only for rows you get.

**Why this actor**

- **Drop-in input.** Input written for `yumitori/etsy-listings-scraper` (`queries`, `maxResults`, `maxPages`, `sort`, `country`, `onSaleOnly`, `freeShippingOnly`), `yumitori/etsy-search-scraper` (`searchTerms`, `maxResultsPerTerm`, `sortOn`, `sortOrder`), `epctex/etsy-scraper` (`startUrls`, `search`, `maxItems`, `includeDescription`) or `automation-lab/etsy-scraper` (`searchQuery`, `sort`, `freeShipping`, `onSale`) works as it is.
- **A full listing for 10% less.** Every row is a full listing (variations, sale price, the listing's rating, shop and shipping), not just a search card, at $7.20 per 1,000 against $8 at yumitori/etsy-listings-scraper, the most used Etsy scraper (free plan, Apify Store, 2026-10-31).
- **Runs that finish.** 14.9% of epctex/etsy-scraper's runs failed or timed out (1,997 time-outs and 69 failures of 13,904 runs, Apify Store, 30 days to 2026-10-31). 30 of our 30 runs since 2026-10-22 succeeded (scheduled and test runs, Apify run history). Unknown listing ids and unreadable inputs come back as free error rows.
- Etsy's own filters: price range, ship-to country and sort order. Run the same keywords weekly and you have a price, rank and favorites history.

| Row | Free plan | Business (Gold) and above |
| --- | --- | --- |
| Listing (full details) | $7.20 per 1,000 | $5.40 per 1,000 |
| Review (only when requested) | $4.50 per 1,000 | $3.38 per 1,000 |

Built by Arcadium Data: public data, dated and agent-ready, never personal data.

### Use with AI agents

Returns Etsy listings (price, sale price, favorites, rating, review count, tags, materials, variations, shipping, shop sales and rating, rank) for keywords, category URLs or listing URLs. Agents can call it through the Apify MCP server (`https://mcp.apify.com`, open source as `apify/actors-mcp-server`) as `arcadium/etsy-products`: the input schema is read from the actor, the result is a dataset of JSON rows.

Input example (minimal):

```json
{
  "searchTerms": ["ceramic mug"],
  "maxResultsPerSearch": 20
}
```

What comes back: one `listing` row per listing (charged), one `review` row per review when `maxReviewsPerListing` is above 0 (charged), and a free `error` row for an input that could not be read. Filter on the `rowType` field. The error row carries `rowType: "error"`, a `code` (`invalid_input`, `not_found`, `blocked`, `unavailable`, `filter_unsupported` or `no_match`), a `message` and the `input` it is about. Retry on `blocked` and `unavailable` later; do not retry the other codes with the same input.

Cost of the call above: 20 listings at $7.20 per 1,000 plus the $0.001 start fee is about $0.15. Error rows are never charged.

### What you get

| Field | What it holds |
| --- | --- |
| `listingId`, `title`, `url` | The listing id (the number in the link), its title and link. |
| `price`, `currency`, `priceUsd` | Price in the seller's currency (lowest option when options change the price), and Etsy's US-dollar figure. |
| `salePrice`, `discountPercent`, `onSale` | The sale price and discount while a sale runs. |
| `favorites` | How many shoppers saved the listing. |
| `rating`, `reviewCount`, `ratingCounts` | This listing's own reviews: average, count and per-star counts. |
| `tags`, `materials` | The seller's search tags and materials. |
| `category`, `categoryPath`, `taxonomyId` | Etsy category. |
| `variations` | Buyer options (color, size, personalization...) with the price per option when it differs. |
| `images` | Full-size image links. |
| `whenMade`, `isDigital`, `isCustomizable`, `isMadeToOrder`, `isBestseller` | What kind of item it is. |
| `shipping` | Ships from, free or cost, delivery estimate to the US, returns policy. |
| `shop` | The shop as a business: name, link, lifetime sales, rating, review count, location, active listings, favorites, opening date, Star Seller badge. |
| `createdAt`, `updatedAt` | Listing dates (a renewal resets `createdAt`). |
| `searchInput`, `searchRank`, `isAd` | The keyword or URL that found it, the organic position, and whether it was a paid placement. |
| `scrapedAt` | When the row was collected. |

Output example:

```json
{
  "rowType": "listing",
  "platform": "etsy",
  "listingId": "962939986",
  "title": "Leather Envelope Wallet for Women, Personalized Mini Card Wallet, Full Grain Crazy Horse",
  "url": "https://www.etsy.com/listing/962939986",
  "price": 64,
  "currency": "USD",
  "salePrice": 57.6,
  "discountPercent": 10,
  "onSale": true,
  "favorites": 4979,
  "rating": 4.95,
  "reviewCount": 245,
  "tags": ["Envelope Wallet", "Women's Leather", "Mini Credit Card", "Handmade Wallet", "card wallet"],
  "materials": ["Full Grain Crazy Horse Leather"],
  "category": "Wallets",
  "variations": [{ "name": "Primary color", "options": [{ "value": "Tan", "price": null, "available": true }] }],
  "whenMade": "2020_2026",
  "shipsFromCountry": "TR",
  "shipping": { "shipsFrom": "Türkiye", "freeShipping": true, "deliveryEstimate": "Oct 26-Nov 2", "deliverTo": "United States", "returns": "Returns & exchanges accepted within 30 days." },
  "shop": { "shopId": "28165607", "shopName": "YukonLeatherGoods", "sales": 6583, "rating": 4.92, "reviewCount": 1412, "location": "Dover, Delaware", "isStarSeller": true },
  "searchInput": "leather wallet",
  "searchRank": 3,
  "isAd": false
}
```

Review rows (`rowType: "review"`) hold `reviewId`, `listingId`, `rating`, `text`, `date`, `sellerResponse` and `hasPhoto`.

### Input

```json
{
  "searchTerms": ["ceramic mug", "leather wallet"],
  "maxResultsPerSearch": 100,
  "sortBy": "newest",
  "minPrice": 20,
  "maxPrice": 60,
  "shipTo": "US",
  "freeShipping": true,
  "maxReviewsPerListing": 10
}
```

| Field | What it does |
| --- | --- |
| `searchTerms` | One or more keywords. Each is searched on its own. |
| `startUrls` | Listing URLs or ids for full details; search, market or category URLs (`https://www.etsy.com/c/jewelry/necklaces`) to read their results. |
| `maxResultsPerSearch` | Listings per keyword or search URL. Default 50. |
| `maxItems` | Optional cap on charged rows for the whole run. |
| `sortBy` | `relevance` (default), `newest`, `price_asc`, `price_desc` or `top_reviews`. |
| `minPrice`, `maxPrice` | Price range in US dollars. |
| `shipTo` | Two-letter country the items must ship to, e.g. `GB`. |
| `freeShipping`, `onSale` | Only free-shipping or discounted listings. |
| `includeAds` | Also return the paid placements among the results (off by default, flagged `isAd`). |
| `maxReviewsPerListing`, `reviewSort` | Review rows per listing (default 0) and their order: `newest`, `relevance`, `highest`, `lowest`. |
| `description` | `short` (default, 300 characters), `full` or `none`. |
| `includeShipping` | The shipping block (on by default). |
| `includeErrorRows` | A free row for every input that returned nothing. On by default. |

Inputs of other Etsy actors work unchanged: `queries`, `searchQueries`, `searchQuery`, `maxResults`, `sort` (`score`, `created`, `price-asc`, `most_relevant`, `recently_listed`...), `country`, `onSaleOnly`, `freeShippingOnly`, `listingUrls` and `includeDescription` are mapped to the fields above. yumitori's `maxPages` becomes `maxResultsPerSearch` at 36 listings per page (`maxResults` wins when both are set), and its `sortOn` + `sortOrder` pair becomes `sortBy` (`score` → `relevance`, `created` → `newest`, `price` + `up`/`down` → `price_asc`/`price_desc`; `updated` keeps relevance). Not supported: `digitalOnly`, `handmadeOnly`, the `carts` and `lastsale` sorts, and epctex's `proxy` (the actor picks its own proxy).

### Use it from an AI assistant

```text
Use the Apify actor "arcadium/etsy-products" to research an Etsy niche.
Input: {"searchTerms":["resin coaster"],"maxResultsPerSearch":60}
Each dataset row with rowType "listing" is one listing. Key fields: title, price, salePrice, favorites, reviewCount, tags, shop.sales.
Run it, then list the 10 listings with the most favorites, the tags they share, and the typical price band.
```

### Use it from code

Python:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("arcadium/etsy-products").call(run_input={
    "searchTerms": ["ceramic mug"],
    "maxResultsPerSearch": 100,
})
for l in client.dataset(run["defaultDatasetId"]).iterate_items():
    if l["rowType"] == "listing":
        print(l["searchRank"], l["price"], l["favorites"], l["shop"]["shopName"], l["title"])
```

JavaScript:

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

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('arcadium/etsy-products').call({
  startUrls: ['https://www.etsy.com/listing/962939986'],
  maxReviewsPerListing: 20,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.filter((i) => i.rowType === 'review').length, 'reviews');
```

### Pricing

You pay per row delivered, at the price of your Apify plan. Error rows are free.

| Apify plan | Per 1,000 listings | Per 1,000 reviews |
| --- | --- | --- |
| Free | $7.20 | $4.50 |
| Starter (Bronze) | $6.48 | $4.05 |
| Scale (Silver) | $5.76 | $3.60 |
| Business (Gold) | $5.40 | $3.38 |
| Platinum | $5.40 | $3.38 |
| Diamond | $5.40 | $3.38 |

Plus a one-off $0.001 per GB of memory when a run starts.

Set a maximum cost per run in the Apify run options and the actor stops before it goes over.

### Related actors

- [Amazon Product & Best Sellers Scraper: Search, Buy Box](https://apify.com/arcadium/amazon-products)

### Use cases

- Niche research: search a keyword, sort by favorites and review count, read the tags of the winners.
- Competitor tracking: run the same listing URLs weekly and compare price, sale, favorites and reviews.
- Category sizing: how many listings, what price bands, how many shop sales behind them.
- Product sourcing: filter by price and ship-to country, then check delivery estimates.

### Limitations

- Public listing data only. Nothing behind a login.
- Prices are in the seller's currency (`currency`), with Etsy's US-dollar figure in `priceUsd`. Shipping and delivery estimates are for a US shopper.
- Search pages show what Etsy decides to show at that moment: rank and paid placements change from run to run.
- A listing found by two keywords is returned (and charged) once, with the first keyword and its rank.
- `createdAt` is the date Etsy keeps for the listing; a renewal resets it, so old listings can look new.
- The number of sales of a single listing and "in carts" counts are not part of the public listing data; the shop's lifetime sales are.

### Privacy

Only listing and shop business data is collected. No buyer or reviewer names, profiles or photos, no shop owner names, no emails or phone numbers (they are removed from descriptions and review texts).

### FAQ

**How fresh is the data?**
It is read when you run the actor, so it matches what Etsy shows at that moment.

**Why are some listings marked `isAd`?**
With `includeAds` on, the paid placements on each results page are returned too and flagged. They carry no rank.

**A keyword returned an error row. Why?**
`no_match` means Etsy has no results for it. `blocked` or `unavailable` means the page could not be read this time; try again later.

**Something looks wrong in the output. Who do I tell?**
Open an issue on the actor page with the run link. We read them all.

# Actor input Schema

## `searchTerms` (type: `array`):

Keywords to search Etsy for, for example "ceramic mug" or "personalized necklace". Each term is searched on its own; every listing keeps the term and its position in the results. Also read from queries, searchQueries, searchQuery, search or keywords (other Etsy actors' names).

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

Listing URLs or ids (e.g. https://www.etsy.com/listing/962939986 or 962939986) for full details, and search or category URLs (e.g. https://www.etsy.com/c/jewelry/necklaces) to read their results. Also read from listingUrls, urls or listingIds; objects with a url field work too.

## `maxResultsPerSearch` (type: `integer`):

Listings per search term or search/category URL, e.g. 50 (the default). Each page holds 40 to 64 results; Etsy pages up to about 250 pages deep. Also read from maxResults, maxResultsPerTerm, maxListings or maxPages (x 36).

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

Optional cap on charged rows (listings plus reviews) for the whole run, e.g. 500. Empty means no cap beyond the per-search limit.

## `sortBy` (type: `string`):

Order of search results: relevance (Etsy's default), newest, price_asc, price_desc or top_reviews, for example newest. Also read from sort, sortOrder or sortOn (other Etsy actors' names and values).

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

Only listings priced at or above this amount, in the shopper currency (US dollars for the default US storefront), e.g. 20.

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

Only listings priced at or below this amount, in US dollars, e.g. 60.

## `shipTo` (type: `string`):

Two-letter country code the items must ship to, e.g. US, GB or DE. Empty means any.

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

Only listings that ship for free (to the US storefront).

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

Only listings with a running discount (salePrice and discountPercent are filled).

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

Also return the sponsored listings shown among the results (isAd: true, no rank). Off by default; they do not count towards the per-search limit.

## `maxReviewsPerListing` (type: `integer`):

Newest reviews to return per listing as separate review rows (text, rating, date, shop reply; no reviewer names), e.g. 20. 0 = none (the default); the rating summary is always on the listing.

## `reviewSort` (type: `string`):

Which reviews come first when reviews are requested: newest, relevance, highest or lowest rating.

## `description` (type: `string`):

Listing description in the output: short (first 300 characters, the default), full, or none. Emails and phone numbers are removed.

## `includeShipping` (type: `boolean`):

Adds the shipping block per listing: ships from, free or cost, delivery estimate to the US and the returns policy.

## `includeErrorRows` (type: `boolean`):

Adds a free row for every input that returned nothing or could not be read, so a failure is never silent.

## `maxRetries` (type: `integer`):

How often a request is retried after a temporary error, e.g. 3 (the default).

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

Leave empty. The actor picks the cheapest proxy that works for each kind of page.

## Actor input object example

```json
{
  "searchTerms": [
    "ceramic mug"
  ],
  "maxResultsPerSearch": 20,
  "includeAds": false,
  "reviewSort": "newest",
  "includeShipping": true,
  "includeErrorRows": true,
  "maxRetries": 3
}
```

# Actor output Schema

## `results` (type: `string`):

All rows of this run in the default dataset.

## `view_overview` (type: `string`):

No description

## `view_details` (type: `string`):

No description

## `view_shops` (type: `string`):

No description

## `view_reviews` (type: `string`):

No description

## `view_errors` (type: `string`):

No description

## `record_stats` (type: `string`):

Requests, data volume, retries and cost per result for 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 = {
    "searchTerms": [
        "ceramic mug"
    ],
    "maxResultsPerSearch": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("arcadium/etsy-products").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 = {
    "searchTerms": ["ceramic mug"],
    "maxResultsPerSearch": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("arcadium/etsy-products").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 '{
  "searchTerms": [
    "ceramic mug"
  ],
  "maxResultsPerSearch": 20
}' |
apify call arcadium/etsy-products --silent --output-dataset

```

## MCP server setup

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

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/EceETzq604hmiXSVg/builds/EuJEjRDi0zBqwto8I/openapi.json
