Does eBay have a sold-listings API for JavaScript?
eBay’s public Browse API returns active listings, not completed sales. Its Marketplace Insights API covers sold history but is restricted. A practical Node.js integration therefore needs an approved data provider or an independently maintained sold-listings API. CompSniper returns up to 240 completed sales through one authenticated GET request.
This distinction is confirmed in the eBay Developer Support answer and the official Marketplace Insights overview.
Make the first sold-listings request with native fetch
Keep the API key on your server. Node.js 20 and newer include fetch, URLSearchParams, and AbortSignal.timeout, so this example needs no HTTP package.
const params = new URLSearchParams({
keyword: "sony wh-1000xm5",
count: "10",
ebaySite: "ebay.com",
itemCondition: "used",
});
const response = await fetch(
`https://api.compsniper.com/v1/scrape?${params}`,
{
headers: {
Authorization: `Bearer ${process.env.COMPSNIPER_API_KEY}`,
},
signal: AbortSignal.timeout(75_000),
},
);
if (!response.ok) {
throw new Error(`CompSniper returned HTTP ${response.status}`);
}
const data = await response.json();
console.log("Listings:", data.totalItems);
console.log("Median:", data.summary.median, data.summary.currency);
console.log("Range:", data.summary.p25, "to", data.summary.p75);
for (const item of data.items.slice(0, 5)) {
console.log(item.title, item.soldPrice, item.endedAt);
}Production verification
Tested September 3, 2026 against the live API with Node.js 22. A 10-row Sony WH-1000XM5 request returned HTTP 200 and a $124 median. Treat every response as a current sample, not a guaranteed future selling price.

Type the listing rows and price summary
The public response keeps monetary listing values as decimal strings while summary statistics are numbers. Nullable fields reflect information that eBay did not display on that result card.
type SoldListing = {
itemId: string | null;
title: string | null;
soldPrice: string | null;
soldCurrency: string | null;
endedAt: string | null;
condition: string | null;
shippingPrice: string | null;
totalPrice: string | null;
bestOfferAccepted: boolean | null;
url: string | null;
};
type SoldSearchResponse = {
keyword: string;
totalItems: number;
totalResults: string | null;
hasNextPage: boolean;
rawMedian: number | null;
rawSampleCount: number | null;
summary: {
count: number;
currency: string | null;
median: number | null;
mean: number | null;
p25: number | null;
p75: number | null;
avgShipping: number | null;
};
items: SoldListing[];
};
const data = (await response.json()) as SoldSearchResponse;Choose the marketplace and narrow the sample
Set ebaySite explicitly when your users sell outside the US. Keep marketplace, condition, and price constraints in request parameters, then use minus-sign exclusions for recurring keyword noise.
const params = new URLSearchParams({
keyword: "iphone 15 pro -case -charger",
ebaySite: "ebay.co.uk",
count: "100",
itemCondition: "used",
minPrice: "250",
maxPrice: "1200",
sortOrder: "endedRecently",
});Eight marketplaces
The response shape stays consistent across US, UK, Germany, France, Italy, Spain, Canada, and Australia.
Raw evidence remains available
Set relevance=false when you intentionally need every valid upstream match.
Retry temporary failures, never exhausted quota
HTTP 429 has two meanings. Retry rate_limited after Retry-After, but stop immediately for quota_exceeded. Temporary 500, 502, and 503 responses can use bounded exponential backoff with jitter.
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
async function searchWithSafeRetries(params) {
for (let attempt = 0; attempt < 5; attempt += 1) {
const response = await fetch(
`https://api.compsniper.com/v1/scrape?${params}`,
{
headers: { Authorization: `Bearer ${process.env.COMPSNIPER_API_KEY}` },
signal: AbortSignal.timeout(75_000),
},
);
const body = await response.json();
if (response.ok) return body;
if (response.status === 429 && body.code === "quota_exceeded") {
throw new Error(`Quota exhausted. Upgrade: ${body.upgrade_url}`);
}
const temporary =
(response.status === 429 && body.code === "rate_limited") ||
[500, 502, 503].includes(response.status);
if (!temporary || attempt === 4) {
throw new Error(body.error || `HTTP ${response.status}`);
}
const retryAfter = Number(response.headers.get("Retry-After"));
const delay = Number.isFinite(retryAfter) ? retryAfter * 1000 : Math.min(1000 * 2 ** attempt, 30_000);
await sleep(delay + Math.random() * 400);
}
}Read X-Usage-Remaining on successful responses and surface the provided upgrade URL before an automated workflow reaches zero.
Paginate with a deliberate request ceiling
Each successful page consumes one request. Increment page while hasNextPage is true, but always set a maximum so a scheduled job cannot run forever.
const allItems = [];
for (let page = 1; page <= 3; page += 1) {
const params = new URLSearchParams({
keyword: "sony wh-1000xm5",
count: "240",
page: String(page),
});
const data = await searchWithSafeRetries(params);
allItems.push(...data.items);
if (!data.hasNextPage) break;
}
console.log("Collected rows:", allItems.length);Evidence behind the cleanup
What changed across 100 real searches
In CompSniper’s predeclared 100-product study, 19,220 raw priced rows became 11,942 cleaned rows. The median moved by at least 10% for 34 products. Read the method, complete product list, limitations, and aggregate CSV before generalizing those results.
Frequently asked questions
JavaScript and eBay sold data
Does eBay's Browse API return sold listings?
No. eBay Developer Support states that Browse API returns active items, not sold listings. Marketplace Insights supports sold history, but access is restricted. CompSniper provides an independent sold-listings API for developers who need completed-sale data.
Can I call the API directly from browser JavaScript?
Do not expose an API key in browser code. Call CompSniper from a Node.js server, serverless function, background worker, or another trusted backend environment.
How many sold listings can one JavaScript request return?
One request can return up to 240 sold listings. The final number can be smaller when eBay has fewer matches or relevance cleaning removes accessories, parts, and wrong models.
Can Node.js search eBay UK, Germany, Canada, and Australia?
Yes. Set ebaySite to one of eight supported marketplaces: ebay.com, ebay.co.uk, ebay.de, ebay.fr, ebay.it, ebay.es, ebay.ca, or ebay.com.au.
Clone the public examples
Node.js, Python, cURL, pagination, retry, and Card Batch workflows.
Prefer Python?
Use the equivalent production-minded Python tutorial.