An eBay seller can ask any price. A listing returned by a sold search gives you a different observation, but it still does not establish the amount the buyer paid. Comparing those two sets is useful when you keep their meanings separate and make the products comparable.
This guide builds a small analysis with SoldFetch: collect active and sold-search results, review product matches, choose one price basis, and report each sample honestly. The result is a research aid for a sourcing tool or internal dashboard. It is not a valuation, a transaction ledger, or an exact measure of demand.
All numeric examples below are synthetic demonstration data. They are not current eBay prices.
Define the two questions before collecting listings
An active search answers, “What displayed asking prices appear among these returned listings?” A sold search answers, “What displayed prices appear among these returned sold-search listings?” Those are related questions with different populations and observation windows.
SoldFetch exposes both through GET /v1/scrape. Set sold=false for active listings and sold=true for sold-search listings. Active rows use currentPrice and currentCurrency; sold rows use soldPrice and soldCurrency.
The sold response includes a crucial limitation: quality.acceptedOfferAmount is null. A displayed amount may differ from an undisclosed accepted offer. quality.saleDateVerified and quality.exactProductMatchVerified are false. Keep those qualifications attached to the analysis rather than removing them during normalization.
Give your two outputs explicit names, such as “active asking-price sample” and “displayed sold-search price sample.” A label like “actual sale price” would imply evidence the response does not provide.
Write one shared comparable-listing policy
Start narrow enough that a reviewer can decide whether a listing belongs. For example, use one headphone model, complete used units, one marketplace, and one currency. Exclude replacement cushions, charging cases, damaged units, and multi-item bundles unless those are the products you intend to study.
| Decision | Shared policy for this example |
|---|---|
| Marketplace | ebay.com |
| Product | One exact headphone model; reviewed by a person |
| Condition | Used complete units; unresolved cases held for review |
| Currency | USD only |
| Price basis | Single displayed item amount, excluding shipping |
| Collection budget | One page per population for the first experiment |
| Observation time | Save the source timestamps and collection run time |
This policy is application metadata. Model identity, completeness, battery health, and accessory inclusion are not guaranteed search filters. Use supported parameters to retrieve candidates, then apply the detailed rules after review. Even exactMatch=true only removes supplier-flagged loose matches; it does not independently verify product identity.
Version the policy. If next week's analysis includes refurbished units or changes to item-plus-shipping prices, create a new cohort or mark the change clearly. Otherwise a methodological change can look like a price movement.
Collect active and sold samples with matching parameters
Check capabilities before making data requests. It describes deployment availability and enabled marketplaces. A tutorial does not guarantee that a particular data route is available at the moment you run it.
These two requests differ only in sold. Each response should be stored separately, with its request parameters and warnings:
curl --fail-with-body --get 'https://soldfetch.com/v1/scrape' \
--data-urlencode 'keyword=sony wh-1000xm5' \
--data-urlencode 'ebaySite=ebay.com' \
--data-urlencode 'itemCondition=used' \
--data-urlencode 'exactMatch=true' \
--data-urlencode 'count=60' \
--data-urlencode 'page=1' \
--data-urlencode 'sold=false' \
--header "API-KEY: $SOLDFETCH_API_KEY" > active.json
curl --fail-with-body --get 'https://soldfetch.com/v1/scrape' \
--data-urlencode 'keyword=sony wh-1000xm5' \
--data-urlencode 'ebaySite=ebay.com' \
--data-urlencode 'itemCondition=used' \
--data-urlencode 'exactMatch=true' \
--data-urlencode 'count=60' \
--data-urlencode 'page=1' \
--data-urlencode 'sold=true' \
--header "API-KEY: $SOLDFETCH_API_KEY" > sold.json
Check the HTTP status before treating either saved file as a successful dataset. Retain fetchedAt and each row's scrapedAt: collecting responses close together does not mean every source observation was created at the same instant. Cache reuse and supplier timing can differ.
For a larger sample, use a fixed page budget, deduplicate item IDs within each population, and stop on an empty page or a page with no new IDs. hasNextPage is inferred and is not a completeness certificate. Record a failed or interrupted run as incomplete rather than treating its partial count as a full sample.
Review identity and preserve the evidence
Create a review file with item ID, include/exclude decision, reason, and reviewer timestamp. Keep uncertain candidates out of the calculation until resolved. A source URL and saved title make the review auditable; neither alone guarantees that the listing is the intended product.
Selected item lookups can provide itemSpecifics, condition description, and images through /v1/item/{itemId}. Use those fields as evidence, not a promise that every attribute exists. The item API guide explains that enrichment workflow.
Preserve the original response even when you create a cleaner analysis table. A reviewer should be able to distinguish an API field from a local decision such as reviewDecision=include or bundleClass=complete_single_unit. Never write those local judgements back into a field named “verified by SoldFetch.”
Choose a price basis and treat unknowns explicitly
For item-only analysis, compare soldPrice with currentPrice after checking their corresponding currencies. For item-plus-shipping analysis, use totalPrice only where available and supported by compatible shipping currency.
A null shipping amount means unknown shipping, not free shipping. The normalized response provides shippingType and shippingCurrency; explicitly free shipping can be represented as zero. A price range is marked quality.priceBasis="range_lower_bound" and should not be treated as a single-price observation.
Keep separate counts for missing amounts, unknown shipping, range prices, nonmatching currencies, rejected products, and unresolved reviews. If one side loses many more rows under your policy, that difference matters as much as the medians.
Do not “correct” an unknown accepted offer by assuming a discount percentage. That substitutes a model for evidence. If you later add a modeled estimate, name it separately and keep the observed displayed amounts accessible.
Calculate two descriptive summaries
The companion Python example runs offline with synthetic reviewed rows. It uses Decimal, checks listing type and currency, excludes ranges and unknown prices, deduplicates IDs, and reports one median per population. Its fixture deliberately includes a duplicate, an excluded accessory, and an incompatible currency.
python3 ebay-sold-vs-asking-prices.py
For its synthetic inputs, the reviewed active item-price sample has three rows and a median of USD 150.00. The reviewed sold-search sample has three rows and a median of USD 120.00. The displayed-sample difference is USD 30.00.
That calculation is reproducible, but it does not establish that buyers pay 30 dollars less, that an individual item will sell, or that the samples contain every comparable listing. The useful statement is deliberately narrower: the two collected and reviewed samples have different displayed-price distributions under the documented policy.
Avoid turning result counts into a sell-through claim
Dividing a sold-search count by an active count does not produce an exact sell-through rate. The sets can cover different time windows, contain relisted items, overlap imperfectly, and expose inferred pagination. Search ordering and your page budget affect which listings you observe.
Likewise, a larger displayed sold-search sample does not prove stronger current demand. To evaluate turnover, you would need a defensible observation window, stable inventory definitions, repeated observations, and explicit treatment of listings entering or leaving the population.
Use result counts as collection diagnostics. Show the requested page budget, completed pages, unique listings collected, accepted matches, and timestamp range. If fewer than your chosen minimum survive review, display “insufficient comparable observations” and the rows that are available.
Publish an analysis someone else can inspect
A useful report includes both distributions, their sample sizes, the cohort definition, price basis, observation times, and exclusion reasons. Link each summary to its source listings and make the unverified accepted-offer and sale-date fields visible in the methodology.
When the numbers disagree with your expectation, inspect the rows first. A different mixture of accessories, bundles, conditions, or listing ages can explain more than a sophisticated chart. For repeated observations, continue with Build an eBay Price Tracking Workflow.
Create a SoldFetch account to try one narrow comparison, then use the Search API reference to adapt the requests to your application. Start with a sample small enough to review completely before automating the summary.



