eBay findCompletedItems alternative
eBay retired findCompletedItems in February 2025. The findCompletedItems alternative for sold prices is GET /ebay/v1/sold. One short script sends the old request there and answers in the old field names.
What findCompletedItems was
findCompletedItems was a call of the eBay Finding API. It searched completed listings by keyword and category, with a filter for the ones that sold. eBay restricted the call in October 2020 and decommissioned the Finding API in February 2025. A program that calls it today gets an error.
eBay's replacements do not cover it. The Browse API searches live listings only, see Does eBay API return sold listings? The Marketplace Insights API returns sales, but eBay grants it to few applicants. The Trawl vs the eBay Marketplace Insights API page has that comparison.
For sold prices as data, create a free account and do the steps below. The eBay Sold Listings API takes the same keywords, category and condition, and answers with completed sales.
Do these steps
Click the button to copy this prompt, then paste it into your coding agent. The link tells it which page you came from.
Read https://trawl.dev/llms.txt?from=/guides/findcompleteditems-alternative and help me integrate this API into my project.- Set your API key. Get a key from the free tier. terminal
export TRAWL_API_KEY=your_key - Run the script in your language. Python needs the requests package. Node 18 and Go 1.22 need nothing more. The search costs 1 credit.
# findcompleteditems_to_sold.py (pip install requests)
import os
import requests
# The request your code sent to findCompletedItems.
legacy = {
"keywords": "iphone 15 pro 256gb",
"categoryId": "9355",
"itemFilter": [
{"name": "SoldItemsOnly", "value": "true"},
{"name": "Condition", "value": "3000"}, # 3000 was Used
],
"paginationInput": {"entriesPerPage": 100, "pageNumber": 1},
}
# The same request on GET /sold. Every result sold, so SoldItemsOnly goes.
CONDITION = {"1000": "new", "1500": "new", "1750": "new", "7000": "parts"}
for used in ("2000", "2010", "2020", "2030", "2500", "2750", "3000", "4000", "5000", "6000"):
CONDITION[used] = "used"
r = requests.get("https://api.trawl.dev/ebay/v1/sold", timeout=30, headers={"x-api-key": os.environ["TRAWL_API_KEY"]}, params={
"query": legacy["keywords"],
"category": legacy["categoryId"],
"condition": ",".join(CONDITION[f["value"]] for f in legacy["itemFilter"] if f["name"] == "Condition"),
"max_pages": legacy["paginationInput"]["pageNumber"], # a page is 100 results
})
r.raise_for_status()
sold = r.json()
# The answer in findCompletedItems' own field names.
items = [
{
"itemId": s["item_id"], "title": s["title"],
"sellingStatus": {"currentPrice": {"value": s["sale_price"], "currencyId": sold["currency"]}},
"listingInfo": {"endTime": s["date_sold"], "listingType": s["buying_format"]},
"condition": {"conditionDisplayName": s["condition_raw"]},
}
for s in sold["results"]
]
print(f"{sold['count']} sold listings, {sold['credits_charged']} credit")
for it in items[:5]:
price = it["sellingStatus"]["currentPrice"]
print(f"{price['currencyId']} {price['value']:>7.2f} {it['listingInfo']['endTime'][:10]} {it['title'][:58]}")// findcompleteditems_to_sold.mjs (Node 18 or newer, no package)
// The request your code sent to findCompletedItems.
const legacy = {
keywords: "iphone 15 pro 256gb",
categoryId: "9355",
itemFilter: [
{ name: "SoldItemsOnly", value: "true" },
{ name: "Condition", value: "3000" }, // 3000 was Used
],
paginationInput: { entriesPerPage: 100, pageNumber: 1 },
};
// The same request on GET /sold. Every result sold, so SoldItemsOnly goes.
const CONDITION = { 1000: "new", 1500: "new", 1750: "new", 7000: "parts" };
for (const used of [2000, 2010, 2020, 2030, 2500, 2750, 3000, 4000, 5000, 6000]) CONDITION[used] = "used";
const params = new URLSearchParams({
query: legacy.keywords,
category: legacy.categoryId,
condition: legacy.itemFilter.filter((f) => f.name === "Condition").map((f) => CONDITION[f.value]).join(","),
max_pages: legacy.paginationInput.pageNumber, // a page is 100 results
});
const r = await fetch(`https://api.trawl.dev/ebay/v1/sold?${params}`, {
headers: { "x-api-key": process.env.TRAWL_API_KEY },
});
if (!r.ok) throw new Error(`${r.status} ${await r.text()}`);
const sold = await r.json();
// The answer in findCompletedItems' own field names.
const items = sold.results.map((s) => ({
itemId: s.item_id, title: s.title,
sellingStatus: { currentPrice: { value: s.sale_price, currencyId: sold.currency } },
listingInfo: { endTime: s.date_sold, listingType: s.buying_format },
condition: { conditionDisplayName: s.condition_raw },
}));
console.log(`${sold.count} sold listings, ${sold.credits_charged} credit`);
for (const it of items.slice(0, 5)) {
const price = it.sellingStatus.currentPrice;
console.log(`${price.currencyId} ${price.value.toFixed(2).padStart(7)} ${it.listingInfo.endTime.slice(0, 10)} ${it.title.slice(0, 58)}`);
}// findcompleteditems_to_sold.go (Go 1.22 or newer, standard library only)
package main
import (
"encoding/json"
"fmt"
"net/http"
"net/url"
"os"
)
// GET /sold's answer, read into findCompletedItems' own field names.
type sold struct {
Count int `json:"count"`
Credits int `json:"credits_charged"`
Currency string `json:"currency"`
Results []struct {
ItemID string `json:"item_id"` // was itemId
Title string `json:"title"`
CurrentPrice float64 `json:"sale_price"` // was sellingStatus.currentPrice
EndTime string `json:"date_sold"` // was listingInfo.endTime
ListingType string `json:"buying_format"` // was listingInfo.listingType
ConditionDisplayName string `json:"condition_raw"` // was condition.conditionDisplayName
} `json:"results"`
}
func main() {
// The request your code sent to findCompletedItems: keywords, categoryId,
// itemFilter Condition 3000 (Used) and SoldItemsOnly, pageNumber 1.
condition := map[string]string{"1000": "new", "1500": "new", "1750": "new", "7000": "parts"}
for _, used := range []string{"2000", "2010", "2020", "2030", "2500", "2750", "3000", "4000", "5000", "6000"} {
condition[used] = "used"
}
// The same request on GET /sold. Every result sold, so SoldItemsOnly goes.
q := url.Values{"query": {"iphone 15 pro 256gb"}, "category": {"9355"}, "condition": {condition["3000"]}, "max_pages": {"1"}}
req, _ := http.NewRequest("GET", "https://api.trawl.dev/ebay/v1/sold?"+q.Encode(), nil)
req.Header.Set("x-api-key", os.Getenv("TRAWL_API_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil || res.StatusCode != 200 {
panic(fmt.Sprint(err, res))
}
var s sold
json.NewDecoder(res.Body).Decode(&s)
fmt.Printf("%d sold listings, %d credit\n", s.Count, s.Credits)
for _, it := range s.Results[:5] {
fmt.Printf("%s %7.2f %s %.58s\n", s.Currency, it.CurrentPrice, it.EndTime[:10], it.Title)
}
}100 sold listings, 1 credit
USD 420.00 2026-10-07 Apple iPhone 15 Pro Max - 256GB - Blue Titanium - Unlocked
USD 549.99 2026-10-07 Apple iPhone 15 Pro 256GB Blue Unlocked Worldwide Excellen
USD 570.00 2026-10-07 Apple iPhone 15 Pro Max 256GB Unlocked Used Good Condition
USD 550.00 2026-10-07 Apple iPhone 15 Pro 256GB Unlocked - White
USD 450.00 2026-10-07 Apple iPhone 15 Pro 256GB unlocked gray100 sold listings, 1 credit
USD 420.00 2026-10-07 Apple iPhone 15 Pro Max - 256GB - Blue Titanium - Unlocked
USD 549.99 2026-10-07 Apple iPhone 15 Pro 256GB Blue Unlocked Worldwide Excellen
USD 570.00 2026-10-07 Apple iPhone 15 Pro Max 256GB Unlocked Used Good Condition
USD 550.00 2026-10-07 Apple iPhone 15 Pro 256GB Unlocked - White
USD 450.00 2026-10-07 Apple iPhone 15 Pro 256GB unlocked gray100 sold listings, 1 credit
USD 420.00 2026-10-07 Apple iPhone 15 Pro Max - 256GB - Blue Titanium - Unlocked
USD 549.99 2026-10-07 Apple iPhone 15 Pro 256GB Blue Unlocked Worldwide Excellen
USD 570.00 2026-10-07 Apple iPhone 15 Pro Max 256GB Unlocked Used Good Condition
USD 550.00 2026-10-07 Apple iPhone 15 Pro 256GB Unlocked - White
USD 450.00 2026-10-07 Apple iPhone 15 Pro 256GB unlocked grayHow the request maps
keywords becomes query. Each word must be in the listing title, as before. categoryId becomes category, the same leaf id. Find one with the eBay Category IDs API. The condition ids become the words new, used and parts. SoldItemsOnly has no counterpart. Every result of the eBay Sold Listings API sold.
pageNumber becomes max_pages. A page holds 100 results. max_pages returns pages 1 to N in one response, 20 at most. Drop the old paging loop. EndTimeFrom and EndTimeTo become date_from and date_to, as calendar dates. GLOBAL-ID becomes site.
How the response maps
The Python and Node scripts build one object per result. The old names are outside and the new names are inside. The Go script reads the answer into a struct whose fields carry the old names. currentPrice is sale_price, and the currency is stated once at the top of the response. endTime is date_sold. listingType is buying_format. conditionDisplayName is condition_raw. There is no totalEntries. A count of 100 means older sales may exist.
Open one listing
Each result has an item_id. Pass it to the eBay Sold Item Details API to open that listing. You get the item specifics, the images, the seller and the recorded sales. findCompletedItems never returned the specifics or the sales.