---
title: "Automate Sold Comps: Price a Whole Inventory From a CSV in One Script"
url: https://www.soldcomps.io/blog/automate-sold-comps
updated: 2026-10-30
answer: "To automate sold comps, put one search per item in a CSV, call a sold comps API for each row, and write back the median, 25th and 75th percentile and the number of clean sales. Flag rows with fewer than 10 sales or a wide price range for manual review, and price the rest from the median. Re-run it on a schedule to keep prices current."
publisher: SoldComps (https://www.soldcomps.io)
---

# Automate Sold Comps: Price a Whole Inventory From a CSV in One Script

> To automate sold comps, put one search per item in a CSV, call a sold comps API for each row, and write back the median, 25th and 75th percentile and the number of clean sales. Flag rows with fewer than 10 sales or a wide price range for manual review, and price the rest from the median. Re-run it on a schedule to keep prices current.

By SoldComps Editorial · Published Oct 30, 2026

Looking up **sold comps** one item at a time works for five items. It doesn't work for 500: a store buyout, a storage unit, a consignment intake, or a catalog you need to keep priced every week.

The fix is a short script. Give it a spreadsheet of items, and it fills in what each one actually sold for, how confident that number is, and which rows a human should look at. This guide walks through one in Python, about 40 lines.

If you'd rather stay in a spreadsheet, the [Google Sheets formula](https://www.soldcomps.io/blog/ebay-sold-listings-google-sheets) does the same thing one cell at a time.

## What you need

- A list of items, one search per row, written the way you'd type it into eBay. Specific beats vague: `canon ae-1 camera 50mm`, not `old camera`.
- An API key. The free plan includes 100 requests a month with no card. [Get one here](https://www.soldcomps.io/signup).
- Python 3 with the `requests` package.

Your CSV, `inventory.csv`:

```csv
sku,query,condition
A101,canon ae-1 camera,used
A102,nintendo ds lite,used
A103,kitchenaid artisan stand mixer,used
A104,levis 501 made in usa vintage,
A105,lladro figurine,
```

The `condition` column is optional. Leave it empty for any condition.

## The script

```python
import csv, os, time, requests

KEY = os.environ["SOLDCOMPS_KEY"]
API = "https://www.soldcomps.io/v1/sold"

def comps(query, condition=""):
    params = {"q": query}
    if condition:
        params["condition"] = condition
    for attempt in range(3):
        r = requests.get(API, params=params, headers={"Authorization": f"Bearer {KEY}"}, timeout=60)
        if r.status_code == 429:  # rate limited: wait as told, then retry
            time.sleep(int(r.headers.get("Retry-After", "10")))
            continue
        r.raise_for_status()
        return r.json()
    raise RuntimeError(f"rate limited: {query}")

with open("inventory.csv") as f, open("priced.csv", "w", newline="") as out:
    rows = csv.DictReader(f)
    w = csv.writer(out)
    w.writerow(["sku", "query", "clean_sales", "p25", "median", "p75", "review"])
    for row in rows:
        d = comps(row["query"], row.get("condition", ""))
        s = d["stats"]
        clean = d["count"] - s["excluded"]
        wide = s["p25"] and s["p75"] and s["p75"] / s["p25"] > 1.6
        review = "few sales" if clean < 10 else "wide range" if wide else ""
        w.writerow([row["sku"], row["query"], clean, s["p25"], s["median"], s["p75"], review])
        print(row["sku"], s["median"], review or "ok")
```

Run it:

```bash
export SOLDCOMPS_KEY=sh_live_...
python price.py
```

## What comes back

Real output for the CSV above, eBay US, last 90 days, Oct 30, 2026:

| sku | query | clean_sales | p25 | median | p75 | review |
|---|---|---|---|---|---|---|
| A101 | canon ae-1 camera | 134 | 110.00 | 135.48 | 164.38 | |
| A102 | nintendo ds lite | 104 | 50.97 | 59.97 | 70.25 | |
| A103 | kitchenaid artisan stand mixer | 126 | 120.00 | 148.28 | 174.99 | |
| A104 | levis 501 made in usa vintage | 67 | 34.99 | 48.00 | 69.88 | wide range |
| A105 | lladro figurine | 72 | 30.00 | 48.50 | 100.88 | wide range |

Three rows are ready to price as they are. Two need a person, and the reason is useful:

- **Levi's 501:** waist, length and era matter. Search each pair with its size.
- **Lladró:** "any figurine" isn't one product. It needs the exact figure name or number.

Notice what the `condition` column did for the KitchenAid. Without it, the same search mixes new and used mixers: a $249.99 median and a middle half from $173.75 to $319.48, wide enough to get flagged. Limited to used sales, it's a tight $120 to $175 around a $148.28 median. Filtering by condition is often the difference between a usable price and a flagged row.

That's the real job of automation here. It doesn't replace judgment. It **sorts** the inventory into "priced" and "look at this", so you spend your time only on the rows that need it.

## Why these thresholds

- **Under 10 clean sales:** the median is a rough guide, not a price. [How many sold comps you need](https://www.soldcomps.io/blog/how-many-sold-comps) explains the cutoff.
- **p75 more than 1.6× p25:** the middle half of buyers paid very different prices, which usually means the search mixes versions or conditions. Tune the number to your category: tighter for electronics, looser for vintage.
- **Clean sales, not raw count:** `count` includes sales left out of the stats (lots, for-parts, empty boxes, replicas, outliers). `count − excluded` is what the median is actually based on.

## Turning comps into prices

The median is what the item sold for. What you do with it depends on your business:

- **Listing price:** start at the median, or slightly above for fast sellers in great condition.
- **Max buy price:** median minus fees, shipping and profit. [Sold comps for resellers](https://www.soldcomps.io/blog/sold-comps-for-resellers) has the formula.
- **Buy or loan offer:** start from the 25th percentile, the quick-sale price. See [pawn shop sold comps](https://www.soldcomps.io/blog/pawn-shop-sold-comps).

Add the column you need to the script. It's one line.

## Keeping prices fresh

Prices drift. To keep a catalog current, run the same script on a schedule (cron, a GitHub Action, or your job runner) and compare each new median to the last one:

- Re-price automatically when the median moves by more than a set amount, say 10%.
- Flag items whose clean sales drop sharply. Demand may be drying up.
- Store each run with a date. After a few weeks you have your own price history for every item you carry.

How often depends on the category. Cards, graphics cards and new consoles move week to week. Kitchenware and vintage household goods barely move in a month.

## Cost and limits

- **One request per search**, whether it returns 3 sales or 200.
- **Never counted:** failed requests.
- **Rate limits** depend on your plan, from 30 to 300 requests a minute. The script above waits and retries when it gets a 429.

Five hundred items is 500 requests, which the $5 Starter plan (1,000 requests a month) covers with room to re-price. Details are on the [pricing page](https://www.soldcomps.io/pricing), and the full parameter list (condition, price range, buying format, days, eBay site) is in the [API docs](https://www.soldcomps.io/docs).

## Other ways to plug it in

- **AI assistants:** connect the [MCP server](https://www.soldcomps.io/mcp-server) and ask Claude or ChatGPT to comp a list for you.
- **Point-of-sale and buy screens:** call the API when an item is scanned and show the quick-sale price next to it.
- **Listing tools:** pre-fill a suggested price from the median when a seller creates a listing.

Whichever way you run it, the habit is the same one that makes manual comps work: clean sales, enough of them, a tight range, and a human for the rest. That's [sold comps](https://www.soldcomps.io/) at scale.

## Frequently asked questions

### How do I automate sold comps?

Keep a list of searches, one per item, and run a script that calls a sold comps API for each one and saves the median, middle range and sales count. Price confident rows automatically and send the rest to a person.

### How many requests does it take to price an inventory?

One request per search. Pricing 500 items takes 500 requests, whether each search returns 3 sales or 200. Failed requests don't count.

### Which items should a script not price on its own?

Rows with fewer than 10 clean sales, rows where the 75th percentile is much higher than the 25th (a wide range), and anything easy to fake or highly condition-sensitive. Those need a person to read the listings.

### How often should I refresh sold comps?

It depends on the category. Fast-moving items like trading cards, graphics cards and new consoles can shift week to week, so refresh them weekly or daily. Slow categories like kitchenware and vintage household goods are fine monthly.

SoldComps sells an eBay sold comps API, so we have a commercial interest in this topic. Corrections: hello@soldcomps.io
