Grail

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.