Get eBay sold prices in Python
You want real sold prices in code — what an item actually sold for, not what it is listed at. This is the shortest path: one request, no scraping stack to babysit, and no approval process to get in.
The three lines that do it
One GET request returns cleaned eBay completed sales for a keyword, plus a median/range summary computed over those rows. Copy, add a key, run it.
import requests
KEY = "gk_live_..." # free key: https://grail.solutions/signup
r = requests.get(
"https://grail.solutions/v1/scrape",
params={"keyword": "leica m6", "count": 60, "sold": "true"},
headers={"Authorization": f"Bearer {KEY}"},
)
data = r.json()
print(data["rawMedian"]) # 1549.5
print(len(data["items"])) # 58 real completed sales
print(data["items"][0]["soldPrice"], data["items"][0]["endedAt"])Prefer Node or a shell? The same call:
const r = await fetch(
"https://grail.solutions/v1/scrape?keyword=leica+m6&count=60&sold=true",
{ headers: { Authorization: `Bearer ${process.env.GRAIL_KEY}` } },
);
const data = await r.json();
console.log(data.rawMedian, data.items.length);curl -H "Authorization: Bearer $GRAIL_KEY" \
"https://grail.solutions/v1/scrape?keyword=leica+m6&count=60&sold=true"Get a key at /signup — 100 requests a month free, no card. Then create the secret at /dashboard/api-keys (shown once).
Why the obvious paths are harder than they look
Most people spend a weekend discovering the three dead ends first, so here they are up front:
- eBay’s Product Research (Terapeak) is a dashboard. It reports aggregates — average price, range, sell-through — but not individual sold rows, and it has no JSON API. Fine to eyeball; useless to feed a model.
- eBay’s official sold-data API (Marketplace Insights) is the one first-party source of completed sales, but it is a limited-release API behind Business-level approval — and access is currently restricted, with no self-serve path in.
- Scraping eBay yourself works until it doesn’t: you inherit residential proxies, CAPTCHAs, cookie/session rotation, and a parser that breaks when the page layout changes. That pipeline is a second product you have to maintain.
Grail is the fourth path: a hosted endpoint that returns the same roughly-90-day window of completed sales as structured JSON — no approval gate, no scraping stack.
What you get back
Each call returns up to 240 rows (count = 60 · 120 · 240), each carrying the final sold price, sale date, condition, seller, shipping, item location, and a link to the source listing. The response also includes a summary — rawMedian, mean, min, max, p25, p75, and average shipping — so the number is ready to use.
Add relevance=true to strip accessories, parts, and wrong-model matches before the median is computed. Add relevance=false for the raw eBay feed. By default you get the raw completed sales — the same shape SoldComps-style tools return — with cleaning as an opt-in.
One gotcha that bites everyone
Prices come back as decimal strings (e.g. "1549.50"), not floats — this avoids silent rounding on money. Cast before you do math:
prices = [float(row["soldPrice"]) for row in data["items"] if row.get("soldPrice")]Where to go next
- Quickstart — key → first call in under a minute.
- API reference — every parameter, filter, and field.
- MCP server — hand the same data to an agent in one tool call.
- Max Mode — deep pagination and async batch when you need more than 240.
Ready? Grab a free key and make the call: grail.solutions/signup.