compsapi-com Comps API
The Comps API from compsapi-com — 1 operation(s) for comps.
The Comps API from compsapi-com — 1 operation(s) for comps.
Every API here is available over the APIs.io API and to AI agents over MCP.
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
find_apisBrowse and filter every API in the catalog.get_api_artifactsOne API's artifacts, grouped by type.get_openapiThe primary OpenAPI for this API.find_similar_apisAPIs that look like this one.apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.resolveTurn a domain, URL or GitHub org into the provider it belongs to.find_cohortsEvery scored population of providers in the catalog.curl "https://apis.io/api/v1/apis/compsapi-com:compsapi-com-comps-api"
curl "https://apis.io/api/v1/apis?limit=25"
Discovery needs no key. Ratings and market analysis are Pro.
Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.
A second provider on the same verified email joins the account you already have.
openapi: 3.2.0
info:
title: Comps API
version: 1.4.0
description: 'Real eBay sold prices as JSON. Up to 3 years of history, real accepted Best Offer prices, and a trading card layer for graded cards and comps. Docs: https://compsapi.com/docs · Keys: https://app.compsapi.com'
contact:
name: CompsAPI support
url: https://app.compsapi.com/support
email: keys@compsapi.com
termsOfService: https://compsapi.com/terms
x-logo:
url: https://compsapi.com/apple-touch-icon.png
altText: CompsAPI
servers:
- url: https://api.compsapi.com
security:
- apiKey: []
- bearer: []
tags:
- name: Comps
paths:
/v1/comps:
get:
summary: Price stats plus the 10 newest matching sales (card comps)
description: Searches the newest sales, removes junk listings, always parses the card layer, and returns stats plus the 10 newest matching sales. Not pageable.
parameters:
- $ref: '#/components/parameters/q'
- $ref: '#/components/parameters/days'
- $ref: '#/components/parameters/start'
- $ref: '#/components/parameters/end'
- $ref: '#/components/parameters/size'
- $ref: '#/components/parameters/condition'
- $ref: '#/components/parameters/best_offer'
- $ref: '#/components/parameters/grader'
- $ref: '#/components/parameters/grade'
- $ref: '#/components/parameters/graded'
- $ref: '#/components/parameters/lang'
- $ref: '#/components/parameters/first_edition'
- $ref: '#/components/parameters/exclude'
- $ref: '#/components/parameters/require'
- $ref: '#/components/parameters/print_run'
- name: native_price
in: query
description: '1 adds native_price and native_currency: the amount in the currency the buyer actually paid. eBay US sales are free (already USD); each non US sale costs one extra read, so it is capped per request and native_price_info reports coverage.'
schema:
type: string
enum:
- '0'
- '1'
responses:
'200':
description: Stats and newest sales.
content:
application/json:
schema:
$ref: '#/components/schemas/SoldResponse'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
tags:
- Comps
operationId: getV1Comps
x-operation-id-source: derived
components:
parameters:
best_offer:
name: best_offer
in: query
schema:
type: string
enum:
- '1'
- only
- verify
description: 1 adds the best_offer flag with real accepted prices; only returns accepted offers exclusively; verify also reads the public sold page and adds best_offer_confidence, asking_price and asking_gap_pct, because eBay's own accepted offer filter misses some negotiated sales.
days:
name: days
in: query
schema:
type: integer
minimum: 1
maximum: 1095
default: 90
description: How far back to search, up to 3 years.
exclude:
name: exclude
in: query
schema:
type: string
description: Flags to drop, e.g. junk or lot,sealed. none keeps everything.
condition:
name: condition
in: query
schema:
type: string
description: new, open_box, refurbished, used or parts. Comma separate to combine.
lang:
name: lang
in: query
schema:
type: string
description: en, ja, zh, ko, de, fr, it, es, pt. Comma separate to combine.
graded:
name: graded
in: query
schema:
type: string
enum:
- 'true'
- 'false'
description: Only slabs, or only raw cards.
grader:
name: grader
in: query
schema:
type: string
description: PSA, BGS, CGC, SGC, TAG, ACE and others. Comma separate to combine.
start:
name: start
in: query
schema:
type: string
format: date
example: '2024-12-01'
description: First day of an absolute window, written YYYY-MM-DD. Use start and end to ask for a fixed historical period instead of a lookback from today, for example start=2024-12-01&end=2025-12-01. Inclusive. Given on its own, the window runs to now. A start more than 1095 days ago is refused; on a plan with less history the start is pulled forward to that plan's limit and plan_limit says so. Slicing a long period into several short windows is faster and more reliable than walking deep pages.
end:
name: end
in: query
schema:
type: string
format: date
example: '2025-12-01'
description: Last day of an absolute window, written YYYY-MM-DD. Inclusive, so the whole of that day counts, and it never runs past today. Given on its own, the window starts days before it. The answer carries window with the period actually served.
first_edition:
name: first_edition
in: query
schema:
type: string
enum:
- 'true'
- 'false'
grade:
name: grade
in: query
schema:
type: string
description: Grade as text, e.g. 10 or 9.5. Comma separate to combine.
q:
name: q
in: query
required: true
schema:
type: string
maxLength: 300
description: Search terms. eBay matches every word in the title.
require:
name: require
in: query
schema:
type: string
description: Keep only sales whose title holds every one of these words or phrases, comma separated, for example require=lebron james or require=lebron james,red disco. Matching ignores case and punctuation and lands on whole words, so 2017 matches a 2017-18 title and ron jam matches nothing. Up to 6 terms. This is how you search broadly and then insist on the one thing that identifies your item. It can only remove rows, never find new ones, so the answer carries title_filter with the count before it ran and how many it removed. It runs on the page already retrieved, so on a broad query widen size or walk pages to see more.
size:
name: size
in: query
schema:
type: integer
minimum: 10
maximum: 240
default: 200
description: Results per page.
print_run:
name: print_run
in: query
schema:
type: string
example: '125'
description: 'The serial number a card is limited to, read from the title. A number keeps only that print run, for example print_run=125 for a /125 parallel. numbered keeps every serial numbered card. any filters nothing and just labels the rows. All three add print_run to every item. #098/125 reads as 125 and not as 98, a bare /85 reads as 85, and a season like 2025/26, a card number like #100, a 100 card lot and a date are all left alone. A print run is not an identity: a /125 parallel of the same player exists in several seasons at very different prices, so keep the year in q and use this to separate parallels within it. Reported in title_filter the same way as require.'
responses:
BadRequest:
description: Missing or invalid parameter.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
Unauthorized:
description: Missing or invalid API key.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
schemas:
Item:
type: object
properties:
item_id:
type: string
title:
type: string
last_sold:
type: string
format: date-time
price:
type: number
description: Average price actually paid per unit, USD. Accepted best offers show the real offer price.
units_sold:
type: integer
total_sales:
type: number
format:
type: string
enum:
- FIXED_PRICE
- AUCTION
bids:
type: integer
us_sale:
type: boolean
description: true when the sale was made on eBay US, false when it was made on another eBay site. The default search merges eBay US with international coverage, so this separates them. Which other site a false row came from is not reported, because eBay does not expose it per item. With site pinned, every row is true or every row is false.
native_price:
type:
- number
- 'null'
description: Only with native_price=1. The amount in the currency the sale actually happened in. price is always USD; this is the untouched figure. null when it could not be confirmed, typically a listing that sold several units at different prices.
native_currency:
type:
- string
- 'null'
description: Only with native_price=1. ISO 4217 code for native_price, for example GBP, EUR, AUD.
best_offer:
type:
- boolean
- 'null'
description: Only present with the best_offer parameter. true means the sale was identified as an accepted offer; false means it was not identified as one, which is not the same as a full price sale.
condition:
type: string
description: Only present with a single condition value.
shipping_cost:
type:
- number
- 'null'
free_shipping_pct:
type: number
url:
type: string
image:
type: string
card:
$ref: '#/components/schemas/Card'
best_offer_confidence:
type: string
enum:
- confirmed
- likely
- unlikely
- unknown
description: 'Only with best_offer=verify. confirmed: eBay identifies the sale as an accepted offer. likely: the listing accepted offers and the sale closed materially below the asking price. unlikely: neither holds. unknown: the sale was not on the public pages we read, or another marketplace was pinned, so no comparison was possible.'
asking_price:
type:
- number
- 'null'
description: Only with best_offer=verify. The price eBay's public sold page displays for this listing.
asking_gap_pct:
type:
- number
- 'null'
description: Only with best_offer=verify. How far the asking price sits above what the sale closed at, as a percentage of the amount paid. Negative when the public page reads low.
Stats:
type: object
properties:
count:
type: integer
used_for_stats:
type: integer
median:
type: number
mean:
type: number
p25:
type: number
p75:
type: number
min:
type: number
max:
type: number
outliers_removed:
type: integer
newest_sale:
type: string
format: date-time
oldest_sale:
type: string
format: date-time
Error:
type: object
properties:
ok:
type: boolean
example: false
error:
type: string
Card:
type: object
properties:
graded:
type:
- boolean
- 'null'
grader:
type:
- string
- 'null'
grade:
type:
- string
- 'null'
grade_label:
type:
- string
- 'null'
card_number:
type:
- string
- 'null'
year:
type:
- integer
- 'null'
language:
type:
- string
- 'null'
first_edition:
type:
- boolean
- 'null'
flags:
type: array
items:
type: string
SoldResponse:
type: object
properties:
ok:
type: boolean
query:
type: string
page:
type: integer
ms:
type: integer
count:
type: integer
no_results:
type: boolean
next_page:
type: boolean
days:
type: integer
size:
type: integer
cached:
type: boolean
cached_age_s:
type: integer
source:
type: string
filters:
type: object
count_before_filters:
type: integer
stats:
$ref: '#/components/schemas/Stats'
items:
type: array
items:
$ref: '#/components/schemas/Item'
securitySchemes:
apiKey:
type: apiKey
in: header
name: X-API-Key
bearer:
type: http
scheme: bearer