If you build anything that needs eBay sold prices, 2026 broke your stack twice. Here is the current state of every route to completed listings data, and working code for the one that still works.
Full disclosure up front: I run CompsAPI, which is one of the options below. The rest of this post is accurate whether or not you use it.
What actually happened
A short timeline, because half the tutorials online are now wrong:
-
2020: eBay deprecates
findCompletedItems, the Finding API call everyone used for sold listings. - February 2025: the whole Finding API is decommissioned. Code that called it gets security errors, not data.
- The official replacement, the Marketplace Insights API, is a limited release. You apply with a business case, and approval is restricted to established eBay partners. Most applications go nowhere, and the developer forums are full of people stuck at the same wall.
-
Mid 2026: eBay puts the public sold listings filter behind a login. The
LH_Sold=1URL that every scraper and browser extension relied on now redirects to a sign in page for most traffic.
So the two free routes, the official API and scraping the public page, are both gone. What is left:
Option 1: Terapeak, if a human is doing the looking
eBay's own research tool (now called Product Research, inside Seller Hub) shows up to three years of sold data. If you price items by hand a few times a day, it is genuinely good and costs nothing extra with a seller account.
It stops at the browser: no API, no export of research results, and sellers report daily caps on queries. The moment you need sold prices in a spreadsheet, a pricing model or an app, there is no door.
Option 2: scrapers against the logged in page
Some scrapers now run eBay accounts to get through the login wall. Two structural problems. First, the public sold page shows at most 90 days of history. Second, when a sale closes as an accepted Best Offer, the page shows the asking price, not what the buyer actually paid. In offer heavy categories (watches, cards, anything expensive) those numbers run high by 20 to 40 percent per affected row. A scraper inherits both limits because the page itself is the ceiling.
Option 3: a sold listings API
This is what I build. One GET request, JSON back, up to 1,095 days of history per search, and on accepted Best Offer sales the price field is the real closing amount, flagged per row.
Get a free key at app.compsapi.com (100 searches a month, no card), then:
curl -G "https://api.compsapi.com/v1/sold" \
-H "Authorization: Bearer sk_live_YOUR_KEY" \
--data-urlencode "q=charizard psa 10" \
-d "days=365" -d "best_offer=1"
Response, trimmed:
{
"ok": true,
"count": 239,
"items": [
{
"title": "Charizard PSA 10 Base Set Holo",
"price": 1450.0,
"best_offer": true,
"last_sold": "2026-10-04 18:32",
"format": "FIXED_PRICE",
"shipping_cost": 12.5,
"url": "https://www.ebay.com/itm/..."
}
],
"next_page": true
}
That best_offer: true row is the point. On the eBay page this sale displays its asking price. Here it is the amount it closed at.
The Python version
There is a zero dependency client (github.com/compsapi/compsapi-python):
pip install git+https://github.com/compsapi/compsapi-python.git
from compsapi import CompsAPI
api = CompsAPI("sk_live_YOUR_KEY")
r = api.sold("iphone 14 pro 256gb", days=365, condition="used")
prices = sorted(s["price"] for s in r["items"])
print(f"{r['count']} sales, median ${prices[len(prices)//2]}")
Three years of price history in 15 lines
The thing you could never do with the public page, since it stops at 90 days:
from collections import defaultdict
from statistics import median
from compsapi import CompsAPI
api = CompsAPI("sk_live_YOUR_KEY")
monthly = defaultdict(list)
for sale in api.iter_sold("iphone 14 pro 256gb", days=1095, max_results=3000):
monthly[sale["last_sold"][:7]].append(sale["price"])
for month in sorted(monthly):
print(month, round(median(monthly[month]), 2), f"({len(monthly[month])} sales)")
Output is a monthly depreciation curve built from real completed sales. Each page of 240 sales costs one request, so a three year pull like this uses about a dozen requests.
If you want a pre-aggregated answer instead of rows, /v1/comps returns a cleaned median, the spread and the newest matches in one call, with lots, bundles and wrong model junk filtered before the math runs.
Honest limitations
- eBay US is the primary marketplace, with matching sales from UK, DE, FR, IT, ES, CA and AU merged in or pinned with
site=. - No seller information on rows.
- The free tier is 100 requests a month. Paid plans start at $9.
If you came here from dead code
Migrating from findCompletedItems: your keywords becomes q, EndTimeFrom/To becomes days, entriesPerPage becomes size (max 240 instead of 100), and the response is flat JSON instead of a SOAP envelope. There is a field by field mapping at compsapi.com/replace/findcompleteditems.
Questions welcome in the comments. If you hit something the API does not cover, tell me and there is a decent chance it ships.
Top comments (0)