Quick answer: Use Etsy's official Open API v3 before you write a single line of scraping code. It exposes listings, shops, prices, images, and shop-level review data through a documented, permitted interface. Etsy's Terms prohibit unauthorized scraping, and the API gives you cleaner, structured data anyway. Reserve public-page fetching for the narrow cases the API does not cover.
If you are researching Etsy for product research, price tracking, or a marketplace analytics tool, the good news is that Etsy actually wants developers to build on their data, within limits. That is what the Open API is for. Let me walk through the right approach.
Start With Etsy Open API v3
Etsy's Open API v3 is the sanctioned way to read Etsy data programmatically. It covers the endpoints most people are reaching for when they say "scrape Etsy":
- Listings: title, description, price, currency, quantity, tags, images.
- Shops: shop details, policies, and the shop's active listings.
- Reviews: shop-level and listing-level reviews.
- Taxonomy: Etsy's category tree for classification.
You register an app, get an API key (the keystring), and call REST endpoints that return JSON. For public read-only data you can use API-key authentication; for anything touching a specific user's account, Etsy uses OAuth 2.0. That gate exists to protect seller and buyer data, and it is the right design.
Rate Limits and Scopes, in Plain Terms
Two concepts govern what you can do with the Open API:
Rate limits. Etsy meters requests per app, per day and per second. In practice this means you design for polling, not real-time hammering: cache listing data, refresh on a schedule, and back off when you hit a limit. Check Etsy's developer docs for the current numbers, since they change.
Scopes. OAuth scopes define what a token is allowed to do (read a shop's listings, read transactions, write inventory, and so on). Request the minimum scopes you need. If you only read public listings, you often do not need user OAuth at all, just the app key.
| Approach | Data | Auth | Standing |
|---|---|---|---|
| Scraping Etsy HTML | Fragile, parsed | None (evades) | Prohibited by ToS |
| Open API v3 (key) | Public listings, shops | API key | Permitted |
| Open API v3 (OAuth) | Account-scoped data | OAuth 2.0 scopes | Permitted, consented |
What Fetching Public Pages Can and Cannot Do
Suppose you hit a genuine gap: some rendered detail on a public listing page that the API does not expose in the shape you need. Fetching a public URL you are permitted to read, at a respectful rate, is a reasonable fallback.
What fetching can do: pull the visible content of a single public listing page as clean markdown or JSON for a one-off enrichment.
What fetching cannot legitimately do: replace the API for bulk collection, access account-only data, or bypass Etsy's bot protection. If the data lives behind login or a challenge, that is a boundary, not an obstacle. For the reasoning here, see whether web scraping is legal.
For permitted public-page enrichment, link.sc fetches a URL and returns clean markdown or structured JSON, which saves you from maintaining a headless browser for occasional pages. Use it on public data you may access, never to defeat protections.
A Code Sketch: Open API First
Here is the primary path: read listings from the Open API v3.
import requests
ETSY_KEY = "your-etsy-keystring"
def get_shop_listings(shop_id):
url = f"https://openapi.etsy.com/v3/application/shops/{shop_id}/listings/active"
r = requests.get(
url,
headers={"x-api-key": ETSY_KEY}, # Etsy's app-key header
params={"limit": 100},
timeout=30,
)
r.raise_for_status()
data = r.json()
return [
{
"listing_id": l["listing_id"],
"title": l["title"],
"price": l["price"]["amount"] / l["price"]["divisor"],
"currency": l["price"]["currency_code"],
"quantity": l["quantity"],
}
for l in data["results"]
]
A quick note on headers so you do not get confused. Etsy's Open API happens to use an x-api-key header for the app key, and for user-scoped calls it adds an Authorization: Bearer OAuth token. link.sc also uses x-api-key, but it is a separate service with its own key format (lsc_...). Different keys, different services.
And the permitted public-page fallback:
import requests
def fetch_public_listing(url):
r = requests.post(
"https://api.link.sc/v1/fetch",
headers={"x-api-key": "lsc_your_key_here"},
json={"url": url, "format": "markdown"},
timeout=60,
)
r.raise_for_status()
return r.json()["content"]
Price Tracking and Product Research
If your goal is competitive pricing or product research, the Open API's listing prices plus a polite refresh schedule is all you need. Cache aggressively, respect the rate limit, and store historical snapshots yourself to build trends. We cover the general architecture (change detection, snapshotting, normalization) in the ecommerce price scraping guide, which applies cleanly on top of an official API.
ToS and Ethics Note
Not legal advice. But the framing is simple: Etsy publishes an Open API precisely so you do not have to scrape, and its Terms prohibit unauthorized scraping. Use the API, request minimal scopes, respect the rate limits, and treat seller and review data as belonging to real people. Reviews in particular can contain personal information, so store and display them responsibly and only as Etsy's terms allow.
The short version: Open API v3 first, minimal scopes, respect rate limits, and reserve fetching for the rare permitted public page the API does not cover.
Want to enrich official API data with clean fetches from the public web? Start free with link.sc.