What 1,090 unique "mushroom lamp" listings reveal about Etsy pricing and promotion
A complete Etsy search run shows a market built around mid-priced products, constant discounting, and a sponsored tier that is materially more expensive than the organic-only catalog. The duplicate placements matter too: the raw export contained 1,275 rows, but only 1,090 unique listing IDs.
// methodology
run 2026-07-18- captured rows
- 1,275
- unique listings
- 1,090
- unique shops
- 561
- run / charge
- 24s / $5.10
analysis unit
- The actor requested up to 30 US search pages for mushroom lamp; Etsy returned 21 pages before the run completed.
- Market statistics deduplicate by listing_id and retain the row with the smallest captured rank, producing 1,090 unique listings.
- The complete 1,275-row dataset is used only when a finding is explicitly described as captured search-result rows or page placement.
- Percentages use 1,090 unique listings as the denominator unless another unit is stated.
limits
- 178 listing IDs recur across pages, producing 185 duplicate rows; no duplicate occurs twice on the same page.
- 97 listing IDs appeared as both sponsored and organic somewhere in the run. Nine captured rows contain both is_ad: true and an organic_rank, so sponsored counts describe classified rows rather than an exact inventory of every placement.
- ships_from is absent throughout; shop age is missing for 42.8% of rows and shop rating for 10.0%, so those fields are excluded from the analysis.
- Etsy's displayed total changed from 22,143 to 22,146 during the 24-second run; it is a live estimate, not the number collected.
Inspect the run behind the findings.
- collection window · UTC
- 2026-07-18T17:52:55.702Z
to 2026-07-18T17:53:16.840Z - source CSV
- 1,275 rows · 775.7 KiB · not redistributed
- source dataset · SHA-256
- 727d71391b4074a7e3942feae86655d7fe03b7cf76e27472e86e69b8d6a1fc4c
The complete CSV contains third-party public listing, seller, media, and destination data, so it is not republished here. Its checksum records the exact source file used. The public extract contains the first 50 captured rows with listing and shop identifiers pseudonymized and text, media, and destination URLs removed.
python3 analyze_mushroom_lamp.py dataset.csv
The center of the market is $50–74
The median unique-listing price is $55.19. More than a third of the market sits between $50 and $74, and 71.3% falls between $25 and $99. A small luxury tail reaches $6,500, which pulls the mean up to $96.05; the median is the more representative benchmark.
// displayed USD prices; one first-seen row retained per listing_id
Discounting is the default, not the exception
Of 1,090 unique listings, 769 are marked on sale. A numeric discount_pct is available for 756 of them, and its median is 35%. Thirteen sale-marked listings do not expose a parsable original price and are kept in the sale rate but not assigned to a discount band.
// 'none' includes 13 sale-marked listings without a numeric discount_pct
Sponsored-only products occupy a more expensive tier
Across the full run, 229 listing IDs appeared only as sponsored results, 764 appeared only organically, and 97 appeared in both modes. Excluding that overlap gives a clean comparison: the sponsored-only median is $80.27, 50.6% above the $53.30 organic-only median. Sponsored-only products are also less likely to be marked on sale.
| placement group | listings | median price | on sale | Star Seller |
|---|---|---|---|---|
| Sponsored only | 229 | $80.27 | 54.1% | 38.4% |
| Organic only | 764 | $53.30 | 75.3% | 26.7% |
| Appeared in both | 97 | $53.90 | 72.2% | 38.1% |
// the 97 overlap listings are shown for completeness but excluded from the sponsored-only versus organic-only comparison
The data supports a price-positioning difference, not a causal claim: paying for placement does not itself make a product more expensive, and the run captures one keyword at one point in time.
Bestseller is front-loaded; Star Seller is not
The first 100 captured rows contain 95 unique listing IDs after deduplication. Bestseller appears on 9.5% of those listings versus 2.4% of the remaining market—about four times the rate. Popular now is also twice as common near the front. Etsy's Pick is nearly flat, while Star Seller is actually slightly more common outside the first 100 rows. Treating every badge as the same ranking signal would hide that difference.
| badge | first 100 rows · 95 unique | remaining · 995 unique |
|---|---|---|
| Bestseller | 9 · 9.5% | 24 · 2.4% |
| Popular now | 4 · 4.2% | 21 · 2.1% |
| Etsy's Pick | 5 · 5.3% | 50 · 5.0% |
| Star Seller | 26 · 27.4% | 303 · 30.5% |
// descriptive association only; captured rank combines sponsored and organic search-result rows
A large market still has visible seller concentration
The 1,090 unique listings come from 561 shops. The ten most represented shops account for 224 listings, or 20.6% of the deduplicated market. No single shop dominates—GennybooFinds leads with 35 listings—but a relatively small seller group still occupies one fifth of the collected catalog.
| shop | listings | share of unique market |
|---|---|---|
| GennybooFinds | 35 | 3.2% |
| Comptonclothes | 30 | 2.8% |
| Fangfangstory | 29 | 2.7% |
| StarArtisans | 26 | 2.4% |
| PurpleForestUS | 21 | 1.9% |
// shop share counts one row per unique listing_id
Etsy niche research workflow
Exact actor inputs, handoffs, deduplication rules, and cost controls.
// reproduce the source run
These commands use the exact input behind this case study. The CLI and HTTP API start the same Actor run.
apify call astravalabs/etsy-listings-scraper --input '{"keywords":["mushroom lamp"],"maxPages":30,"maxTotalResults":2000,"country":"US","sortOrder":"most_relevant"}'# Set API token
API_TOKEN=<YOUR_API_TOKEN>
# Prepare Actor input
cat > input.json << 'EOF'
{
"keywords": [
"mushroom lamp"
],
"maxPages": 30,
"maxTotalResults": 2000,
"country": "US",
"sortOrder": "most_relevant"
}
EOF
# Run the Actor using the HTTP API
# Full API reference: https://docs.apify.com/api/v2
curl "https://api.apify.com/v2/acts/astravalabs~etsy-listings-scraper/runs?token=$API_TOKEN" \
-X POST \
-d @input.json \
-H 'Content-Type: application/json'// Replace <YOUR_API_TOKEN> before running the cURL example. Keep tokens out of committed files.