Etsy API & Scraper
The Etsy API returns handmade and vintage listings, shop and review data as clean JSON.
🤖 Using an AI assistant? Copy this link into ChatGPT / Claude / Cursor — it reads every endpoint and parameter instantly and tells you if this API fits your use case.
The primary listings/search endpoint returns items with listing id, title, price, list price, shop, image and URL (ad-flagged), and you can pull a listings/detail, a shop's detail and listings, a category, and shop or listing reviews. It is built for marketplace research, price monitoring and catalog tools that need Etsy data without a scraper. One ReefAPI key, one shared credit pool, the standard envelope.
Etsy identifiers, price fields and page sizes, as they actually come back
The same idea has a different field name in each action, and two of the identifiers are typed differently depending on where you read them. Everything below was measured on 2026-08-27 against listings 1509738931, 4389097710 and 1112702820 and shop GlamAndGemsNY (shop_id 7357402). Use it to work out which field you are actually holding before you join two responses together.
| Field | Where it appears | What we measured |
|---|---|---|
| listing_id | listings/search, listings/detail, shops/listings | A digit string: "1509738931". The same value appears as schema_product.sku. |
| listing_id / transaction_id | reviews/shop rows | Integers here, not strings: 1899686745 and 5160518010. Cast before joining to a search row. |
| shop_id | listings/search, listings/detail, shops/detail | Numeric string, and it is the first path segment of every i.etsystatic.com image URL. Listing 1509738931 returned shop_id "7357402" and images under i.etsystatic.com/7357402/. reviews/shop takes this number, never the slug. |
| price + list_price | listings/search, shops/detail featured_listings, shops/listings | Decimal strings. price is what the buyer pays now, list_price is the struck-through original: "12.90" against "42.99". |
| price + original_price | listings/detail | The detail action renames the pair. original_price came back null and on_sale false on all three listings tested, including 4389097710, whose own shop grid showed list_price "3059.00" against price "1529.50". |
| currency | Every action that carries a price | "USD" on every row measured. There is no currency or locale parameter. |
| rating vs schema_product.aggregateRating.ratingValue | listings/detail | rating sits one notch below the schema value: 4.9 vs "5.0", 4.7 vs "4.8", 5.0 vs "5.0". review_count_display matched schema reviewCount exactly (4, 2, 4987). |
| created_at | reviews/shop | Unix epoch seconds as an integer (1787400100), not an ISO string. |
| reviewer | reviews/shop | null on every row returned. language ("en") and photo_url are populated; the buyer name is not. |
| Rows per page | listings/search | About 12, not the ~48 the max_pages description implies: max_pages 1, 2 and 3 returned 12, 24 and 35 rows for the same query. |
| Rows per page | listings/category | 64 rows in a single page for category "jewelry/rings". |
| Rows per page | shops/listings | 38 rows on page 1, with total_listings 2202 giving the size of the whole catalog. |
shop_slug is null in every shops/listings row and was null on one of the three listings/detail responses, while shop_id was always present. Join on shop_id.
Real request and response JSON
Captured from the indexed primary action, listings/search, on .
{
"method": "POST",
"url": "https://api.reefapi.com/etsy/v1/listings/search",
"headers": {
"x-api-key": "$REEF_KEY",
"content-type": "application/json"
},
"body": {
"query": "silver ring"
}
}{
"ok": true,
"meta": {
"api": "etsy",
"endpoint": "listings/search",
"mode": "live",
"latency_ms": 3305.9,
"record_count": 12,
"bytes": 0,
"cache_hit": false
},
"data": {
"query": "silver ring",
"items": [
{
"listing_id": "[redacted-phone]",
"title": "Solid 925 Sterling Silver Stacking Rings - Pick your favorite! Minimalist Midi Custom",
"url": "https://www.etsy.com/listing/[redacted-phone]/solid-925-sterling-silver-stacking-rings",
"image_url": "https://i.etsystatic.com/5780988/r/il/c03e23/[redacted-phone]/il_fullxfull.[redacted-phone]_3vwg.jpg",
"shop_slug": "JewelryBySally",
"shop_id": "5780988",
"price": "12.00",
"currency": "USD",
"list_price": null,
"is_ad": null
},
{
"listing_id": "[redacted-phone]",
"title": "2118-Handmade Sterling Silver Pattern Ring, Daily Wearing Band(2.8mm)",
"url": "https://www.etsy.com/listing/[redacted-phone]/2118-handmade-sterling-silver-pattern",
"image_url": "https://i.etsystatic.com/11956698/r/il/434a1a/[redacted-phone]/il_fullxfull.[redacted-phone]_f4a7.jpg",
"shop_slug": "MasteRetsaM",
"shop_id": "11956698",
"price": "23.10",
"currency": "USD",
"list_price": "42.00",
"is_ad": null
},
{
"listing_id": "[redacted-phone]",
"title": "Bundle of Vintage Silver Stainless Steel Spoon Rings | 2 rings for 25",
"url": "https://www.etsy.com/listing/[redacted-phone]/bundle-of-vintage-silver-stainless-steel",
"image_url": "https://i.etsystatic.com/30341384/r/il/9afaff/[redacted-phone]/il_fullxfull.[redacted-phone]_bekf.jpg",
"shop_slug": "SilverWearSilas",
"shop_id": "30341384",
"price": "25.00",
"currency": "USD",
"list_price": null,
"is_ad": null
}
],
"count": 12,
"pages_fetched": 1
}
}What the Etsy API does
| Action | Description | Concrete use case | Key params |
|---|---|---|---|
| listings/search | Search Etsy handmade/vintage listings by keyword — returns listing cards with id, title, price, sale price, shop, image and listing URL. Supports category, price range, sort and pagination filters. | Pricing teams call listings/search to search Etsy handmade/vintage listings by keyword. | query, max_pages, sort, min_price, max_price, ... |
| listings/detail | Full Etsy listing detail by listing ID or URL — title, price, sale price, currency, rating, review count, favorites (demand signal), stock-left, variations, gallery images, description, tags, materials, category and shipping estimate. | Marketplace operators call listings/detail to get full Etsy listing detail by listing ID or URL. | listing_url, listing_id, url |
| shops/detail | Etsy shop profile by shop slug or URL — name, total lifetime sales, location, star rating, review count, admirers (followers), active-listings count, year opened and the shop's featured listings (deduped, with title/price/image). | Catalog enrichment teams call shops/detail to get etsy shop profile by shop slug or URL. | shop_url, shop_slug, url |
| shops/listings | Browse a shop's full active catalog, paginated — every product the shop sells (not just the featured handful), with id, title, price, sale price, image and listing URL. Page through with max_pages to pull the whole inventory. | Retail analysts call shops/listings to get browse a shop's full active catalog, paginated. | shop_url, shop_slug, url, max_pages, sort |
| listings/category | Browse Etsy by category (the /c/<category> grid) — discover trending/popular listings in a category without a keyword. Supports sub-categories, price range, sort and pagination. | Pricing teams call listings/category to get browse Etsy by category (the /c/<category> grid). | category, max_pages, sort, min_price, max_price, ... |
| reviews/shop | Customer reviews for an Etsy shop — star rating, review text, reviewer, date, the shop's reply, buyer photos and the listing each review is about. Pass shop_slug for the full paginated review stream (about 14 reviews per page). Passing only the numeric shop_id uses Etsy's compact shop-summary feed, which returns at most 6 reviews no matter what limit you ask for. | Marketplace operators call reviews/shop to get customer reviews for an Etsy shop. | shop_slug, shop_url, url, shop_id, limit, ... |
| reviews/listing | Customer reviews for a single Etsy listing — star rating, review text, reviewer, date, the shop's reply and buyer photos, plus the item's overall rating and total review count. Page 1 is the review panel Etsy shows on the item page (up to 4 reviews); raise max_pages to pull further reviews of this item out of the shop's review stream. | Catalog enrichment teams call reviews/listing to get customer reviews for a single Etsy listing. | listing_url, listing_id, url, max_pages |
| search/suggest | Etsy search autocomplete — keyword suggestions for a partial query (no account required). | Retail analysts call search/suggest to get etsy search autocomplete. | query, limit |
Call listings/search from your stack
curl -X POST https://api.reefapi.com/etsy/v1/listings/search \
-H "x-api-key: $REEF_KEY" \
-H "content-type: application/json" \
-d '{"query":"silver ring"}'import requests
r = requests.post(
"https://api.reefapi.com/etsy/v1/listings/search",
headers={"x-api-key": REEF_KEY},
json={
"query": "silver ring"
},
)
print(r.json()["data"])const res = await fetch("https://api.reefapi.com/etsy/v1/listings/search", {
method: "POST",
headers: {
"x-api-key": process.env.REEF_KEY,
"content-type": "application/json",
},
body: JSON.stringify({
"query": "silver ring"
}),
});
const { ok, data, meta, error } = await res.json();Ask your MCP-connected assistant: call reefapi.etsy.listings/search with {"query":"silver ring"}.Who uses this API and why
- Marketplace researchers call listings/search to track prices and trends in a handmade category.
- Seller-intelligence tools use shops/listings to audit a competitor shop's full catalog.
- Product tools pull reviews/listing to gauge demand and sentiment for a listing.
Questions developers ask before integrating
Where do I get the numeric shop_id that reviews/shop wants?
Three places, all cheap. Every listings/search row carries shop_id next to shop_slug; shops/detail returns it for a slug you already have; and it is the first path segment of any Etsy image URL, so i.etsystatic.com/7357402/r/il/... tells you the shop is 7357402 without another call. reviews/shop rejects the slug, so this step is not optional.
Which field is the sale price and which is the original?
In listings/search, shops/detail and shops/listings, price is the current price and list_price is the higher struck-through one: "12.90" against "42.99". listings/detail renames the pair to price and original_price, and original_price came back null with on_sale false on all three listings we pulled, including listing 4389097710 whose own shop page showed a 50 percent markdown. If you need the discount, take it from a search or shop row, not from detail.
Why is rating lower than the star rating in schema_product?
They round differently on the same underlying average. Listing 1509738931 returned rating 4.9 with schema_product.aggregateRating.ratingValue "5.0"; listing 1112702820 returned 4.7 against "4.8"; a listing whose true average is exactly 5.0 returned 5.0 in both. Treat rating as the truncated number and the schema value as the rounded one Etsy prints on the page. review_count_display and schema reviewCount agreed on every listing.
How many listings does one search page actually return?
About 12. Running the same query at max_pages 1, 2 and 3 returned 12, 24 and 35 rows, so budget roughly a dozen listings per page rather than the 48 the parameter text suggests. listings/category is far denser: category "jewelry/rings" returned 64 rows in a single page, and shops/listings returned 38 with total_listings 2202. If you want volume from a keyword, raise max_pages; if you want volume from a category, one page is usually enough.
What do the review timestamps look like?
reviews/shop returns created_at as Unix epoch seconds, so 1787400100 is a date in August 2026, not a date string. Each row also carries rating, text, language, photo_url (null when the buyer posted no photo) and the listing_id plus transaction_id the review belongs to, both as integers. reviewer was null on every row we pulled, so do not plan on a buyer name.
Does the API tell me how much a shop has sold?
shops/detail returns sales_count, the shop's lifetime order count, alongside followers_count, active_listings_count and rating. GlamAndGemsNY returned sales_count 2093, followers_count 7173, active_listings_count 2202 and rating 5.0. opened_year came back null on that shop, so treat the shop's age as optional. There is no per-listing sold count anywhere in the response.
What does search/suggest give me?
Plain keyword completions and nothing else. Query "silver ring" with limit 6 returned six suggestions, starting with the query itself and continuing "silver ring men", "silver rings for women", "silver ring set", "silver ring stack" and "silver ring with stone". The categories array on each suggestion was empty on all six, so use it for keyword expansion, not for category discovery.
Why did one listing come back with empty tags and a single image?
It happens on a page that renders thin, and the response still says ok true. Listing 4389097710 returned tags [], image_count 1 and shop_slug null on the first pull, while listing 1509738931 returned 30 tags, 8 images and a full shop slug. Price, listing_id, shop_id, description and the schema_product block were correct in both. If tags or images matter to you, check image_count before you trust the payload and retry, or take the image from the search row.
What is the Etsy API?
Etsy API is a ReefAPI endpoint group for handmade and vintage product listings and shops. It returns live JSON through POST requests under /etsy/v1.
Is the Etsy API free to try?
Yes. ReefAPI starts with 1,000 free credits, no card required. Etsy calls use the same shared credit balance as every other ReefAPI engine.
Do I need an Etsy login or account?
No login to Etsy is needed for the API response. You call ReefAPI with your x-api-key header, and the playground can run live examples before you create a production key.
How fresh is the Etsy data?
The page example is captured from a live listings/search call, and production requests fetch live data through ReefAPI rather than a static sample.
How many credits does the Etsy API use?
Etsy actions currently cost 1-2 credits per successful call. Failed or blocked calls are free, and all APIs draw from one credit pool.
Can I call Etsy from an AI assistant or MCP client?
Yes. Connect ReefAPI once through MCP and your assistant can call etsy actions with the same key, credit pool and JSON envelope used by normal REST requests.