How to Scrape eBay Listings and Build Sold-Item Comparisons
An active eBay listing tells you what a seller is asking. A sold-listing search gives you a different set of comparisons, but the price displayed on a result still needs to be interpreted in context, especially for Best Offer sales.
The eBay Listings Scraper collects search, category, and sold-item results, with optional item details. I maintain the Actor. This guide keeps active and sold samples separate and shows how to build a comparison without treating every displayed price as a verified final payment.

Define the item before collecting comparisons
Choose the model, capacity, condition, and relevant restrictions. For a phone, carrier lock matters. For a collectible, edition and grading can matter more than the broad product name.
Open a search on eBay and apply those filters first. Copy the resulting URL rather than trying to encode every filter by hand. Check that the results actually match: a broad search can include accessories, parts, bundles, and related products.
For a sold sample, apply Sold items. The example URL below includes both LH_Sold=1 and LH_Complete=1. Do not treat an arbitrary completed-listing sample as sold simply because its listings have ended.
Collect a small sold-item sample
Open the Actor on Apify and use the JSON view of Input:
{
"startUrls": [
{"url": "https://www.ebay.com/sch/i.html?_nkw=iphone+15&LH_Sold=1&LH_Complete=1"}
],
"maxItems": 20,
"scrapeDetails": false,
"proxyConfiguration": {
"useApifyProxy": true,
"apifyProxyGroups": ["RESIDENTIAL"],
"apifyProxyCountry": "US"
}
}
Replace the URL with your own filtered search for a useful comparison. The current schema calls for US residential proxies and enables them by default. Keep that setting for the first run.
maxItems is a total cap across all start URLs. Zero removes the Actor cap, but does not remove eBay's source limits. The documentation describes roughly 10,000 results as the ceiling for a search or category; a capped twenty-row run is only a sample.
Click Start, inspect the dataset and log, and open several returned links. Check model, condition, buying format, and sold-date information before treating the rows as comparable sales.
This abbreviated historical record shows the sold-result fields:
{
"itemId": "236873524013",
"title": "iphone 15 pro 128g Att Cricket Clean Esn",
"price": 298.0,
"priceText": "$298.00",
"currency": "USD",
"condition": "Pre-Owned",
"buyingFormat": "Buy It Now",
"soldDate": "Sold Jun 16, 2026"
}
The title identifies a Pro model and a carrier restriction. It should not be pooled with unlocked base-model phones. The displayed $298 is historical sample data, not a current price benchmark.
Do not assume Best Offer prices are verified payments
The Actor collects the values exposed by eBay's pages. Its documentation calls sold-result prices realized prices, but this guide does not establish that hidden accepted-offer amounts are recovered for every listing.
For ambiguous Best Offer cases, verify the transaction price before including it in a precise sold-price calculation. eBay's own Product Research documentation identifies actual sold prices, including accepted Best Offers, as a capability of that separate research tool. This Actor does not claim access to your Seller Hub research data.
Keep priceText next to the numeric price, and add a manual priceVerified or review-status column. Exclude unresolved prices from calculations that require actual final sale amounts. A cleaner, smaller comparison set is more useful than a large one with uncertain price meanings.
Collect active offers in a separate run
For asking prices, use the corresponding active search:
{
"startUrls": [
{"url": "https://www.ebay.com/sch/i.html?_nkw=iphone+15"}
],
"maxItems": 20,
"scrapeDetails": false,
"proxyConfiguration": {
"useApifyProxy": true,
"apifyProxyGroups": ["RESIDENTIAL"],
"apifyProxyCountry": "US"
}
}
Keep the same product filters you used for sold items. Label the export active and retain the search URL and collection time. A soldCount such as “15+ sold” on an active multi-quantity listing does not make its current asking price a separate verified transaction for each unit.
The existing screenshot shows the dataset layout from an earlier run:

Add details when a title leaves the match uncertain
Enable scrapeDetails for a small search when you need item specifics, seller notes, seller feedback, or the full image gallery. These fields help identify differences hidden behind a similar title.
For phones, inspect network and lock-status specifics. For used items, read seller notes for damage, missing parts, or refurbishment details. Seller feedback describes the seller's history; it does not replace checking the condition of the item in this listing.
Some fields can be absent depending on the listing. Do not turn missing watchers into zero demand or empty sold dates into proof that an item never sold. Review the source and the run log when a field matters to your decision.
Build two comparison groups, not one average
Export CSV for the price sheet, keeping itemId, url, title, price, priceText, currency, condition, buyingFormat, shipping, and soldDate. Use JSON for nested item specifics and image arrays.
Deduplicate by item ID within each snapshot. Then group by exact model, specification, condition, and selling format. Keep bundles and parts-only listings out of a working-product group. Record shipping separately when it is an amount you can verify; a free-text delivery estimate is not automatically a numeric shipping cost.
Calculate separate summaries for active asking prices and verified sold comparisons. Show the sample size, currency, and dates beside each. For illustration, comparable verified values of 280, 300, and 340 have a median of 300. That does not predict the next sale or include fees and other costs absent from the records.
Do not calculate sell-through by dividing a limited sold sample by a separately capped active sample. They may cover different periods and neither is necessarily a complete population. Use a method with a defined time window and coverage if that metric is required.
Budget for the mode and the date
Pricing as of September 6, 2026: the configured listing event is $0.002, and a fully enriched detail result is $0.007. The documentation describes these as the respective basic and detail mode rates; there is no configured Actor-start event. Twenty basic results represent $0.04 in listing event fees, while twenty detail results represent $0.14 in detail event fees.
A listing-rate increase to $0.004 is scheduled for September 15, 2026. Check current pricing and the events charged in a small run before scaling or scheduling it.
If results are irrelevant, refine the URL filters. If they are empty, check the source page, proxy settings, and log. Keep dated snapshots for monitoring, and check a disappeared item directly before classifying it as a sale.
The eBay Node.js example provides a separate reference for automating the workflow after you have checked the two exports.
Frequently asked questions
Does every sold-result price equal the final amount paid?
Do not assume so. This guide does not establish recovery of hidden accepted-offer amounts. Verify ambiguous Best Offer prices before using them in an actual-sale-price calculation.
Does maxItems apply to each URL?
No. It caps results across all start URLs. Keep active and sold searches in separate runs when you want independently sized comparison samples.
Can I calculate sell-through from two capped exports?
Not reliably without a defined time window and coverage. A limited sold sample and a separate active sample are not necessarily complete or comparable populations.
