# Etsy Listings Scraper (search, shops, listing details, prices) (`datahamster/etsy-listings`) Actor

Etsy scraper for listings by keyword, category URL, shop or listing id: price and variant price range, currency, shop, rating and reviews, seller tags, category path, availability, ad flag. Monitor mode alerts on price and stock changes. $5 per 1,000 listings, no login, no charge for empty runs.

- **URL**: https://apify.com/datahamster/etsy-listings.md
- **Developed by:** [Viktor Dubnytskiy](https://apify.com/datahamster) (community)
- **Categories:** E-commerce
- **Features:** Limited permissions
- **Stats:** 6 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.00 / 1,000 result items

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 Listings Scraper (search, shops, listing details, prices)

Turn a keyword, a category/search URL, a shop name or a listing id into flat Etsy listing rows with price, currency, shop, rating and tags — no login, no browser.

**Try it:** the prefilled example returns 12 rows in about 40 seconds and costs about $0.060. Results cost $5 per 1,000.

### What you get

Real rows from the example dataset (keyword `ceramic lamp`, one search page plus listing detail):

| title | price | currency | source |
|---|---|---|---|
| Handgefertigte Keramik Tischlampe: Exotische Limetten Glasur... | `250` (up to `350`) | EUR | `listing` — full detail: tags, category, rating `5`, 21 reviews |
| Handmade Ocher Fabric Table Lamp with Dark Green Trim-62# | `191.74` | USD | `search`, `isAd: true` |
| Raku Ceramic Pendant Light – Custom Copper Blue Green Pottery Lamp | `250` | USD | `search`, `isAd: true` |

Full row: `id`/`listingId`, `title`, `url`, `price`, `priceMax`, `currency`, `shopId`, `shopName`, `rating`, `reviewCount`, `image`, `isAd`, `tags`, `category`, `availability`, `description`, `source`, `scrapedAt`.

### Use cases

- **Product and keyword research** — pull a keyword's whole result set with prices and seller `tags` to see how top listings are titled and tagged.
- **Competitor and shop tracking** — collect every listing of a shop, then re-run in monitor mode to catch price changes and sold-out items.
- **Catalogue and pricing datasets** — export price, price range, currency, category and rating for thousands of listings into a spreadsheet or database.

### Try it in 10 seconds

Hit **Start**/**Try it** — the input already works: `keywords: ["ceramic lamp"]`, `searchPages: 1`, `maxItems: 20`, nothing required. Add `fetchDetails: true` to get tags, category and rating too.

To track price and stock changes over time: save as a Task, set **Mode** to `monitor`, give it a `monitorStateId`, and schedule it (Apify → Schedules → cron `0 9 * * *` for daily) with a `webhookUrl` or Telegram target — each run then returns only listings whose price, availability or review count changed.

### Related actors

- [Amazon Price Tracker: Product Scraper by ASIN, Buy Box, Stock](https://apify.com/datahamster/amazon-product-monitor) — same monitor pattern for mainstream retail listings.
- [Walmart Product Scraper & Price Monitor (stock, seller, store)](https://apify.com/datahamster/walmart-product-monitor) — same pattern for Walmart's marketplace.
- [Flipkart Product Scraper & Price Monitor (search, specs, stock)](https://apify.com/datahamster/flipkart-products) — the same pattern for the Indian market.
- [Shopify Store Products Scraper (products.json, prices, monitor)](https://apify.com/datahamster/shopify-store-products) — for an independent seller's own storefront instead of a marketplace.

### How it works

Etsy is behind DataDome, so pages are fetched through the Apify Web Unblocker tier and the server-rendered HTML is parsed directly — no browser, no login. A search page yields up to 48 listings in a single request; `fetchDetails` (or `listingIds`) opens individual listing pages for tags, category, description, review count and the variant price range. In `monitor` mode the run compares price, availability and review count against the previous run of the same Task and pushes only the listings that changed.

### Input

| Field | Meaning | Default |
|---|---|---|
| `keywords` | Etsy search queries, e.g. `ceramic lamp`; 48 listings per page | `["ceramic lamp"]` |
| `categoryUrls` | Category or search URLs, e.g. `https://www.etsy.com/c/home-and-living/lighting` | empty |
| `shops` | Shop names as they appear in `etsy.com/shop/<name>`, e.g. `DEBARROceramics` | empty |
| `listingIds` | Listing ids or full listing URLs for full detail rows | empty |
| `searchPages` | Pages per search query, 48 listings each | `1` |
| `fetchDetails` | Open every search hit's listing page for tags, category, description and review count | `false` |
| `locale` | `Accept-Language` value that decides the language of titles and categories, e.g. `en-US` | `en-US` |
| `maxItems` | Stop after this many pushed rows | `30` |
| `mode` | `scrape` = every listing; `monitor` = only listings changed since the previous run of this Task | `scrape` |
| `monitorStateId` | State key for monitor mode when not running as a saved Task | empty |
| `webhookUrl` | URL that receives a POST with the change summary in monitor mode | empty |
| `telegramBotToken` / `telegramChatId` | Optional Telegram destination for monitor-mode summaries | empty |

### Pricing

| Event | Price |
|---|---|
| result | $0.005 per listing ($5 per 1,000) |
| monitor-check | $0.015 per monitor run |
| change | $0.005 per changed listing |

Charged only for listings actually pushed; blocked pages are not charged and three in a row end the run with reason `blocked`.

**Found it useful?** A short review on the Store page helps other buyers and sellers find this actor and tells us what to improve. If something is wrong, open an issue on the actor page — issues are answered within a day.

### Why this actor

- **Four ways in, one row schema** — keyword search, category/search URL, shop and listing id all produce the same flat row, with `source` saying where it came from.
- **No charge for empty runs** — result events are billed per pushed row, so a run that finds nothing costs nothing in results.
- **Seller tags and full category path** included on detail rows, which is what keyword research actually needs.
- **Monitor mode built in** — price, availability and review-count changes with webhook or Telegram alerts, no second actor needed.
- **No login, no cookies, no API key** — nothing to configure beyond the input.
- **Every run leaves a `RUN_SUMMARY`** record in the key-value store with the reason for an empty result, so a zero-row run is diagnosable.

### Limits

- Etsy uses DataDome; the actor runs through the Apify Web Unblocker, which Apify bills per request on top of the result price. A search page yields up to 48 listings per request, while `fetchDetails` costs one request per listing.
- Sales counts and "ships from" are not exposed in Etsy's server-rendered HTML and are not returned.
- Search-page `rating` appears only where Etsy renders it in the card; use `fetchDetails` or `listingIds` for reliable rating and review count.
- Review texts and buyer names are not collected.

### FAQ

**How do I scrape all listings of an Etsy shop?** Put the shop name (the part after `etsy.com/shop/`) into `shops` and raise `maxItems`. Add `fetchDetails: true` if you also want tags, category and description for each listing.

**Can I get Etsy tags and category for keyword research?** Yes. Tags and the full category path come from listing pages, so either set `fetchDetails: true` on a keyword search or pass listing ids directly in `listingIds`.

**How do I track Etsy price changes?** Save the actor as a Task, set `mode` to `monitor`, fill in `webhookUrl` or the Telegram fields and schedule the Task. Each run returns only the listings whose price, availability or review count changed since the previous run.

### Changelog

- 0.1: initial release — search, category, shop and listing pages; monitor mode.

***

If this actor is useful, a review on its Apify Store page genuinely helps other buyers find it. Found a bug or need a field that is missing? Open a ticket on the **Issues** tab of the actor and it will be looked at.

# Actor input Schema

## `keywords` (type: `array`):

Etsy search queries, e.g. "ceramic lamp". Each search page returns up to 48 listing rows (id, title, price, currency, shop, image, ad flag).

## `categoryUrls` (type: `array`):

Etsy category or search URLs, e.g. "https://www.etsy.com/c/home-and-living/lighting" or "https://www.etsy.com/search?q=ceramic+lamp". Paged the same way as keywords.

## `shops` (type: `array`):

Etsy shop names exactly as they appear in etsy.com/shop/<name>, e.g. "DEBARROceramics". Returns that shop's listings.

## `listingIds` (type: `array`):

Etsy listing ids or full listing URLs, e.g. "1769625985" or "https://www.etsy.com/listing/1769625985". Each one is opened as a listing page, so the row also has tags, category, description, review count and the variant price range.

## `searchPages` (type: `integer`):

How many result pages to open per search query, shop or category URL, e.g. 1. Each page holds up to 48 listings.

## `fetchDetails` (type: `boolean`):

If true, open every search hit's listing page to add tags, category, description, review count and priceMax. Costs one extra request per listing. Example: false.

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

Accept-Language header value that decides the language of titles and categories, e.g. "en-US" or "de-DE".

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

Stop after this many pushed rows, e.g. 30. You are charged only for rows actually pushed.

## `mode` (type: `string`):

"scrape" returns every listing found; "monitor" returns only listings whose price, availability or review count changed since the previous run of the same saved Task, with changedFields and previous attached. Example: "scrape".

## `monitorStateId` (type: `string`):

Optional state id when not running as a saved task (monitor mode)

## `webhookUrl` (type: `string`):

URL that receives a POST with the monitor-mode change summary (JSON), e.g. "https://hooks.zapier.com/hooks/catch/123/abc". Leave empty to skip.

## `telegramBotToken` (type: `string`):

Optional Telegram bot token used to send monitor-mode change summaries, e.g. "123456789:AAF...". Leave empty to skip Telegram.

## `telegramChatId` (type: `string`):

Optional Telegram chat id that receives monitor-mode summaries, e.g. "-1001234567890". Required only when telegramBotToken is set.

## Actor input object example

```json
{
  "keywords": [
    "ceramic lamp"
  ],
  "searchPages": 1,
  "fetchDetails": false,
  "locale": "en-US",
  "maxItems": 20,
  "mode": "scrape"
}
```

# Actor output Schema

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

All pushed rows (dataset, JSON)

## `resultsTable` (type: `string`):

Dataset in the Console viewer

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

RUN_SUMMARY record (pushed, skipped, emptyReason)

# 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 = {
    "keywords": [
        "ceramic lamp"
    ],
    "searchPages": 1,
    "locale": "en-US",
    "maxItems": 20,
    "mode": "scrape"
};

// Run the Actor and wait for it to finish
const run = await client.actor("datahamster/etsy-listings").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 = {
    "keywords": ["ceramic lamp"],
    "searchPages": 1,
    "locale": "en-US",
    "maxItems": 20,
    "mode": "scrape",
}

# Run the Actor and wait for it to finish
run = client.actor("datahamster/etsy-listings").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 '{
  "keywords": [
    "ceramic lamp"
  ],
  "searchPages": 1,
  "locale": "en-US",
  "maxItems": 20,
  "mode": "scrape"
}' |
apify call datahamster/etsy-listings --silent --output-dataset

```

## MCP server setup

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

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/9eVVfNAam6yN0ZvfN/builds/5bVRmC1thrOGnFmzO/openapi.json
