# eBay sold listings in Python: a complete example

URL: https://ebaysoldlistingsapi.com/blog/ebay-sold-listings-api-python

Updated: 2026-09-29



Pull eBay sold listings into Python with requests, compute a clean median price, handle retries, run batch jobs, and load everything into pandas. Copy-paste code.



This is a complete, production-ready Python workflow for eBay sold data: one search, a clean price, retries that survive busy moments, batch jobs for many keywords, and a pandas DataFrame at the end.

### 1. Setup

```
pip install requests pandas
export EBAY_SOLD_API_KEY="eb_..."   # from your dashboard
```

### 2. One search with retries

```
import os, time, requests

API = "https://api.ebaysoldlistingsapi.com"
HEADERS = {"Authorization": f"Bearer {os.environ['EBAY_SOLD_API_KEY']}"}
RETRY = {429, 502, 503, 504}

def sold(keyword, **params):
    for attempt in range(4):
        r = requests.get(f"{API}/scrape", params={"keyword": keyword, **params},
                         headers=HEADERS, timeout=90)
        if r.status_code not in RETRY:
            r.raise_for_status()
            return r.json()["results"]
        time.sleep(int(r.headers.get("Retry-After", 2 ** attempt)))
    r.raise_for_status()

rows = sold("shure sm7b", itemCondition="used")
print(len(rows), "sales")
```

Failed requests are never billed, so retrying `429`, `502`, `503`, and `504` costs nothing. Do not retry `400` (bad parameter) or `402` (quota).

### 3. A price you can trust

Use the median, and trim the extremes that come from parts-only units and big bundles:

```
import statistics

def fair_price(rows, trim=0.1):
    prices = sorted(float(r["soldPrice"]) for r in rows if r.get("soldPrice"))
    k = int(len(prices) * trim)
    core = prices[k:len(prices) - k] or prices
    return {
        "n": len(prices),
        "median": statistics.median(core),
        "p25": core[len(core) // 4],
        "p75": core[(3 * len(core)) // 4],
    }

print(fair_price(rows))
```

### 4. Into pandas

```
import pandas as pd

df = pd.DataFrame(rows)
df["soldPrice"] = df["soldPrice"].astype(float)
df["endedAt"] = pd.to_datetime(df["endedAt"])
print(df.groupby("condition")["soldPrice"].agg(["count", "median"]))
weekly = df.set_index("endedAt")["soldPrice"].resample("W").median()
```

Prefer a file? Add `format=csv` and load it straight in: `pd.read_csv(io.StringIO(r.text))`.

### 5. Many keywords: batch jobs

```
job = requests.post(f"{API}/scrape/batch", headers=HEADERS, json={
    "keywords": ["shure sm7b", "steam deck oled", "ps5 slim"],
    "ebaySite": "ebay.com",
}).json()

while True:
    status = requests.get(f"{API}/scrape/batch/{job['jobId']}", headers=HEADERS).json()
    if status["status"] == "complete":
        break
    time.sleep(5)

for r in status["results"]:
    print(r["keyword"], r["status"], r["count"])
```

Each keyword counts as one request, and failed keywords are not charged. Download the whole job as one CSV from `downloadUrl`.

Q: Is there a Python SDK?
A: You do not need one. The API is a single REST endpoint, so requests (or httpx) is all it takes. The examples above are complete.

Q: What timeout should I use?
A: At least 90 seconds. Most searches return in a few seconds, but hard searches can take longer while the API retries on your behalf.

Q: How do I get other countries?
A: Pass ebaySite, for example sold('barbour jacket', ebaySite='ebay.co.uk'). Prices return in the local currency.
