# Etsy Search Scraper (`mlg14/etsy-search-scraper`) Actor

Collect organic Etsy search listings by keyword with rank, title, price, image, shop and ratings. Optionally enrich each listing with description and stock.

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

## Pricing

$4.80 / 1,000 results

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 Search Scraper

Scrape Etsy search listings by keyword and collect organic rank, listing title, price, image, shop, and visible rating data. Export Etsy search data to CSV, JSON, or Excel for product research, price monitoring, and ranking checks without building a separate Etsy search integration.

The scraper accepts several buyer phrases in one run. Each phrase is searched and ranked independently, so a listing can appear once for each phrase where it ranks. Every dataset row includes the original search term and a clean listing URL.

### What data can you extract from Etsy?

The public search page supplies the core fields below. Optional listing detail requests can add a description, category, available quantity, item rating, item review count, and availability when the listing page is reachable. Fields that Etsy does not disclose in the accessible public pages stay empty; an empty value is never an estimate.

| Field | Description | Example |
| --- | --- | --- |
| searchTerm | Input phrase that returned the item | ceramic mug |
| rank | Organic position for that phrase, excluding ads | 1 |
| page | Search results page number | 1 |
| listingId | Stable numeric listing identifier | 1510223855 |
| url | Listing link without tracking parameters | Public listing link |
| title | Search card title | The Libby Travel Mug... |
| image | Primary product photo URL | Product image link |
| price | Displayed current price | 34.99 |
| currency | Displayed currency code | USD |
| shopName | Shop name shown on the card | Seller shop name |
| shopRating | Rating shown on the search card | 4.9 |
| shopReviewCount | Approximate count displayed beside that rating | 16800 |
| description | Full description when detail fetching succeeds | Product description |
| quantity | Available quantity from listing structured data | 5684 |
| rating | Item rating from listing structured data | 5.0 |
| reviewCount | Item review count from listing structured data | 245 |
| category | Listing category path | Home & Living |
| availability | Availability label from listing structured data | InStock |
| views | Lifetime listing views, if disclosed | null when hidden |
| viewsPerDay | Derived daily views, if inputs are disclosed | null when hidden |
| favorers | Favorite count, if disclosed | null when hidden |
| favRatePct | Favorite rate, if inputs are disclosed | null when hidden |
| ageDays | Listing age, if creation date is disclosed | null when hidden |
| madeIn | Manufacturing origin, if disclosed | null when hidden |
| partnerName | Disclosed production partner, if available | null when hidden |
| partnerLocation | Disclosed partner location, if available | null when hidden |
| whoMade | Maker disclosure, if available | null when hidden |
| shopSales | Exact public shop sales count, if available | null when hidden |
| shopFollowers | Public shop follower count, if available | null when hidden |
| tags | Actual listing tags, if publicly disclosed | null when hidden |

The output uses camelCase field names and a flat record for each result. shopReviewCount reflects the abbreviated number on the search card; a displayed value such as 16.8k is converted to approximately 16800. Treat it as a displayed approximation, not an audited total. The rating and reviewCount fields refer to the item when its listing page can be read. They are distinct from the search card's shop rating and count.

### How to scrape Etsy search results

1. Enter one or more phrases a shopper would type, such as ceramic mug or linen table runner, in Search terms.
2. Set Results per term. Eight organic listings appeared on each page during verification, so a target of 32 usually needs at least four pages.
3. Optionally set a minimum or maximum displayed price. The scraper checks prices after receiving search cards and keeps visiting pages until it reaches the requested result count or page limit.
4. Run the actor. Results appear in the dataset with the input phrase, organic rank, listing URL, photo, price, and shop information.
5. Download the dataset as JSON, CSV, or Excel, or connect it to a scheduled workflow.

Sponsored cards are recognized and excluded from the organic ranking. If you compare rank over time, use the same phrase and a similar run schedule: live results and advertisements can change between requests.

### Input

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| searchTerms | Array of strings | Required | Buyer phrases. Each phrase is paged independently. |
| maxResultsPerTerm | Integer | 50 | Maximum saved organic listings for each phrase. |
| maxItems | Integer | 0 | Overall dataset cap; zero means no overall cap. |
| maxPagesPerTerm | Integer | 50 | Search pages to attempt for each phrase, up to 100. |
| minPrice | Number | Empty | Keep listings at or above this displayed price. |
| maxPrice | Number | Empty | Keep listings at or below this displayed price. |
| includeDescription | Boolean | false | Try listing pages for description, quantity, category, item rating, review count, and availability. |
| proxyConfiguration | Object | Enabled | Proxy settings for public page access. |

A practical input for a small niche review is:

```
{
  "searchTerms": ["ceramic mug", "stoneware coffee cup"],
  "maxResultsPerTerm": 32,
  "maxItems": 64,
  "maxPagesPerTerm": 8,
  "minPrice": 20,
  "maxPrice": 80,
  "includeDescription": false
}
```

Price filtering happens on the fetched search results. It does not ask Etsy to restrict the search before ranking, so a filtered dataset may skip rank numbers. For example, ranks 1 and 2 may cost less than the selected minimum, making rank 3 the first saved row. Increase maxPagesPerTerm if the price band is narrow.

maxResultsPerTerm is a saved-result target, not a promise that the site will provide that many unique listings. A phrase can have few results, pages can repeat items, and access can be limited temporarily. The scraper deduplicates listing IDs within each term and stops when pages stop providing fresh listings.

### Output example

This shortened example is drawn from a successful search for ceramic mug. The full dataset row also includes the other documented fields, with empty values where the public source has no data.

```
{
  "searchTerm": "ceramic mug",
  "rank": 2,
  "page": 1,
  "listingId": "1510223855",
  "url": "https://www.etsy.com/listing/1510223855/the-libby-travel-mug-hand-thrown-ceramic",
  "title": "The Libby Travel Mug hand thrown ceramic mug cup Pottery Travel Mug Cup with attached lid fits in cup holder",
  "image": "https://i.etsystatic.com/8740835/r/il/d4f720/7239142492/il_255x319.7239142492_oym6.jpg",
  "price": 34.99,
  "currency": "USD",
  "shopRating": 4.9,
  "shopReviewCount": 16800,
  "views": null,
  "favorers": null,
  "tags": null
}
```

Prices, ranks, photos, and availability are snapshots. Repeated searches may return different ordering, especially around promotions or seasonal demand. Save the run date beside the exported dataset if you plan to compare snapshots later.

### Use cases

- Keyword position checks: Search the same phrase on a schedule and compare a known listingId across the organic rank values. The listing URL and search term make it easy to join snapshots.
- Competitive price research: Review current prices in a niche, filter to a useful price band, and compare products occupying nearby organic positions.
- Product assortment research: Collect titles and photos across related buyer phrases to see which product types recur and which descriptions distinguish them.
- Shop concentration checks: Group rows by shopName to see whether a few sellers occupy many visible results for a niche.
- Merchandising review: Inspect result photos and titles together to study how top listings present size, material, occasion, or use.
- Category monitoring: Schedule a recurring set of terms and export each dataset to a spreadsheet for a simple history of rank and price changes.

These are observations from public search results. A ranking position is specific to the phrase and the crawler's browsing context. It is not a guarantee that every shopper sees the same sequence.

### How much does it cost to scrape Etsy?

The configured result charge is **$4.80 per 1,000 saved listings**, or **$0.0048 per listing**. A result is charged when it is delivered to the dataset. Pages that contain ads or duplicates are inspected but do not create extra result rows.

| Saved results | Result charge |
| ---: | ---: |
| 32 | $0.1536 |
| 250 | $1.20 |
| 1,000 | $4.80 |

These examples describe the result charge at the current setting and do not include taxes or account-specific adjustments. The run screen shows applicable charges before or after execution. Requesting listing descriptions can add time and traffic without increasing the number of saved results. Keep that option off when search-card fields meet your needs.

A low result limit is useful for checking a new phrase before collecting hundreds of rows. For repeat monitoring, a stable limit also makes it easier to compare the same number of visible organic positions on each date.

### Tips for best results

Start with a specific phrase. Broad terms can shift quickly and may mix different product types, making comparisons difficult. Search handmade ceramic travel mug when you need that niche rather than collecting a broad mug dataset and assuming it represents the same buyer intent.

Use several related phrases in one input when comparing wording. Each phrase has its own rank sequence and result limit. The same listing can legitimately appear once for each phrase. Deduplicate by listingId across terms only if your analysis needs a single record per item.

Leave detail fetching off for rank and price work. Search cards already include title, photo, price, shop, and visible rating information. Turning on includeDescription makes one extra request per saved listing, and listing pages may be blocked even when search pages work.

Set maxPagesPerTerm with the result target in mind. During verification, pages exposed about eight organic cards plus sponsored placements. At that density, 32 organic rows require around four pages; a tighter price band may require more. A large page limit can increase run time and proxy traffic without increasing unique results when the site repeats listings.

Expect prices in the currency displayed to the crawler. The verified browse location returned USD. Store the currency column with price rather than treating bare price numbers as interchangeable across regions.

### Limits

Etsy currently blocks direct /search?q= requests in this environment, while the trailing-slash /search/?q= route returned full public search pages. The actor uses that working route. Etsy can change routing, markup, rate limits, and access rules at any time.

The accessible search page includes a small JSON-LD item list, but it does not cover all result cards. The actor reads rendered public search-card markup to capture the complete visible organic set. If Etsy changes those cards, a future update may be needed.

Views, favorites, daily view velocity, actual listing tags, manufacturing disclosures, and production partners were not present as reliable fields in the accessible public pages. Their columns stay null. In particular, related-search phrases on a listing page are not treated as the seller's tags. The actor does not invent a view count, infer a production partner from a photo, or turn an unrelated keyword into a listing tag.

Individual listing pages were reachable during a probe but returned repeated 403 responses during the detail-enabled verification run. Optional detail fields are therefore best effort. After two consecutive detail blocks, the actor stops trying detail pages for that term and still saves the search-card results. For a reliable rank, title, price, and shop export, leave includeDescription off.

Search rank is organic rank in the returned page sequence after sponsored cards are removed. It is not a global, personalized, or permanent Etsy ranking. Results can vary by time, location, language, and the site's own experiments. The actor deduplicates repeated cards and stops when pages no longer yield fresh listing IDs. A 100-page safety limit also bounds requests per phrase.

### Automated research workflows

The input and output support scheduled keyword checks and downstream analysis. Two example instructions for a workflow are:

- Search for ceramic mug and stoneware coffee cup, save 32 organic listings per phrase, and compare median price and repeated shops.
- Collect 50 results for linen table runner each week and flag listing IDs whose organic position or displayed price changed.

When interpreting a dataset automatically, treat empty fields as unavailable public data. Do not replace missing views, favorers, or tags with guessed values. The searchTerm, listingId, rank, and currency columns provide the context needed to join and compare exports.

### FAQ

#### Is it legal to collect these results?

The actor reads public search pages. You are responsible for how you use exported data. Respect the site's terms, applicable privacy rules, and the rights of sellers. Avoid using the results for personal-data misuse.

#### Do I need to configure a proxy?

A proxy is enabled by default. Datacenter search requests were blocked in verification, so the actor uses residential access for the working search route. Change proxy settings only when you have a specific routing requirement.

#### How fast is a run?

The search-only golden run saved 32 listings successfully. Run time depends on the number of terms, pages, blocked requests, and optional detail requests. A narrow price filter may require more pages than an unfiltered search for the same number of saved rows.

#### Why are views and tags empty?

The public pages reached during verification did not disclose dependable listing view counts or seller tag lists. Empty values signal that the source did not provide them. Related suggestions and review labels are different data and are not substituted for tags.

#### Why can description or quantity be empty?

Those values require the individual listing page. The search page can work while listing pages return an access block. The actor keeps the search result and leaves detail-only fields empty if the listing page is unavailable.

#### Can I schedule a recurring check?

Yes. Use a saved input with consistent terms and limits, run it on a schedule, and compare datasets by searchTerm and listingId. Compare positions within the same phrase, and allow for changes in live search ordering.

#### Can I export to a spreadsheet?

Yes. Download the dataset in CSV or Excel format, or use JSON for a programmatic pipeline. Keep price and currency together, and preserve rank as a number when importing.

### Integrations

The default dataset can be read through its API, sent by webhook after a run, or exported for spreadsheet and automation workflows. Scheduled runs can refresh a keyword set, while a downstream step can compare current rows with earlier exports using the listing ID and search term. Keep the original run timestamp in your storage when building historical reports.

### Support

Open an issue on the Issues tab; we reply within 24h and add fields on request.

# Actor input Schema

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

One or more buyer search phrases. Each phrase is paged and ranked separately.

## `maxResultsPerTerm` (type: `integer`):

Maximum organic listings to save for each search phrase.

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

Stop after this many listings across all terms. Zero means no total cap.

## `maxPagesPerTerm` (type: `integer`):

Maximum search pages to visit per phrase. A page currently shows about eight organic listings.

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

Keep listings priced at or above this amount in the displayed currency. Sent to Etsy as a search filter, so result pages come back full.

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

Keep listings priced at or below this amount in the displayed currency. Sent to Etsy as a search filter, so result pages come back full.

## `includeDescription` (type: `boolean`):

Open each listing page to add its description, stock quantity, category, item rating, review count, and availability. Adds one request per result.

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

Proxy settings for public pages. Residential access is used because datacenter search requests were blocked during verification.

## Actor input object example

```json
{
  "searchTerms": [
    "ceramic mug"
  ],
  "maxResultsPerTerm": 50,
  "maxItems": 0,
  "maxPagesPerTerm": 50,
  "includeDescription": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

Organic search listings 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 = {
    "searchTerms": [
        "ceramic mug"
    ]
};

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

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

```

## MCP server setup

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