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](https://soldfetch.com/v1/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:

```sh
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](https://soldfetch.com/blog/ebay-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](/downloads/blog/ebay-sold-vs-asking-prices.py) 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.

```sh
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](https://soldfetch.com/blog/ebay-price-tracking-api-guide).

[Create a SoldFetch account](https://soldfetch.com/auth/register) to try one narrow comparison, then use the [Search API reference](https://docs.soldfetch.com/api-reference/search) to adapt the requests to your application. Start with a sample small enough to review completely before automating the summary.