# Etsy Scraper: Search Listings, In-Cart Counts, Ads & Tags (`epicscrapers/etsy-scraper`) Actor

Scrape Etsy search results for any keyword: price, rank, ad slots, live in-cart counts, 24h sales, tags and each shop's total sales. Goes past Etsy's ~1,000-result limit per search. No login. Export to JSON, CSV or Excel.

- **URL**: https://apify.com/epicscrapers/etsy-scraper.md
- **Developed by:** [Epic Scrapers](https://apify.com/epicscrapers) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 listing saveds

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/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: search results with live cart counts, ads, tags and shop sales

Etsy Scraper turns any Etsy keyword search into a spreadsheet. You get every listing a shopper sees, with its search rank and ad slot. Each listing also has the numbers Etsy itself publishes: how many people have it in their cart right now, how many bought it in the last 24 hours, its 13 tags, its favorites, and its shop's total sales.

- **Real Etsy figures, not estimates.** Cart counts, 24-hour sales, shop sales, tags and favorites come straight from Etsy. Nothing is modeled or guessed.
- **More than Etsy's ~1,000-result limit.** Etsy stops every search at about 1,000 results. This actor splits large searches by price, so a broad keyword can return several thousand unique listings.
- **Built for repeated runs.** Schedule it daily or weekly and compare runs to see what's selling, how ranks move and who is buying ads.
- **No Etsy account, API key, browser extension or proxy setup.** Runs in the cloud. Export to JSON, CSV, Excel or Google Sheets.

**Scope:** This actor searches by keyword. To scrape a known shop's listings, profile or reviews, use [Etsy Shop Scraper](https://apify.com/epicscrapers/etsy-shop-scraper).

### What can you do with Etsy Scraper?

|                             |                                                                                                                                |
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| Goal                        | How                                                                                                                            |
| Find products that sell now | Search a niche and sort by `inCartCount` or `boughtLast24h`.                                                                   |
| Research Etsy SEO and tags  | Collect the `tags`, titles and categories of the listings that rank first for your keywords.                                   |
| Track your search rank      | Schedule your keywords daily and follow your listings' `searchRank` over time.                                                 |
| Watch competitors' ads      | See which listings are paid placements (`isAd`) and where they appear (`page`, `pagePosition`).                                |
| Measure real shop sales     | Schedule a run and subtract yesterday's `shopSales` from today's. That's the shop's actual sales for the day, not an estimate. |
| Price a product             | Collect prices, sale prices and discounts across a niche, filtered by price range, shipping country or shop location.          |

#### Why real figures matter

Popular Etsy research tools estimate sales with a model, because Etsy doesn't publish per-listing sales. Their estimates can be far off in either direction, and different tools often disagree. This actor reports only what Etsy publishes: the shop's total sales count, the live in-cart count, the "bought in the last 24 hours" message, favorites, ratings and tags. If you need sales over a period, the scheduled-run recipe below gives you exact shop-level numbers from Etsy's own counter.

### Quick start

1. Click **Try for free** and sign in to Apify. The free plan includes monthly credit for test runs.
2. Add one or more keywords to **Search keywords**, for example `ceramic mug`. Use short phrases a shopper would type.
3. For a first run, set **Max results per search** to `20`.
4. Click **Start**. When the run finishes, open the **Output** tab and export the data.

```json
{
    "queries": ["ceramic mug"],
    "maxResultsPerQuery": 20
}
```

### What data can you extract?

Every row is one listing. These fields come from the search results page:

|         |                                                                                                           |
| ------- | --------------------------------------------------------------------------------------------------------- |
| Group   | Fields                                                                                                    |
| Listing | `listingId`, `title`, `url`, `price`, `salePrice`, `discount`, `currency`, `hasVariations`, `isEtsysPick` |
| Search  | `query`, `searchRank`, `page`, `pagePosition`, `isAd`                                                     |
| Shop    | `shopId`, `shopRating`, `shopReviewCount`                                                                 |
| Media   | `thumbnail`, `imageUrl`, `imageCount`, `hasVideo`                                                         |

With **Include listing details** on (the default), each row also has:

|             |                                                                                                                                                                                                                                   |
| ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Group       | Fields                                                                                                                                                                                                                            |
| Demand      | `inCartCount` (shoppers with it in their cart now), `boughtLast24h`, `demandMessage`, `lastSaleAt`, `favorites`, `quantity`, `isScarce`, `isSoldOut`                                                                              |
| Reputation  | `listingRating`, `listingReviewCount`, `reviewPhotoCount`, `isBestseller`, `isTopRated`                                                                                                                                           |
| SEO         | `tags` (all 13), `materials`, `category`, `categoryPath`, `description`, `whenMade`                                                                                                                                               |
| Product     | `isHandmade`, `isVintage`, `isDigital`, `isMadeToOrder`, `isPersonalizable`, `isCustomizable`, `minPrice`, `maxPrice`, `variations` (with per-option prices), `images`, `shopWideSale`, `createdAt`, `renewedAt`                  |
| Fulfillment | `hasFreeShipping`, `shippingCost`, `minProcessingDays`, `maxProcessingDays`, `shipsFromCountryCode`, `acceptsReturns`                                                                                                             |
| Shop        | `shopName`, `shopUrl`, `shopSales` (total sales, also for shops that hide it), `shopSalesHiddenOnSite`, `shopRating`, `shopReviewCount`, `shopFollowers`, `shopActiveListings`, `shopCountryCode`, `shopOpenedAt`, `isStarSeller` |

### Track Etsy over time with scheduled runs

Most of the value comes from running the same search again and comparing. Apify can run the actor for you on a schedule and send the results wherever you need them.

#### 1. Save your input as a task

Open the actor, set your keywords and options, then click **Save as a new task**. A task is a saved input you can run again with one click or on a schedule.

#### 2. Schedule the task

In Apify Console, go to **Schedules**, click **Create new**, choose how often to run (for example daily at 06:00 in your time zone), and add your task. See [Schedules](https://docs.apify.com/platform/schedules) and [How to create a schedule](https://help.apify.com/en/articles/7120817-how-to-create-a-schedule).

Watch how to schedule an Actor:

https://www.youtube.com/watch?v=1jI7WcVQmwM

Or schedule a saved task:

https://www.youtube.com/watch?v=GRFW\_Loo2dk

Common schedules:

|                                  |                 |                 |
| -------------------------------- | --------------- | --------------- |
| Use case                         | Frequency       | Cron expression |
| Rank and ad tracking             | Daily           | `0 6 * * *`     |
| Shop sales from `shopSales`      | Daily           | `0 0 * * *`     |
| Trend and niche research         | Weekly (Monday) | `0 6 * * 1`     |
| Seasonal launches (Q4, holidays) | Twice a day     | `0 6,18 * * *`  |

#### 3. Send the results to a spreadsheet

Connect Google in the task's **Integrations** tab to save every run to Google Sheets or Google Drive automatically:

https://www.youtube.com/watch?v=IFTeKdj6ZGM

#### 4. Compare runs

Every row has `listingId`, `shopId` and `scrapedAt`, so runs are easy to join:

- **Daily shop sales:** for each `shopId`, today's `shopSales` minus yesterday's.
- **Demand trend:** `inCartCount` and `favorites` per `listingId` over time.
- **Rank movement:** `searchRank` per `listingId` and `query`. Set `dedupeAcrossQueries` to `false` so every keyword keeps its full list.
- **New competitors:** `listingId`s that appear for the first time, and their `createdAt`.
- **Price changes:** `price` and `salePrice` per `listingId`.

### Input reference

|                       |                  |               |                                                                                                                    |
| --------------------- | ---------------- | ------------- | ------------------------------------------------------------------------------------------------------------------ |
| Field                 | Type             | Default       | Effect                                                                                                             |
| `queries`             | Array of strings | Required      | Keywords, one search per entry. An Etsy search URL also works; its `q=` keyword is used.                           |
| `maxResultsPerQuery`  | Integer          | `100`         | Maximum listings saved per search. `0` means no limit.                                                             |
| `includeDetails`      | Boolean          | `true`        | Adds the detail fields above. One extra request per listing, charged as a separate event.                          |
| `sortBy`              | String           | `"relevance"` | `"relevance"`, `"newest"`, `"price_asc"`, `"price_desc"`, or `"top_reviews"`.                                      |
| `minPrice`/`maxPrice` | Integer          | Empty         | Price range in US dollars.                                                                                         |
| `shipTo`              | String           | `"US"`        | Search as a shopper in this country (2-letter code). Etsy only shows listings that ship there. Prices stay in USD. |
| `shopLocation`        | String           | Empty         | Only shops in this country (2-letter code).                                                                        |
| `onSaleOnly`          | Boolean          | `false`       | Only discounted listings.                                                                                          |
| `freeShippingOnly`    | Boolean          | `false`       | Only listings with free shipping.                                                                                  |
| `digitalOnly`         | Boolean          | `false`       | Only instant digital downloads.                                                                                    |
| `vintageOnly`         | Boolean          | `false`       | Only vintage items.                                                                                                |
| `personalizableOnly`  | Boolean          | `false`       | Only personalizable listings.                                                                                      |
| `includeAds`          | Boolean          | `true`        | Keep sponsored results. Turn off for organic results only.                                                         |
| `dedupeAcrossQueries` | Boolean          | `true`        | Save each listing once per run. Turn off to get each search's full result list, for example for rank tracking.     |

### Ready-to-run examples

#### Find what's selling now in a niche

```json
{
    "queries": ["halloween sweatshirt", "ghost mug", "fall wreath"],
    "maxResultsPerQuery": 300,
    "includeAds": false
}
```

Sort the dataset by `inCartCount` or `boughtLast24h`. Listings with high cart counts and a recent `createdAt` are newer products that are already selling.

#### Track your keyword ranks and competitors' ads every day

```json
{
    "queries": ["personalized leather wallet", "custom family ornament"],
    "maxResultsPerQuery": 200,
    "includeDetails": false,
    "dedupeAcrossQueries": false
}
```

Save it as a task and schedule it daily. Search data without details is the cheapest way to follow `searchRank`, `isAd` and prices.

#### Research tags for digital products

```json
{
    "queries": ["digital planner", "budget spreadsheet"],
    "maxResultsPerQuery": 100,
    "digitalOnly": true
}
```

Open the **Tags & SEO** view and count which `tags` the top-ranked listings share.

#### Compare a market abroad

```json
{
    "queries": ["linen apron"],
    "shipTo": "GB",
    "shopLocation": "GB",
    "maxResultsPerQuery": 200
}
```

Run it again with another country to compare competition and prices.

#### Collect a whole niche

```json
{
    "queries": ["ceramic mug"],
    "maxResultsPerQuery": 5000,
    "includeDetails": false,
    "includeAds": false
}
```

The actor splits the search into price ranges once it reaches Etsy's limit of about 1,000 results.

### Output

Results go to the run's default dataset. Use the views **Overview**, **Demand & shops**, **Ranking & ads**, or **Tags & SEO**, or export everything as JSON, CSV, Excel, XML or HTML.

This is an illustrative record with details. Values are examples.

```json
{
    "listingId": 1549249672,
    "title": "14K Gold Initial Necklace with Birthstone",
    "url": "https://www.etsy.com/listing/1549249672/14k-solid-gold-initial-necklace-with",
    "price": 42.71,
    "salePrice": 29.9,
    "discount": "(30% off)",
    "currency": "USD",
    "isAd": false,
    "query": "necklace",
    "page": 1,
    "pagePosition": 3,
    "searchRank": 2,
    "inCartCount": 1005,
    "boughtLast24h": 19,
    "favorites": 23468,
    "listingRating": 4.79,
    "listingReviewCount": 3051,
    "isBestseller": true,
    "tags": ["personalized jewelry", "gift for her", "birthstone necklace"],
    "category": "Monogram & Name Necklaces",
    "lastSaleAt": "2026-09-30T22:01:15.000Z",
    "createdAt": "2023-09-10T13:26:20.000Z",
    "shopName": "StellaGoldJewelry",
    "shopSales": 33989,
    "shopSalesHiddenOnSite": true,
    "shopRating": 4.8,
    "shopCountryCode": "TR",
    "isStarSeller": false,
    "scrapedAt": "2026-10-01T12:00:00.000Z"
}
```

#### Notes on fields

- `searchRank` is the position among organic results, as a shopper sees them. It is `null` for ads and for listings found by price splitting, which are ranked within a price range, not the whole search.
- `page` and `pagePosition` give where the listing appeared, including ad slots.
- Each listing is saved once per search. If Etsy shows it first as an ad and later as an organic result, the row describes the first appearance.
- On a search row, `shopRating` and `shopReviewCount` are the shop's. The listing's own rating is `listingRating`, from details.
- `price` and `salePrice` are in US dollars. With variations, `price` is the starting price; details add `minPrice`, `maxPrice` and per-option prices.
- `boughtLast24h` is set only when Etsy shows its "In demand" message; `null` means no message, not zero sales.
- `shopSales` is the shop's total, not the listing's. Etsy doesn't publish per-listing sales.
- Missing values are `null`. Titles and descriptions have HTML entities decoded. Every row has `scrapedAt` in UTC.

### FAQ

#### Why did I get few or no results?

Use short keywords a shopper would type, such as `leather journal`, not a full listing title. Narrow filters (price range, shop location, on sale) also reduce results. The log says when Etsy returned nothing.

#### How many results can I get?

Etsy stops each search at about 1,000 results. Above that, the actor splits the search into price ranges and keeps going until it reaches your limit or every range is below Etsy's limit. Broad keywords have hundreds of thousands of listings, so with `maxResultsPerQuery: 0` a run can take long and cost more. Set a limit or a maximum cost per run.

#### How fast is it?

Etsy takes about a second to answer each request. In our tests, 3,000 search results without details took about 4 minutes, and 80 listings with details took under 40 seconds. Details add one request per listing, run in parallel.

#### Why did my run stop before reaching my limit?

If the log says the charge limit was reached, the run hit its maximum cost or your account's spending limit. Everything collected before that is saved in the dataset. Raise the limit in the run options to collect more.

#### Are the sales numbers estimates?

No. `shopSales`, `inCartCount`, `boughtLast24h`, `favorites` and ratings are Etsy's own numbers at the time of the run. Etsy doesn't publish sales per listing, so the actor doesn't invent them. For sales over a period, compare `shopSales` between scheduled runs.

#### Why do ranks differ from what I see on etsy.com?

Etsy personalizes results for signed-in shoppers, rotates ads on every page load, and changes results by country. The actor searches as a signed-out shopper in the `shipTo` country. Expect small differences, and compare ranks between runs of the same task.

#### Do I need proxies, an Etsy account or an API key?

No. The actor handles connections and retries itself. It doesn't log in and doesn't use your Etsy account.

#### How is this different from Etsy's official API?

Etsy's Open API needs an approved developer app and doesn't offer search ranking, ads, cart counts or "bought in 24 hours". This actor returns what a shopper sees in search, plus those signals.

#### Is it legal to scrape Etsy?

The actor collects publicly visible listing and shop data and no buyer data. You are responsible for how you use it, including Etsy's terms and laws such as GDPR.

#### Can it scrape a specific shop or its reviews?

Use [Etsy Shop Scraper](https://apify.com/epicscrapers/etsy-shop-scraper) for a shop's full catalog, profile, contact details and reviews.

### Cost

This actor uses pay-per-event pricing. Check the current prices in the **Pricing** tab. Events:

- `listing`: one listing saved.
- `listing-detail`: listing details added to a listing, charged only when the detail request succeeds.

Turn off `includeDetails` for the cheapest runs, for example for daily rank tracking. Set a maximum cost per run; the actor stops cleanly when it's reached.

### Support

Report problems or request features in the **Issues** tab. Include the run ID, the input and what you expected. We read every issue.

This actor is not affiliated with or endorsed by Etsy, Inc. "Etsy" is a trademark of Etsy, Inc.

# Actor input Schema

## `queries` (type: `array`):

What a shopper would type in Etsy's search box, one search per entry (e.g. ceramic mug). An Etsy search URL also works; its q= keyword is used.

## `maxResultsPerQuery` (type: `integer`):

Maximum listings saved per search. Etsy shows about 1,000 results per search; above that, the actor splits the search into price ranges to collect more. Set to 0 for no limit (can be hundreds of thousands for broad keywords).

## `includeDetails` (type: `boolean`):

Adds live in-cart count, sales in the last 24 hours, tags, favorites, shop name and total shop sales, ratings, variations and more. One extra request per listing, charged as a separate event. Turn off for the fastest, cheapest run with search-card data only (title, price, rating, rank, ad slot).

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

Etsy's sort order. Relevance matches what shoppers see by default.

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

Only listings at or above this price in US dollars.

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

Only listings at or below this price in US dollars.

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

Search as a shopper in this country (2-letter code, e.g. US, GB, DE). Etsy only shows listings that ship there. Prices stay in USD.

## `shopLocation` (type: `string`):

Only shops located in this country (2-letter code, e.g. US, GB). Leave empty for anywhere.

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

Only discounted listings.

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

Only listings with free shipping to the ship-to country.

## `digitalOnly` (type: `boolean`):

Only instant digital downloads.

## `vintageOnly` (type: `boolean`):

Only vintage items.

## `personalizableOnly` (type: `boolean`):

Only listings that can be personalized.

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

Keep sponsored results (isAd is true, with their page and slot). Turn off to save only organic results.

## `dedupeAcrossQueries` (type: `boolean`):

Save each listing once per run even if several searches find it. Turn off to get every search's full result list, e.g. for rank tracking.

## Actor input object example

```json
{
  "queries": [
    "ceramic mug"
  ],
  "maxResultsPerQuery": 100,
  "includeDetails": true,
  "sortBy": "relevance",
  "shipTo": "US",
  "onSaleOnly": false,
  "freeShippingOnly": false,
  "digitalOnly": false,
  "vintageOnly": false,
  "personalizableOnly": false,
  "includeAds": true,
  "dedupeAcrossQueries": true
}
```

# Actor output Schema

## `results` (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 = {
    "queries": [
        "ceramic mug"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("epicscrapers/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 = { "queries": ["ceramic mug"] }

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

```

## MCP server setup

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