Scrappa's GET /api/etsy/search returns Etsy listings for a search query as structured JSON.
What you get
Each listing has listing_id, title, url, image, image_large, price, original_price, discount_percent, currency, rating, review_count, shop_id, shop_name, shop_url and the flags bestseller, popular_now, etsys_pick, star_seller, free_shipping and from_price. pagination.total_results is the number of matches Etsy reports.
Ads are removed
Etsy mixes paid ads into the results. They are removed from data, and pagination.sponsored_removed tells you how many. position counts the remaining listings of the page. A page can hold fewer listings than Etsy shows.
Filters are honoured
min_price, max_price, free_shipping and on_sale are checked on every listing. A listing that breaks a filter is removed, and pagination.filtered_out counts them. Etsy reports the sort and ship_to it applied. If one is missing, the request returns 422 (upstream_rejected) instead of unfiltered listings.
Currency
currency is the currency Etsy shows on the page. Prices are not converted.
Rounded review counts
Search cards show review counts like 12.5k. Then review_count is rounded and review_count_is_rounded is true. The product endpoint returns the exact count.
Pagination
page is 1-20, because Etsy serves 20 result pages at most. The label always equals the page Etsy served. If Etsy served another page, the request fails with 503 and costs nothing. has_more tells you whether a next page exists.
Credits
A successful request costs 10 credits. Etsy needs a full browser for every page, so it costs more than a plain request. Errors, blocks and 404 answers cost nothing. The response shows the charge in meta.credits_charged.
Run this endpoint
Endpoint
Parameters
Start with the required fields, then add optional filters only when your use case needs them.
Runnable path
1 required parameter needed before sending a request.
7 optional filters available.
string
Required
Search words, for example leather wallet.
leather wallet
integer
Optional
Result page, 1-20.
1
string
Optional
relevance (default), newest, price_low_to_high, price_high_to_low or top_customer_reviews.
relevance
number
Optional
Lowest listing price in the currency of the page. Cheaper listings are removed.
10
number
Optional
Highest listing price in the currency of the page. Dearer listings are removed.
50
string
Optional
Two-letter country code such as DE or GB. Only listings that ship to that country.
example
boolean
Optional
true for listings with free shipping only.
true
boolean
Optional
true for listings on sale only.
true
Request Examples
<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://scrappa.co/api/etsy/search?query=leather+wallet&max_price=50&free_shipping=true",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"x-api-key: YOUR_API_KEY_HERE"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}
<?php
use Illuminate\Support\Facades\Http;
$response = Http::timeout(30)
->withHeaders(['x-api-key' => 'YOUR_API_KEY_HERE'])
->get('https://scrappa.co/api/etsy/search?query=leather+wallet&max_price=50&free_shipping=true');
if ($response->successful()) {
echo $response->body();
} else {
echo "Error: " . $response->status();
}
const options = {
method: 'GET',
headers: {
'x-api-key': 'YOUR_API_KEY_HERE'
}
};
fetch('https://scrappa.co/api/etsy/search?query=leather+wallet&max_price=50&free_shipping=true', options)
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.text();
})
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
const axios = require('axios');
const options = {
method: 'GET',
url: 'https://scrappa.co/api/etsy/search?query=leather+wallet&max_price=50&free_shipping=true',
headers: {
x-api-key: 'YOUR_API_KEY_HERE',
}
};
try {
const response = await axios(options);
console.log(response.data);
} catch (error) {
console.error('Error:', error.message);
}
require 'net/http'
require 'uri'
uri = URI.parse("https://scrappa.co/api/etsy/search?query=leather+wallet&max_price=50&free_shipping=true")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = uri.scheme == 'https'
request = Net::HTTP::Get.new(uri.request_uri)
request['x-api-key'] = 'YOUR_API_KEY_HERE'
begin
response = http.request(request)
puts response.body
rescue => e
puts "Error: #{e.message}"
end
import http.client
import json
conn = http.client.HTTPSConnection("scrappa.co")
headers = {
'x-api-key': 'YOUR_API_KEY_HERE',
}
try:
conn.request("GET", "/api/etsy/search?query=leather+wallet&max_price=50&free_shipping=true", headers=headers)
res = conn.getresponse()
data = res.read()
print(data.decode("utf-8"))
except Exception as e:
print(f"Error: {e}")
finally:
conn.close()
import requests
headers = {
'x-api-key': 'YOUR_API_KEY_HERE',
}
try:
response = requests.get('https://scrappa.co/api/etsy/search?query=leather+wallet&max_price=50&free_shipping=true', headers=headers)
response.raise_for_status()
print(response.text)
except requests.exceptions.RequestException as e:
print(f"Error: {e}")
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import java.io.IOException;
public class ApiExample {
public static void main(String[] args) {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("https://scrappa.co/api/etsy/search?query=leather+wallet&max_price=50&free_shipping=true")
.addHeader("x-api-key", "YOUR_API_KEY_HERE")
.build();
try (Response response = client.newCall(request).execute()) {
if (response.isSuccessful()) {
System.out.println(response.body().string());
} else {
System.out.println("Error: " + response.code());
}
} catch (IOException e) {
System.out.println("Error: " + e.getMessage());
}
}
}
package main
import (
"fmt"
"net/http"
"io/ioutil"
)
func main() {
client := &http.Client{}
req, err := http.NewRequest("GET", "https://scrappa.co/api/etsy/search?query=leather+wallet&max_price=50&free_shipping=true", nil)
if err != nil {
fmt.Println("Error creating request:", err)
return
}
req.Header.Set("x-api-key", "YOUR_API_KEY_HERE")
resp, err := client.Do(req)
if err != nil {
fmt.Println("Error making request:", err)
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println("Error reading response:", err)
return
}
fmt.Println(string(body))
}
#!/bin/bash
curl -X GET \
-H "x-api-key: YOUR_API_KEY_HERE" \
"https://scrappa.co/api/etsy/search?query=leather+wallet&max_price=50&free_shipping=true"
using System;
using System.Net.Http;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("x-api-key", "YOUR_API_KEY_HERE");
try
{
var response = await client.SendAsync(new HttpRequestMessage(HttpMethod.Get, "https://scrappa.co/api/etsy/search?query=leather+wallet&max_price=50&free_shipping=true"));
var content = await response.Content.ReadAsStringAsync();
Console.WriteLine(content);
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
}
}
import axios from 'axios';
async function run(): Promise<void> {
try {
const response = await axios({
method: 'GET',
url: 'https://scrappa.co/api/etsy/search?query=leather+wallet&max_price=50&free_shipping=true',
headers: {
'x-api-key': 'YOUR_API_KEY_HERE',
},
});
console.log(response.data);
} catch (error) {
console.error('Error:', error);
}
}
void run();
use reqwest::Client;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = Client::new();
let response = client
.get("https://scrappa.co/api/etsy/search?query=leather+wallet&max_price=50&free_shipping=true")
.header("x-api-key", "YOUR_API_KEY_HERE")
.send()
.await?;
println!("{}", response.text().await?);
Ok(())
}
Response Schema
Example response fields are illustrative; inspect the JSON before integrating.
Example response fields
Scan these fields before integrating.
success
data
pagination
applied_filters
currency
meta
Common data fields
position
listing_id
title
url
{
"success": true,
"data": [
{
"position": 1,
"listing_id": "1241528786",
"title": "Personalized Leather RFID Wallet, Engraved Genuine Leather Custom Wallet",
"url": "https://www.etsy.com/listing/1241528786/personalized-leather-rfid-wallet",
"image": "https://i.etsystatic.com/35016605/r/il/a5f633/4007318605/il_255x319.4007318605_mj36.jpg",
"image_large": "https://i.etsystatic.com/35016605/r/il/a5f633/4007318605/il_794xN.4007318605_mj36.jpg",
"price": 28,
"original_price": 70,
"discount_percent": 60,
"currency": "USD",
"from_price": false,
"rating": 4.9,
"review_count": 12500,
"review_count_is_rounded": true,
"shop_id": "35016605",
"shop_name": "PremiumEngrave",
"shop_url": "https://www.etsy.com/shop/PremiumEngrave",
"bestseller": true,
"popular_now": true,
"etsys_pick": false,
"star_seller": true,
"free_shipping": true
}
],
"pagination": {
"page": 1,
"has_more": true,
"total_results": 89578,
"filtered_out": 0,
"sponsored_removed": 12
},
"applied_filters": {
"query": "leather wallet",
"max_price": 50,
"free_shipping": true
},
"currency": "USD",
"meta": {
"endpoint_family": "etsy-search",
"billable": true,
"attempts": 1,
"credits_charged": 10
}
}
Errors
Handle these documented responses before retrying or showing customer-facing failures.
Filter Not Applied
Etsy did not apply a requested filter such as `ship_to` or `sort` (`upstream_rejected`). The request is rejected instead of returning unfiltered listings. Non-billable.
{
"meta": {
"attempts": 1,
"billable": false,
"retryable": false,
"endpoint_family": "etsy-search"
},
"error": {
"code": "upstream_rejected",
"message": "Etsy did not apply a requested filter. The request is rejected instead of returning unfiltered results."
},
"success": false
}Validation Error
A query parameter is missing, unknown or unusable. Unknown parameters are rejected, never ignored. Non-billable.
{
"errors": {
"sort": [
"The selected sort is invalid."
]
},
"message": "The selected sort is invalid."
}Upstream Unavailable
Etsy could not be reached in time or answered with a block or an unexpected page (`upstream_unavailable`, `upstream_blocked`, `parser_drift`). Retryable and never billed. Clients are never rate limited.
{
"meta": {
"attempts": 3,
"billable": false,
"retryable": true,
"endpoint_family": "etsy-search"
},
"error": {
"code": "upstream_unavailable",
"message": "Etsy is temporarily unavailable. Please retry."
},
"success": false
}Generate Code with AI
Copy a ready-made prompt with all the endpoint details, parameters, and example responses. Paste it into ChatGPT, Claude, or any AI assistant to instantly generate working code.
Try It Live
Test this endpoint in our interactive playground with real data.