CompsAPI Reference
Real eBay sold prices as JSON. Up to 3 years of history, prices are what buyers actually paid (accepted best offers included), with a trading card layer for graded cards, raw cards and comps.
Quickstart
1. Check your key
Every request sends your key in the X-API-Key header. This call is free and shows your limits.
curl -H "X-API-Key: YOUR_API_KEY" https://api.compsapi.com/v1/usage
2. Get comps for a card
One call returns price stats from every matching sale in the last 90 days, plus the 10 newest sales.
curl -H "X-API-Key: YOUR_API_KEY" \
"https://api.compsapi.com/v1/comps?q=umbreon+vmax+215/203&grader=PSA&grade=10"
import requests
r = requests.get(
"https://api.compsapi.com/v1/comps",
params={"q": "umbreon vmax 215/203", "grader": "PSA", "grade": "10"},
headers={"X-API-Key": "YOUR_API_KEY"},
timeout=30,
)
data = r.json()
print(data["stats"]["median"], "from", data["stats"]["count"], "sales")
const url = new URL("https://api.compsapi.com/v1/comps");
url.search = new URLSearchParams({ q: "umbreon vmax 215/203", grader: "PSA", grade: "10" });
const res = await fetch(url, { headers: { "X-API-Key": "YOUR_API_KEY" } });
const data = await res.json();
console.log(data.stats.median, "from", data.stats.count, "sales");
3. Read the answer
{
"ok": true,
"query": "umbreon vmax 215/203",
"count": 84,
"count_before_filters": 241,
"filters": { "grader": "PSA", "grade": "10", "exclude": "junk" },
"stats": {
"count": 84, "median": 3937.0, "mean": 3993.86, "p25": 3849.99, "p75": 4058.93,
"min": 3375.0, "max": 4650.0, "outliers_removed": 0,
"newest_sale": "2026-10-05T22:46:00Z", "oldest_sale": "2026-09-14T23:45:00Z"
},
"items": [
{
"item_id": "820200933060",
"title": "Pokemon TCG Umbreon VMAX 215/203 PSA 10 Evolving Skies",
"last_sold": "2026-10-05T22:46:00Z",
"price": 3800.0,
"format": "FIXED_PRICE",
"url": "https://www.ebay.com/itm/820200933060",
"card": { "graded": true, "grader": "PSA", "grade": "10", "card_number": "215/203",
"language": null, "first_edition": false, "flags": [] }
}
]
}
Same card, ungraded: /v1/comps?q=umbreon+vmax+215/203&graded=false (94 sales, median $1,692.50 at the time of writing).
Authentication
Send your key in the X-API-Key header. Authorization: Bearer YOUR_API_KEY also works. Keep the key on your server; do not ship it in a browser or mobile app.
Set a User-Agent header on every request (most HTTP libraries do this for you). Requests from Python's built-in urllib without one are blocked by our firewall; requests, httpx, Node fetch, axios, Go and curl all work as is.
Sold listings
GET/v1/sold returns sold listings for a search, newest sale first.
| Parameter | Default | Description |
|---|---|---|
q | required | Search terms, max 300 characters. eBay matches every word in the title. |
days | 90 | How far back to search, 1 to 1095 (3 years). |
size | 200 | Results per page, 10 to 240. |
page | 1 | Page number, 1 to 50. When next_page is true, ask for the next page to get older sales. |
condition | any | Only sales in that condition: new, open_box, refurbished, used or parts. Comma separate to combine, for example new,open_box. With one value, every item also carries condition. Works on /v1/comps too. |
best_offer | off | 1 adds best_offer to every item: true when the sale went through as an accepted offer. only returns accepted best offer sales and nothing else. Works on /v1/comps too. |
| card options | off | Add any of the card options to parse, filter and price the results. |
Each search covers eBay US plus international eBay sites and a common spelling variant of your terms, merged and de-duplicated, so count can be a little higher than size.
Comps
GET/v1/comps is built for pricing a card. It searches the newest 200 sales over 90 days, removes junk listings, applies your card options and returns stats plus the 10 newest matching sales. One call counts as one request.
| Parameter | Default | Description |
|---|---|---|
q | required | The card, as specific as you can (see tips). |
days, size | 90, 200 | Same as /v1/sold. |
exclude | junk | Pass exclude=none to keep every listing. |
| card options | grader, grade, graded, lang, first_edition. |
Stats
| Field | Description |
|---|---|
count | Matching sales with a price. |
median, mean | Middle and average sold price (USD) after removing outliers. |
p25, p75 | 25th and 75th percentile: the range the middle half of sales fell in. |
min, max | Lowest and highest price used. |
outliers_removed | Sales more than 3 times the middle range away from it (8+ sales needed), usually mislabeled listings. |
newest_sale, oldest_sale | The time span the stats cover (UTC). |
Usage
GET/v1/usage shows searches used today (UTC day), your daily limit and when your key expires. It does not count towards your limit.
{"ok": true, "day_utc": "2026-10-09", "used": 1250, "daily_cap": 5000,
"trial_hours": 72, "trial_started": "2026-10-07T09:12:44Z", "expires": "2026-10-10T09:12:44Z"}
A trial key's clock starts at its first search, so expires is null until then.
Card options
Add these to /v1/sold or /v1/comps. Each item then gets a card object, and the options filter the results. On /v1/sold they filter the page you asked for; count_before_filters shows how many there were before.
| Option | Example | Keeps |
|---|---|---|
cards=1 | Everything, just adds the card object. | |
grader | PSA, BGS,CGC | Cards graded by these companies. BECKETT counts as BGS. Also SGC, TAG, ACE and others. |
grade | 10, 9.5, 9,10 | Cards with this grade. |
graded | true / false | Only slabs, or only raw cards. "PSA 10 contender" style listings count as raw. |
lang | ja, en,ja | Cards whose title names this language: en ja zh ko de fr it es pt. Titles that name none are left out when you use it. |
first_edition | true | 1st edition cards only (or false for none). |
exclude | junk, lot,sealed | Drops listings with these flags. junk means lot, mystery, accessory, replica, keyword_stuffing, title_missing and not_graded_claim. |
stats=1 | Adds stats for what is left (on /v1/comps it is always on). |
# Which sales were accepted offers
curl -H "X-API-Key: YOUR_API_KEY" \
"https://api.compsapi.com/v1/sold?q=omega+speedmaster&days=30&best_offer=1"
# PSA 9 and 10 Japanese Luffy cards, no junk, with stats
curl -H "X-API-Key: YOUR_API_KEY" \
"https://api.compsapi.com/v1/sold?q=luffy+psa&grader=PSA&grade=9,10&lang=ja&exclude=junk&stats=1"
Card fields
Read from the listing title. Fields are null when the title does not say.
| Field | Example | Description |
|---|---|---|
graded | true | The title names a grading company and a grade. |
grader | "PSA" | PSA, BGS, CGC, SGC, TAG, ACE, AGS, HGA, GMA, MNT or ARS. |
grade | "9.5" | Grade as text, so 9.5 stays exact. |
grade_label | "pristine" | gem mint, pristine, black label, mint or near mint, when stated. |
card_number | "215/203", "OP05-119" | Collector number or set code as written. |
year | 1999 | Year in the title. |
language | "ja" | Language named in the title. |
first_edition | false | Title says 1st edition. |
flags | ["lot"] | Warnings about the listing, see below. |
Flags
| Flag | Means | Example title |
|---|---|---|
lot | More than one card sold together. | "4 x Ace Grade 10 Charizard Bundle" |
mystery | Mystery pack, repack or "chance to win" listing. | "Mystery Graded Slab, chance at PSA 10 Charizard" |
sealed | Sealed product: booster box, pack, ETB, tin. | "151 Booster Box SV2a Sealed" |
accessory | Case, sleeves, binder or display, not a card. | "Acrylic case for booster box" |
replica | Custom, proxy, fan art or fake. | "Umbreon VMAX Fan Art Holo" |
keyword_stuffing | Several graders named without a grade, usually to show up in searches. | "Charizard PSA bgs CGC tag" |
not_graded_claim | Raw card described with a grade ("contender", "potential PSA 10"). | "Charizard 4/102 PSA 10 Contender" |
title_missing | The listing title was not available. |
Card fields and flags come from rules that read the title. They catch the common patterns but will miss unusual titles, so treat flags as hints. We report what sold; we do not authenticate cards.
Getting good comps
- Be specific. Include the card name and number:
umbreon vmax 215/203, notumbreon. A broad search returns the newest 200 sales across thousands of cards, which may span only a few hours. - Let the options do the grading. Search
charizard 4/102 base setwithgrader=PSA&grade=8rather than putting "psa 8" inq, so titles written "PSA8" or "PSA 8 NM-MT" are still caught. - Raw vs graded:
graded=falsegives the raw market for the same card. - Thin cards: raise
days(up to1095) when a card sells rarely, and checkstats.countbefore trusting a median.
Item fields
| Field | Description |
|---|---|
item_id | eBay item number. |
title | Listing title. |
last_sold | Time of the most recent sale on this listing (UTC). |
price | Average price actually paid per unit, USD. Accepted best offers show the real offer price, not the asking price. |
units_sold, total_sales | Units sold on this listing in the window, and their total value. |
format | FIXED_PRICE or AUCTION. |
bids | Number of bids (auctions). |
best_offer | Only with best_offer=1 or only. true: sold as an accepted best offer, and price is the accepted offer. false: sold at the listed price or at auction. null: could not be confirmed either way (rare, mostly very busy searches). |
shipping_cost, free_shipping_pct | Average shipping charged, and the share of sales with free shipping (0 to 100). |
url, image | Listing link and main image. |
card | Only with card options: see card fields. |
Response fields: ok, query, page, ms (our response time), count, no_results, next_page, days, size, cached and cached_age_s (identical searches within 15 minutes come from cache), plus filters, count_before_filters and stats when card options are used.
Errors
| Status | Meaning | What to do |
|---|---|---|
400 | Missing or invalid parameter, or eBay rejected the search as too broad. | Fix the request or narrow the terms. |
401 | Missing or invalid API key. | Check the X-API-Key header. |
403 | Key expired, or the request was blocked by our firewall (no User-Agent). | Contact us, or set a User-Agent header. |
429 | Daily limit reached, or too many requests at once. | Wait, or lower your concurrency. |
503 | Temporary problem on our side. | Retry after a second or two. |
Failed searches do not count towards your daily limit. Every error body is JSON with "ok": false and an error message.
Limits
Your daily limit, how many requests you can run at the same time, and your expiry are set on your key and listed in the email it came with; /v1/usage always shows them. Trial keys run 72 hours from your first search.
About the data
- Sold listings from eBay US and international eBay sites, up to 3 years back, usually within minutes of the sale.
- Prices are the price actually paid, in USD. Accepted best offers show the real offer price.
- A listing that sold several units appears once, with
units_soldandtotal_salesfor the window. - We return what sold on eBay. We do not verify authenticity or condition beyond what the title says.
OpenAPI / Postman
Import https://api.compsapi.com/openapi.json into Postman, Insomnia or any OpenAPI tool to get every endpoint ready to call. Set your key as the X-API-Key header.
Questions, or something you need added: reply to the email your key came with.