Grail API
Real eBay sold comps.
As an API.
Cleaned, deduped, summarized sold prices — what buyers actually paid, not asking prices. Keys are instant, with no approval queue. Build bots, repricers, portfolio apps and deal scanners on top.
Ask Grail
search the docsQuickstart
curl -H "Authorization: Bearer gk_live_your_key" \
"https://grail.solutions/v1/scrape?keyword=charizard%20base%20set&count=240&sold=true"Every response is JSON. Sold data lives in the items array; the summary block gives you median / mean / min / max / p25 / p75 over the cleaned set.
Authentication
Pass your key as a Bearer token: Authorization: Bearer gk_live_…. Keys are instant — no app review, no waiting, commercial use included. A missing or invalid key returns 401 {"error":"unauthorized"}.
Endpoints
/v1/scrapeSold (or active) listings for a keyword, cleaned and summarized.
keyword— requiredcount— 1–240 (default 240)page— 1+sold—true(default) | falseebaySite— ebay.com today (more eBay sites on the roadmap)minPrice·maxPrice·itemCondition·buyingFormatsoldAfter·soldBefore·sortOrder·exactMatch
/v1/scrape/jobsBulk / bursty work goes to an async job queue — paced so we never get blocked. Returns 202 with a job_id. Poll it with GET /v1/scrape/jobs/{job_id}.
/v1/usageCurrent key usage, plan, monthly quota, per-minute limit and reset window.
/v1/ledger/statsLedger size and cache stats for the sold-data store.
Response
{
"keyword": "charizard base set",
"page": 1,
"totalItems": 207,
"totalResults": "240",
"hasNextPage": true,
"rawMedian": 215.0,
"rawSampleCount": 240,
"summary": {
"count": 207,
"currency": "USD",
"median": 215.0,
"mean": 268.42,
"min": 42.0,
"max": 1999.99,
"p25": 128.5,
"p75": 349.0,
"avgShipping": 6.4
},
"items": [
{
"itemId": "1234567890",
"title": "1999 Pokemon Base Set Charizard 4/102 Holo PSA 9",
"url": "https://www.ebay.com/itm/1234567890",
"condition": "Pre-Owned",
"conditionId": 3000,
"soldPrice": "1250.00",
"shippingPrice": "0.00",
"totalPrice": "1250.00",
"endedAt": "2026-09-28",
"buyingFormat": "buyItNow"
}
]
}Prices are decimal strings (exact, no float drift). Response headers: X-Grail-Source (live | ledger), X-Request-Id, X-Usage-Remaining.
Examples
Python
import requests
r = requests.get(
"https://grail.solutions/v1/scrape",
params={"keyword": "charizard base set", "count": 240, "sold": True},
headers={"Authorization": "Bearer gk_live_your_key"},
)
data = r.json()
print(data["summary"]["median"], data["summary"]["count"])JavaScript
const r = await fetch(
"https://grail.solutions/v1/scrape?keyword=charizard%20base%20set&count=240",
{ headers: { Authorization: "Bearer gk_live_your_key" } }
)
const { items, summary } = await r.json()
console.log(summary.median, items.length)Build tools
Import the Postman collection to hit every endpoint in seconds, or generate a typed client from the OpenAPI document.
Errors
401— missing / invalid key.429— quota exceeded ({"error":"quota_exceeded"}); checkX-Usage-Remaining.503— upstream momentarily rate-limiting us (server_busy/upstream_blocked). Retry with backoff.
Pricing
Instant keys. Free tier. One request returns up to 240 rows. Overage credits are billed per 1,000 requests.
- 100 requests / mo
- 60 req / min
- 1 key
- 2,000 requests / mo
- 60 req / min
- 2 keys
- 10,000 requests / mo
- 60 req / min
- 5 keys
- 50,000 requests / mo
- 60 req / min
- Unlimited
Need more volume or a custom plan? See all tiers →
MCP connector
Point Claude, Cursor, Codex or any MCP client at the Grail comps server and ask for sold prices in plain language. Four tools — search_sold, card_research, shipping_rates and usage — behind the same bearer key you already have. Hosted streamable-HTTP at https://grail.solutions/mcp, no local install.