3-Year eBay Sold History API
Search up to 3 years of eBay sold history by keyword with GET /v1/research/sold, on every CompSniper plan including free.
GET /v1/research/sold returns eBay sold history going back up to 3 years, across all eight eBay
sites, with the same API key you already use for /v1/scrape. It is available on every plan,
including free.
Use it when 90 days is not enough: slow-moving items, seasonal trends, price changes after a release,
or a sale you remember from last year. For the most recent individual sales with full listing fields
and AI cleanup, keep using /v1/scrape.
How far back it goes
Up to 3 years before today. Any startDate from 3 years ago up to today works.
Make a request
curl --fail-with-body \
-H "Authorization: Bearer cs_live_REPLACEWITHYOURKEY" \
"https://api.compsniper.com/v1/research/sold?keyword=nintendo+switch+oled&startDate=2023-10-11&endDate=2026-10-10&sort=lastSoldDescending&limit=50"import requests
response = requests.get(
"https://api.compsniper.com/v1/research/sold",
headers={"Authorization": "Bearer cs_live_REPLACEWITHYOURKEY"},
params={
"keyword": "nintendo switch oled",
"startDate": "2023-10-11",
"endDate": "2026-10-10",
"sort": "lastSoldDescending",
"limit": 50,
},
timeout=60,
)
response.raise_for_status()
data = response.json()
for row in data["results"]:
print(row["lastSoldDate"], row["soldUnits"], row["averageSoldPrice"]["amount"], row["title"])Parameters
| Parameter | Values | Default |
|---|---|---|
keyword | 1 to 200 characters | Required |
startDate | YYYY-MM-DD, no earlier than 3 years before today | Required |
endDate | YYYY-MM-DD, on or after startDate, no later than today (UTC) | Required |
ebaySite | ebay.com, ebay.co.uk, ebay.com.au, ebay.ca, ebay.de, ebay.fr, ebay.it, ebay.es | ebay.com |
sort | lastSoldDescending (newest sale first) or quantityAscending (eBay's units-sold order) | quantityAscending |
limit | 10, 20, or 50 rows per page | 50 |
page | 1 to 200 | 1 |
singleUnitOnly | true returns only listings that sold exactly one unit | false |
relevance | true removes rows that are not the searched product (see Cleanup) | false |
Unknown or repeated parameters are rejected with 400 invalid_params, so a typo never silently
changes your results.
Response
{
"source": "ebay_product_research",
"ebaySite": "ebay.com",
"displayCurrency": "USD",
"startDate": "2023-10-11",
"endDate": "2026-10-10",
"page": 1,
"limit": 50,
"sourceResultCount": 50,
"resultCount": 50,
"hasNextPage": true,
"nextPage": 2,
"relevanceApplied": false,
"totals": {
"totalSold": 491,
"totalSellers": 147,
"averageSoldPrice": { "amount": "382.74", "currency": "USD", "display": "$ 382.74" },
"minSoldPrice": { "amount": "24.99", "currency": "USD", "display": "$ 24.99" },
"maxSoldPrice": { "amount": "10500.00", "currency": "USD", "display": "$ 10,500.00" },
"averageShipping": { "amount": "18.53", "currency": "USD", "display": "$ 18.53" },
"freeShippingPercent": "43",
"sellThroughPercent": "28.86",
"totalSales": { "amount": "187925.34", "currency": "USD", "display": "$ 187,925.34" }
},
"warnings": [
"Listing-level records, not a transaction feed. Multi-unit rows contain averages.",
"USD is the research display currency, not necessarily the listing's original currency.",
"Older listing titles, links, images and conditions may be unavailable."
],
"results": [
{
"itemId": "267809186741",
"title": "Nintendo Switch OLED Console White Joy-Con Dock AC Adapter + 2 Games Included!",
"itemUrl": "https://www.ebay.com/itm/267809186741",
"imageUrl": "https://i.ebayimg.com/images/g/L3IAAeSwLcZqyUSi/s-l1200.png",
"recordType": "single_unit_listing",
"soldUnits": 1,
"averageSoldPrice": { "amount": "184.00", "currency": "USD", "display": "$ 184.00" },
"totalSales": { "amount": "184.00", "currency": "USD", "display": "$ 184.00" },
"averageShipping": { "amount": "10.00", "currency": "USD", "display": "$ 10.00" },
"soldPrice": { "amount": "184.00", "currency": "USD", "display": "$ 184.00" },
"soldDate": "2026-10-09",
"lastSoldDate": "2026-10-09",
"listingFormat": "Fixed price",
"condition": null
}
],
"requestId": "20fca48c-c1d6-4096-8f39-73a65d1cb9af"
}Rows are listings, not single transactions
Each row is one eBay listing and everything it sold inside your date range:
recordType: "single_unit_listing": the listing sold one unit.soldPriceandsoldDateare that sale.recordType: "listing_aggregate": the listing sold several units (common for multi-quantity fixed-price listings).soldUnitsis how many,averageSoldPriceandtotalSalesdescribe them,lastSoldDateis the most recent one, andsoldPriceandsoldDatearenull.
Use singleUnitOnly=true when you want one-off sales only, for example graded cards or used electronics.
Other things to know
- Prices are always USD, on every eBay site.
- Cleanup is opt-in. By default rows are eBay's results for your keyword exactly as listed, so
accessories, other graders and lots can appear (
relevanceApplied: false). Addrelevance=trueto clean them. - Condition is not available in this data (
conditionisnull). Older listings can also be missing a title, link, or image.
Cleanup
Add relevance=true to remove rows that are not the product you searched for: accessories, parts, lots and
bundles, other models, and, for graded cards, other graders or grades and ungraded "PSA 10 candidate" copies.
It uses the same cleanup as the standard sold search, then drops prices far from the cleaned median. The
response then has relevanceApplied: true and rawResultCount (rows before cleanup). It still costs one request
per page and adds about a second.
curl -H "Authorization: Bearer cs_live_REPLACEWITHYOURKEY" \
"https://api.compsniper.com/v1/research/sold?keyword=charizard+4+celebrations+psa+10&startDate=2023-10-15&endDate=2026-10-14&sort=lastSoldDescending&relevance=true"In a test on October 14, 2026, the newest 50 rows for that search went from a $25 to $20,000 price range (CGC and SGC slabs, raw copies, reprints, lots) to 24 PSA 10 copies between $387 and $551. For "nintendo switch oled" the page median went from $31 (cases and accessories) to $180.
If cleanup is ever unavailable, the request still succeeds with uncleaned rows, relevanceApplied: false and a
warning, so your code can tell the difference.
Getting more results
Most empty or short answers come from the request, not from missing data. Check these three things first.
1. Keep the keyword short. eBay's research search only returns listings whose title contains every word you send. A whole listing title, or extra words like a color, a seller note or a location, usually matches nothing.
| Returns little or nothing | Works |
|---|---|
CyberPower PDU81001G switched metered by outlet PDU 8 outlet 1U (0) | CyberPower PDU81001 (12) |
TOTO SW4724AT40 S7A round (0) | TOTO WASHLET S7A (50 on page 1) |
prada sunglasses tortoiseshell. there are some scratch marks on lenses (0) | prada sunglasses spr 16y (2 in Australia over 3 years) |
Start with brand plus model or set number, then add one word at a time if the results are too broad. If a long search returns 0, retry it shorter before treating the item as unsold.
2. Widen the dates. A few days of history often holds only a handful of sales, or none. The same One Piece card code returned 13 rows for the last 30 days and a full page of 50 for the last 90 days. Use the full 3 years when you are pricing something rare.
3. Ask for the next page. One page holds at most 50 rows. A resultCount of exactly 50 with
hasNextPage: true means there is more: send the same request with page=2, page=3 and so on (see
Pagination).
Totals and sizing a search
totals describes the whole search for your date range, not just the current page, and is the same on
every page. It is null when the search has no sales.
totalSold: units sold across every row. Each row is at least one sold unit, so the number of rows is never higher thantotalSold. Pages needed are at mosttotalSolddivided by yourlimit, rounded up. One call to page 1 is enough to size a backfill.totalSellers,averageSoldPrice,minSoldPrice,maxSoldPrice,averageShippingandtotalSalescover the same window. Percent fields (freeShippingPercent,sellThroughPercent) are strings such as"28.86".- Totals are for eBay's full result set before
relevance=truecleanup orsingleUnitOnly. Any value that is not available comes back asnull.
The exact number of rows is not available, so totalSold is an upper bound, not an exact page count.
Pagination
Each page holds up to 50 rows. Follow nextPage while hasNextPage is true, up to page 200
(10,000 rows for one search). Each page is a separate request.
Billing and limits
- One page = one request from your normal monthly allowance (or purchased credits). There is no separate plan.
- Failed requests are free. A timeout, upstream error, or cancelled request is never charged.
- History has its own per-minute cap: 20 searches per minute per account, in addition to your plan's
normal per-minute limit. Every response carries
X-History-RateLimit-LimitandX-History-RateLimit-Remaining. Need more? Contact support.
Errors
| Status | Code | Meaning | What to do |
|---|---|---|---|
| 400 | invalid_params | Missing keyword, bad date, date older than 3 years, unknown parameter | Fix the request; do not retry as is |
| 429 | history_rate_limited | More than your history searches per minute | Wait for Retry-After, then retry |
| 429 | rate_limited | Your plan's per-minute limit | Wait for Retry-After, then retry |
| 429 | quota_exceeded | Monthly allowance and credits used up | Upgrade, buy credits, or wait for reset |
| 502, 503 | historical_unavailable | eBay's research source did not answer this time | Retry after a few seconds, with a bound |
All error bodies include code, error, and requestId. See Errors and
Rate limits for the shared rules.