How to Find Trending Etsy Products Before They Peak
Every Etsy seller eventually faces the same question: what should I make next? The answer lives in the search page. When listings created in the last few weeks already rank on the first page of a relevance-sorted search, that category is heating up — buyers are gravitating to a new aesthetic, the supply side has not yet flooded in, and the early entrants are still being rewarded by the algorithm. The hard part is spotting this window before the market peaks. This guide walks the full discovery loop: scraping search results, filtering by listing recency and review velocity, comparing snapshots week-over-week, and using a competitor shop's new releases as a validated trend signal.
Why the Search Page Is the Right Signal
The cleanest source of truth for what is trending on Etsy is the search result set sorted by relevance. Etsy's relevance algorithm blends recency, conversion rate, favorites velocity, review quality, and listing freshness. A listing only ranks highly if real shoppers are clicking, favoriting, and buying it — and those signals are accumulated by Etsy in real time. By scraping the top results for a seed query, sellers get the algorithm's own composite assessment of what is working right now, distilled into a ranked list of forty listings per page.
The same data exists nowhere else in a useful form. Etsy publishes no public "trending products" feed, the affiliate API restricts what you can query and stores no historical state, and third-party trend dashboards (eRank, Marmalead, Alura) are themselves derived from the same public listing data — just packaged with a subscription. Scraping the source directly gives a research team raw access to the same underlying signal those tools resell, with no caps on which categories they can analyze and no ceiling on historical retention.
The other reason the search page beats every alternative is the listing creation date. When that field is present in the markup, it is the closest thing Etsy ships to a public "this product is new" flag. Combined with rank position, it answers the question every seller actually has: "is something brand new already winning, and what does it look like?"
What Etsy Search Actually Returns
Per listing on a search-page scrape, the result includes:
| Field | Example |
|---|---|
listing_id | 1547892304 |
title | Cottagecore Soy Candle, Lavender & Sage, Hand-Poured |
url | https://www.etsy.com/listing/1547892304/cottagecore-soy-candle |
price | 18.50 |
currency | USD |
original_price | 24.00 |
shop | TheWildwoodCo |
shop_url | https://www.etsy.com/shop/TheWildwoodCo |
image | https://i.etsystatic.com/.../il_794xN.1547892304_xyz.jpg |
favorites | 412 |
rating | 4.9 |
reviews | 38 |
is_bestseller | true |
free_shipping | false |
listed_at | 2026-05-04T00:00:00Z |
Two of those fields carry the trend signal. The listed_at date tells you the listing's age in days. The reviews count tells you how much purchase momentum it has gathered. The ratio of the two is the metric the cleaning section below computes.
What the search page does not include is the listing's full description, full image gallery, materials, or variations — for those, a downstream call to the product endpoint per shortlisted listing is required.
Building the Search URL
Etsy search URLs are easy to construct manually because the parameters live in the query string. The two parameters that matter for trend research are q (the search query) and order (the sort).
- Open
etsy.comin a browser. - Search for a seed term —
cottagecore candle,birth flower necklace,mushroom phone case. - The URL now looks like
https://www.etsy.com/search?q=cottagecore+candle. This is sort-by-relevance by default, which is what trend research wants. - Optionally add
&order=most_recentfor a recency-first view (useful for spotting which new listings exist at all, regardless of whether they have traction yet).
Three example URLs to test with:
https://www.etsy.com/search?q=cottagecore+candle
https://www.etsy.com/search?q=birth+flower+necklace
https://www.etsy.com/search?q=mushroom+phone+case&order=most_recent
The first two are relevance-sorted (what the algorithm currently rewards). The third is recency-sorted (what is being listed right now). Run both for each seed and the difference between the two result sets is itself a useful signal — listings that appear in both are newly-listed items already winning on relevance, which is the textbook definition of a rising star.
The API Call
Every LogPose Etsy endpoint is asynchronous — submit a job, poll until done, fetch the result. Submit with curl first to confirm the URL works:
curl -G "https://api.logposervices.com/api/v1/ecommerce/etsy/search" \
-H "X-API-Key: lp_xxxxxxx" \
--data-urlencode "url=https://www.etsy.com/search?q=cottagecore+candle" \
--data-urlencode "pages=3"
# → {"job_id": "etsy_a91c..."}
curl -H "X-API-Key: lp_xxxxxxx" \
"https://api.logposervices.com/api/v1/jobs/etsy_a91c?wait=true&timeout=60"
curl -H "X-API-Key: lp_xxxxxxx" \
https://api.logposervices.com/api/v1/jobs/etsy_a91c/result
Etsy returns about 40 listings per page, so pages=3 is roughly 120 listings — enough to see the top of the category clearly without overpaying for ranking noise further down. Most 3-page jobs finish in 30–60 seconds.
The Python Pipeline
This is the script most product researchers end up running on a weekly cron. It takes one seed search URL, pulls three pages, computes review velocity, and writes a CSV sorted by the trend signal:
import os, time, csv, requests
from datetime import datetime, timezone
API_KEY = os.environ["LOGPOSE_API_KEY"]
BASE = "https://api.logposervices.com/api/v1"
HEADERS = {"X-API-Key": API_KEY}
def submit_and_wait(path: str, params: dict, timeout_s: int = 180) -> dict:
r = requests.get(f"{BASE}/{path}", params=params, headers=HEADERS, timeout=30)
r.raise_for_status()
job_id = r.json()["job_id"]
deadline = time.time() + timeout_s
while time.time() < deadline:
s = requests.get(f"{BASE}/jobs/{job_id}", headers=HEADERS, timeout=15).json()
if s["status"] == "completed":
break
if s["status"] == "failed":
raise RuntimeError(s.get("error", "unknown failure"))
time.sleep(2)
else:
raise TimeoutError(f"job {job_id} did not finish in {timeout_s}s")
return requests.get(f"{BASE}/jobs/{job_id}/result", headers=HEADERS, timeout=15).json()
def days_since(iso_ts: str | None) -> int | None:
if not iso_ts:
return None
dt = datetime.fromisoformat(iso_ts.replace("Z", "+00:00"))
return max(1, (datetime.now(timezone.utc) - dt).days)
def scrape_to_csv(search_url: str, pages: int, out_path: str) -> int:
data = submit_and_wait(
"ecommerce/etsy/search",
{"url": search_url, "pages": pages},
)
rows = []
for i, r in enumerate(data["listings"], start=1):
age = days_since(r.get("listed_at"))
reviews = r.get("reviews") or 0
rows.append({
"rank": i,
"title": r.get("title"),
"shop": r.get("shop"),
"price": r.get("price"),
"favorites": r.get("favorites"),
"reviews": reviews,
"rating": r.get("rating"),
"age_days": age,
"review_velocity": round(reviews / age, 3) if age else None,
"url": r.get("url"),
})
with open(out_path, "w", newline="", encoding="utf-8") as f:
w = csv.DictWriter(f, fieldnames=list(rows[0].keys()))
w.writeheader()
w.writerows(rows)
return len(rows)
if __name__ == "__main__":
n = scrape_to_csv(
"https://www.etsy.com/search?q=cottagecore+candle",
pages=3,
out_path="cottagecore_candles.csv",
)
print(f"wrote {n} listings")
Run it once and you have a ranked snapshot of the category. Run it weekly with the same seed URL and you can compute deltas — which is where the actual trend signal lives.
Cleaning the Output to Surface Real Trends
Raw output from one 3-page job is usually 110–125 rows. Four cleaning steps turn that into a usable trend report:
import pandas as pd
df = pd.read_csv("cottagecore_candles.csv")
# 1. Drop listings with no creation date — can't compute velocity
df = df[df["age_days"].notna()]
# 2. Drop listings under 7 days old — too little data to score
df = df[df["age_days"] >= 7]
# 3. Drop listings with fewer than 3 reviews — noise floor
df = df[df["reviews"] >= 3]
# 4. Filter to listings created in the last 90 days — the trend window
recent = df[df["age_days"] <= 90].copy()
# 5. Sort by review velocity desc — the actual trend metric
recent = recent.sort_values("review_velocity", ascending=False)
recent.head(20).to_csv("cottagecore_candles_trending.csv", index=False)
The age_days <= 90 filter is the highest-leverage step. It strips out the established bestsellers that dominate any relevance-sorted result set and isolates the listings that are new but already converting. The top 20 rows after this filter are the working list — products created in the last three months that are already gathering reviews faster than the category baseline. For most seed queries, between 5 and 15 listings will pass all four filters; those are the rising stars worth studying in detail.
Comparing Snapshots Week-Over-Week
A single snapshot is a stock metric. The actual trend signal is the delta between two snapshots. The pattern is straightforward — save each week's results under a dated filename, then diff by listing ID:
import pandas as pd
last_week = pd.read_csv("cottagecore_candles_2026-05-21.csv")
this_week = pd.read_csv("cottagecore_candles_2026-05-28.csv")
# Listings that appeared net-new in this week's top 120
new_arrivals = this_week[~this_week["listing_id"].isin(last_week["listing_id"])]
# Listings present in both — compute rank movement
both = this_week.merge(
last_week[["listing_id", "rank"]].rename(columns={"rank": "rank_last_week"}),
on="listing_id",
)
both["rank_change"] = both["rank_last_week"] - both["rank"]
risers = both[both["rank_change"] >= 10].sort_values("rank_change", ascending=False)
print(f"{len(new_arrivals)} net-new listings on page 1")
print(f"{len(risers)} listings that jumped 10+ ranks")
Net-new arrivals on page one are listings the algorithm has just promoted — a strong leading indicator. Listings that jumped 10+ ranks week-over-week are existing items gaining momentum. Combined, the two lists are a focused weekly view of where the category's energy is moving.
The technique generalizes to any e-commerce search page where the result order encodes a popularity signal. The same pattern appears in How to monitor Amazon competitor pricing daily for a price-focused variant.
Tracking a Competitor Shop's New Releases
A high-revenue Etsy shop has already done the trend validation work — when they release a new variant, the research behind that decision is implicit in the listing. Riding that signal is the most-undervalued move in Etsy research, and the shop endpoint makes it trivial:
shop_data = submit_and_wait(
"ecommerce/etsy/shop",
{"shop": "TheWildwoodCo", "pages": 5},
)
current_ids = {l["listing_id"] for l in shop_data["listings"]}
# Compare against last week's snapshot
import json
with open("wildwood_2026-05-21.json") as f:
previous_ids = set(json.load(f))
new_releases = current_ids - previous_ids
print(f"{len(new_releases)} new listings since last week")
Snapshot the shop weekly, diff by listing ID, and the delta is the seller's net-new releases. Five pages typically covers a shop's full active inventory unless they have several hundred items. The signal is high-quality because it filters out everything except the products a proven seller is actively betting on this week.
Scaling Beyond a Single Seed Query
One seed query gives you one micro-trend. To cover a broader market, run a portfolio of seeds — typically the 10–20 phrases your target buyer types into the search bar. Two patterns work in production.
Cron-driven portfolio. Maintain a YAML or JSON file listing your seed queries, run the pipeline against each one on a weekly schedule, save snapshots by date, and ship the trending-this-week summary as a Sunday-night digest to whoever owns product roadmap decisions.
Bulk submission. Instead of running each seed sequentially, submit the whole portfolio in one bulk request and let the platform parallelize across the proxy pool:
requests.post(
"https://api.logposervices.com/api/v1/ecommerce/etsy/search/bulk",
headers={"X-API-Key": os.environ["LOGPOSE_API_KEY"]},
json={
"targets": [
{"url": "https://www.etsy.com/search?q=cottagecore+candle", "pages": 3},
{"url": "https://www.etsy.com/search?q=birth+flower+necklace", "pages": 3},
{"url": "https://www.etsy.com/search?q=mushroom+phone+case", "pages": 3},
{"url": "https://www.etsy.com/search?q=stanley+cup+accessories", "pages": 3},
{"url": "https://www.etsy.com/search?q=witchy+desk+decor", "pages": 3},
],
},
).raise_for_status()
Bulk submission runs in parallel up to your concurrency cap, which cuts a 20-seed portfolio from 15 minutes sequential to 2–3 minutes wall-clock. For sellers running discovery across multiple niches simultaneously, this is the only practical approach.
For sellers operating across multiple marketplaces, the same weekly-diff pattern applies to Amazon (where BSR replaces review velocity as the primary signal) — see How to track Amazon BSR over time for the cross-channel playbook.
Legality and Ethics
Etsy listing data is public — every field used by this guide is visible to any logged-out shopper browsing etsy.com. Scraping public marketplace listings for competitive research is on settled legal ground in the US (CFAA does not apply to public data per hiQ v. LinkedIn) and is broadly compliant in the EU under the legitimate-interest basis for B2B competitive intelligence. What Etsy's Terms of Service prohibit is automated access to the internal Etsy API without a partnership, and republishing the scraped data as a competing storefront. Researching a category to inform your own product roadmap is not the activity the terms target.
The ethical line is downstream. Copying a specific competitor's listing wholesale — same title, same photos, same product — is both an Etsy policy violation and a trademark/copyright concern. Using the trend data to identify what aesthetic or theme is winning and then producing your own original work in that direction is exactly what every seller in the category is doing simultaneously; that is normal market behavior, not a violation.
Common Mistakes
- Trusting page-one rank as a stand-alone signal. Rank is a composite of factors that include the shop's overall sales history, not just the listing's recent performance. A page-one listing from a 10-year-old shop tells you about the shop, not the trend. Always combine rank with
age_daysto isolate genuinely new winners. - Filtering too aggressively on review count. Setting
reviews >= 20cuts out the listings that are most interesting — recent items mid-rise. A listing two weeks old with 4 reviews is a stronger trend signal than one with 200. Use 3 as the noise floor, not 20. - Scraping daily. Etsy's relevance ranking does not move meaningfully day-to-day. Daily snapshots produce noise that swamps the actual week-over-week signal. Weekly is the right cadence; running tighter than that wastes compute and proxy traffic without adding information.
- Skipping the recency sort cross-check. Relevance-sorted results show what is winning; recency-sorted results show what was just listed. The interesting items are the ones that appear in both — that overlap is the working definition of a rising star, and you only see it if you scrape both sort orders.
- Ignoring the Cloudflare 100-second edge timeout.
api.logposervices.comsits behind Cloudflare, so any single HTTP call that takes 100+ seconds returns a 524 to the client even though the job continues server-side. Always poll for status; never expect a synchronous response on a big page count.
Get Started
- Sign up at logposervices.com and generate an API key under Tool → API Keys.
export LOGPOSE_API_KEY=lp_xxxxxxx- Pick a seed search URL from the Etsy UI and run the Python script above against it.
Related reading: How to monitor Amazon competitor pricing daily for the same diff pattern applied to price tracking, Apify alternative for e-commerce scraping for the broader managed-vs-DIY comparison, and Best Amazon scraper APIs in 2026 for the cross-marketplace tooling landscape.
External: Etsy, hiQ Labs v. LinkedIn.