Complete API reference
Full CompSniper endpoint, parameter, sold-item, active-item, response, and error reference.
Searchable endpoint index
| Method | Endpoint | Purpose |
|---|---|---|
| GET | /v1/scrape | Keyword sold search or active listing search |
| GET | /v1/scrape/category | Browse sold listings by eBay category |
| GET | /v1/summary | Cleaned price intelligence summary |
| GET | /v1/account/usage | Plan, quota, and remaining usage |
| POST | /v1/cards/batch | Start an asynchronous card-pricing batch |
| GET | /v1/cards/batch/:jobId | Poll a card-pricing batch |
| DELETE | /v1/cards/batch/:jobId | Cancel unfinished card work |
| POST | /v1/scrape/max | Start Max Mode |
| GET | /v1/scrape/max/results/:jobId | Poll Max Mode results |
| DELETE | /v1/scrape/max/:jobId | Cancel Max Mode |
Keyword search parameters
keyword, page, count, ebaySite, categoryId, sortOrder, minPrice, maxPrice,
itemLocation, itemCondition, conditionId, buyingFormat, sellerType,
includeCompleteListing, sold, soldAfter, soldBefore, aspectFilter, and relevance.
Sold item fields
itemId, url, thumbnailUrl, fullResThumbnailUrl, epid, title, condition, conditionId,
sellerType, buyingFormat, bestOfferAccepted, bidCount, categoryId, listingType,
shippingPrice, shippingCurrency, shippingType, totalPrice, sellerUsername,
sellerPositivePercent, sellerFeedbackScore, itemLocation, scrapedAt, endedAt, soldPrice, and
soldCurrency.
Active listing fields
With sold=false, sold-only fields are replaced by currentPrice, currentPriceMax, currentCurrency,
watcherCount, unitsSold, acceptsOffers, and timeLeft.
The detailed reference below preserves all examples, tables, response envelopes, MCP instructions, and batch documentation from the original single-page reference.
Overview
CompSniper turns any keyword into up to 240 completed eBay sales in a single request. Each listing carries the sold price, sale date, condition, shipping, and seller reputation across 26 fields of real transaction data, not estimates or asking prices.
All requests go to https://api.compsniper.com and are versioned under /v1. Responses are always JSON. Prices are returned as decimal strings to avoid floating-point rounding.
Quick start: first response in three steps
Pull 10 sold iPhone 15 Pro listings. Each tab below is complete: replace the placeholder with the key from your dashboard, then run it as written.
- 01
Copy your API key
Get it from Dashboard → API keys.
- 02
Replace one placeholder
Replace
cs_live_REPLACEWITHYOURKEYwith the full key. - 03
Run and read JSON
A successful response includes sold rows, price intelligence, and remaining-quota headers.
curl --fail-with-body -H "Authorization: Bearer cs_live_REPLACEWITHYOURKEY" "https://api.compsniper.com/v1/scrape?keyword=iphone+15+pro&count=10"8 marketplaces
US, UK, DE, FR, IT, ES, CA, AU
240 items / request
One page per call, up to 240 listings each.
90 days of history
The completed-sales window eBay exposes.
Copy-paste API requests
These are complete one-line cURL commands, so there are no line-continuation characters to break when pasting into a terminal. Replace the API-key placeholder where shown; the health request works without a key.
Check service health
No API key required. Use this before a scheduled import or when diagnosing an outage.
curl --fail-with-body "https://api.compsniper.com/v1/status"Get a price summary
Return the median, realistic p25-p75 range, sample size, and raw comparison without listing rows.
curl --fail-with-body -H "Authorization: Bearer cs_live_REPLACEWITHYOURKEY" "https://api.compsniper.com/v1/summary?keyword=shure+sm7b&count=240"Search with filters
Pull used items from $150 to $350 while excluding accessories in the keyword.
curl --fail-with-body -H "Authorization: Bearer cs_live_REPLACEWITHYOURKEY" "https://api.compsniper.com/v1/scrape?keyword=shure+sm7b+-stand+-cable&itemCondition=used&minPrice=150&maxPrice=350&count=50"Search another marketplace
Use the same response shape across all eight supported eBay marketplaces.
curl --fail-with-body -H "Authorization: Bearer cs_live_REPLACEWITHYOURKEY" "https://api.compsniper.com/v1/scrape?keyword=iphone+15+pro&ebaySite=ebay.co.uk&itemCondition=used&count=50"Return raw eBay matches
Disable AI relevance filtering when you want every upstream row, including accessories and nearby models.
curl --fail-with-body -H "Authorization: Bearer cs_live_REPLACEWITHYOURKEY" "https://api.compsniper.com/v1/scrape?keyword=iphone+15+pro&relevance=false&count=240"Check account usage
Read the plan, monthly quota, remaining requests, reset period, and per-minute limit.
curl --fail-with-body -H "Authorization: Bearer cs_live_REPLACEWITHYOURKEY" "https://api.compsniper.com/v1/account/usage"Want a complete working project?
Follow the Python tutorial or clone the public Python, Node.js, cURL, retry, pagination, and Card Batch examples.
Authentication
Every request is authenticated with a bearer token. Send your key in the Authorization header. CompSniper keys always start with cs_live_ and are created in your dashboard.
The free Basic plan includes 100 requests per month with no credit card. Treat your key like a password: it grants full access to your quota, so keep it server-side and never commit it to a public repository.
curl --fail-with-body -H "Authorization: Bearer cs_live_REPLACEWITHYOURKEY" "https://api.compsniper.com/v1/scrape?keyword=stanley+tumbler"Where do keys come from?
Create a free account to get an instant key, then rotate or revoke it anytime from the dashboard. Requests without a valid key return 401 unauthorized.
Rate limits
Two independent limits apply to every key. The first is a per-minute rate limit (60 requests per minute on standard plans). The second is your monthly request quota, set by your plan. Hitting either one returns 429, but with a different code so you can tell them apart.
Exceeding the per-minute limit returns rate_limited and is temporary: retry after the Retry-After header (in seconds). Exhausting your monthly quota returns quota_exceeded and blocks further requests until your billing cycle resets. Basic accounts associated with the same free-plan identity share one allowance, so creating another account does not create another quota.
429 · rate_limited
Retry later
Wait the number of seconds in Retry-After, add jitter, and stop after five attempts.
429 · quota_exceeded
Do not retry
Stop immediately. Show upgrade_url or wait until reset_at. Retrying only creates more 429 errors.
Rate and usage headers ship with authenticated search responses and limit errors, so you can slow down before receiving a 429:
HTTP/1.1 200 OK
Content-Type: application/json
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 58
X-RateLimit-Reset: 1765843200
X-Usage-Limit: 10000
X-Usage-Remaining: 8412X-RateLimit-Limit / -RemainingRequests allowed and left in the current per-minute window.
X-RateLimit-ResetUnix epoch (seconds) when the per-minute window resets.
X-Usage-Limit / -RemainingRequests allowed and left in your monthly plan quota.
Retry-AfterSeconds to wait before retrying a temporary rate_limited response.
Never blindly retry every 429
Read the response code first. Retry rate_limited only after Retry-After, with a small random jitter and a maximum attempt count. Never retry quota_exceededin a loop. Stop the job and ask the user to upgrade or wait for reset_at.
Copy one of these bounded retry helpers. Both monitor remaining monthly usage, respect the server delay, add jitter to prevent synchronized retries, stop immediately on quota exhaustion, and retry temporary 5xx failures without looping forever.
import random
import time
import requests
API_KEY = "cs_live_REPLACEWITHYOURKEY"
def search_sold(keyword: str):
for attempt in range(5):
response = requests.get(
"https://api.compsniper.com/v1/scrape",
headers={"Authorization": f"Bearer {API_KEY}"},
params={"keyword": keyword, "count": 240},
timeout=60,
)
body = response.json()
if response.ok:
print("Requests remaining:", response.headers.get("X-Usage-Remaining"))
return body
if response.status_code == 429 and body.get("code") == "quota_exceeded":
raise RuntimeError(
f"Monthly quota exhausted. Reset: {body.get('reset_at')}. "
f"Upgrade: {body.get('upgrade_url')}"
) # Do not retry this request.
if response.status_code == 429 and body.get("code") == "rate_limited":
delay = float(
response.headers.get("Retry-After")
or body.get("retry_after")
or min(2 ** attempt, 30)
)
time.sleep(delay + random.uniform(0, 0.5))
continue
if response.status_code in (500, 502, 503):
delay = float(
response.headers.get("Retry-After")
or body.get("retry_after")
or min(2 ** attempt, 30)
)
time.sleep(delay + random.uniform(0, 0.5))
continue
response.raise_for_status()
raise RuntimeError("CompSniper request failed after 5 bounded retries")
data = search_sold("9780399145636")
print(data["totalItems"], "sold listings")Errors
CompSniper uses conventional HTTP status codes. Every error body is JSON with a human-readable error string and a stable machine-readable code. Rate and quota errors add retry_after or reset_at so you know when to try again.
| Status | Code | Meaning | What to do |
|---|---|---|---|
| 400 | invalid_params | A query parameter is missing or malformed. | Check the parameter table below. Most often keyword is missing or count is outside 1-240. |
| 401 | unauthorized | The API key is missing, malformed, or revoked. | Send a valid Authorization: Bearer header. Keys start with cs_live_ and live in your dashboard. |
| 403 | unauthorized | The credential is valid but does not have the scope required by this route. | Reconnect the OAuth client with the requested scope, or use an API key created in your dashboard. |
| 404 | not_found | The requested asynchronous job does not exist, expired, or belongs to another account. | Check the jobId and API key. Card batch results expire after 30 days. |
| 409 | conflict | Another Max Mode or card batch job is already running for this account. | Poll or cancel the active job before submitting another. Reuse an Idempotency-Key when retrying a submission. |
| 429 | rate_limited | You exceeded your per-minute request limit. | Wait for Retry-After, add a small jitter, and retry with a bounded attempt count. Standard plans allow 60 requests per minute. |
| 429 | quota_exceeded | You used every request in your plan. Free accounts associated with the same identity share one allowance. | Do not retry in a loop. Stop the job, then wait for reset_at or send the user to upgrade_url. This limit is independent of the per-minute limit. |
| 500 | server_error | An unexpected error occurred on our side. | Retry with backoff. If it persists, email [email protected]. |
| 502 | upstream_blocked | eBay blocked or refused the upstream fetch. | Transient. Retry with backoff. We rotate infrastructure automatically, so most retries succeed. |
| 503 | server_busy | The scraping service is temporarily at capacity. | Respect Retry-After, reduce parallel requests, and use a bounded retry count. |
When contacting support, include the HTTP status, response code, and the X-Request-ID response header when present. Never send your full API key.
429 · rate_limited
{
"error": "Per-minute rate limit exceeded.",
"code": "rate_limited",
"retry_after": 12
}429 · quota_exceeded
{
"error": "Free plan quota exhausted. Upgrade your CompSniper plan to continue now, or wait for your monthly reset.",
"code": "quota_exceeded",
"plan": "basic",
"quota": 100,
"used": 100,
"remaining": 0,
"reset_at": "2026-09-18T05: 30: 36.000Z",
"upgrade_url": "https://compsniper.com/dashboard/subscription"
}Pagination
Walk through results with the page parameter, starting at 1. Each response reports whether more pages exist via hasNextPage. Keep incrementing page until it is false. Each page you fetch debits one request from your quota.
Do not confuse the two count fields. totalItems is the number of listings on the current page (up to 240), while totalResults is eBay’s own reported match count as a string (for example "14,000+"), and is null when eBay does not report one.
{
"keyword": "iphone 15 pro",
"page": 2,
"totalItems": 240,
"totalResults": "14,000+",
"hasNextPage": true,
"autoSelectedCategory": { "id": "9355", "name": "Cell Phones & Smartphones" },
"items": [ /* ... */ ]
}Keyword sold search
The core endpoint. Pass a keyword and get back completed eBay sales. Every other parameter is optional and maps directly to an eBay search filter, so you can narrow 240 results down to exactly the comps you need.
/v1/scrapeQuery parameters
- keywordstring·required
- Search terms, exactly as you would type them into eBay. Supports the eBay minus-sign exclusion (for example: iphone 15 -case) to drop unwanted results.
- pageinteger·1
- Result page to return. Combine with hasNextPage to walk through the full result set.
- countinteger·240
- Number of listings to return, from 1 to 240. One request returns up to one page regardless of count.
- ebaySiteenum·ebay.com
- Which of the 8 eBay marketplaces to search. One of ebay.com, ebay.co.uk, ebay.de, ebay.fr, ebay.it, ebay.es, ebay.ca, ebay.com.au.
- categoryIdstring·0
- eBay category id (the _sacat value). 0 searches all categories. Browse the full list of 17,000+ ids at /ebay-categories.
- sortOrderenum·endedRecently
- Result ordering. One of endedRecently, timeNewlyListed, pricePlusPostageLowest, pricePlusPostageHighest, distanceNearest.
- minPricenumber·null
- Lower bound on total price (item plus shipping), in the marketplace currency.
- maxPricenumber·null
- Upper bound on total price (item plus shipping), in the marketplace currency.
- itemLocationenum·default
- Seller location filter. One of default, domestic, worldwide.
- itemConditionenum·any
- High-level condition filter. One of any, new, used.
- conditionIdinteger·null
- Numeric eBay condition id (for example 1000 for New, 3000 for Used). Overrides itemCondition when set.
- buyingFormatenum·all
- Listing format filter. One of all, auction, buyItNow, acceptsOffers.
- sellerTypeenum·null
- Filter by private or business sellers. Available on EU marketplaces only.
- includeCompleteListingboolean·true
- When true, restricts to completed listings (LH_Complete=1) and enables the bestOfferAccepted signal on each item.
- soldboolean·true
- When true, returns completed sales. Set false to return currently active listings (swaps the sold fields for the active fields).
- soldAfterstring·null
- Keep only sales on or after this date (YYYY-MM-DD). Applied as a post-parse filter, so scrapedCount appears in the envelope.
- soldBeforestring·null
- Keep only sales on or before this date (YYYY-MM-DD). Applied as a post-parse filter.
- aspectFilterstring·null
- URL-encoded JSON of eBay sidebar facet names (for example brand or storage size) to narrow results the way the eBay left rail does.
Response envelope
The top-level object wraps the result set with pagination metadata and eBay’s auto-selected category. Listings live in the items array.
{
"keyword": "iphone 17 pro max",
"page": 1,
"totalItems": 240,
"totalResults": "14,000+",
"hasNextPage": true,
"autoSelectedCategory": {
"id": "9355",
"name": "Cell Phones & Smartphones"
},
"items": [
{
"itemId": "256123456789",
"url": "https://www.ebay.com/itm/256123456789?nordt=true",
"thumbnailUrl": "https://i.ebayimg.com/images/g/3nkAAeSw/s-l500.webp",
"epid": "20049285656",
"title": "Apple iPhone 17 Pro Max 256GB Black Titanium - Unlocked",
"condition": "Pre-Owned",
"conditionId": 3000,
"buyingFormat": "buyItNow",
"bestOfferAccepted": false,
"bidCount": null,
"categoryId": "9355",
"listingType": "sold",
"endedAt": "2026-03-10",
"soldPrice": "1245.00",
"soldCurrency": "USD",
"shippingPrice": "0.00",
"shippingCurrency": "USD",
"shippingType": "free",
"totalPrice": "899.99",
"sellerUsername": "top-deals-store",
"sellerPositivePercent": 99.8,
"sellerFeedbackScore": 14200,
"itemLocation": "United States",
"scrapedAt": "2026-03-14T21: 00: 00.000Z"
}
// ... 239 more items
]
}keywordThe keyword you searched, echoed back.pageThe page number returned.totalItemsListings on this page (up to 240).totalResultseBay’s reported match count as a string, or null.hasNextPageWhether another page is available.autoSelectedCategoryThe category eBay auto-picked, or null.scrapedCountPresent only when date-filtering with soldAfter or soldBefore.itemsArray of listing objects (schema below).Item schema (26 fields, sold mode)
Each object in items when sold=true (the default). Prices are decimal strings. On Best Offer sales, soldPrice is an upper bound because eBay never discloses the accepted offer amount.
- itemIdstring
- eBay item id.
- urlstring
- Canonical listing URL (appends ?nordt=true).
- thumbnailUrlstring | null
- Standard resolution image (s-l500).
- fullResThumbnailUrlstring | null
- Full resolution image (s-l1600).
- epidstring | null
- eBay product identifier, when the listing is catalog-matched.
- titlestring | null
- Listing title as shown on eBay.
- conditionstring | null
- Localized condition label (for example Pre-Owned).
- conditionIdnumber | null
- Numeric eBay condition id.
- sellerTypestring | null
- private or business (EU marketplaces).
- buyingFormatstring | null
- auction, buyItNow, auctionWithBIN, or null.
- bestOfferAcceptedboolean
- True when the sale closed via an accepted Best Offer.
- bidCountnumber | null
- Number of bids (auctions only).
- categoryIdstring | null
- eBay category id the listing sold under.
- listingTypestring
- sold or active.
- shippingPricestring | null
- Shipping cost as a decimal string.
- shippingCurrencystring | null
- ISO 4217 currency for shipping.
- shippingTypestring | null
- free, paid, pickup, or unknown.
- totalPricestring | null
- Sold price plus shipping, as a decimal string.
- sellerUsernamestring | null
- Seller handle.
- sellerPositivePercentnumber | null
- Seller positive feedback percentage.
- sellerFeedbackScorenumber | null
- Seller total feedback score.
- itemLocationstring | null
- Seller location. Null when domestic to the marketplace.
- scrapedAtstring
- ISO 8601 timestamp of when the data was fetched.
- endedAtstring | null
- Sale date (YYYY-MM-DD).
- soldPricestring | null
- Sale price as a decimal string. On Best Offer sales this is an upper bound.
- soldCurrencystring | null
- ISO 4217 currency for the sale price.
Active mode (sold=false)
Set sold=false to return currently active listings instead. The shared fields stay the same, but the sold fields (endedAt, soldPrice, soldCurrency) are replaced by these:
- currentPricestring | null
- Current asking price (or lower bound of a range).
- currentPriceMaxstring | null
- Upper bound when the listing shows a price range.
- currentCurrencystring | null
- ISO 4217 currency for the current price.
- watcherCountnumber | null
- Number of watchers on the listing.
- unitsSoldnumber | null
- Units already sold on a multi-quantity listing.
- acceptsOffersboolean
- True when the listing accepts Best Offers.
- timeLeftstring | null
- Raw localized time-remaining string from eBay.
Category browse
Browse an entire eBay category without a keyword. Same request shape and same response envelope as /v1/scrape, except categoryId is required and the response keyword is an empty string. Every other filter (minPrice, itemCondition, sortOrder, and the rest) works identically.
Find category ids at /ebay-categories, which lists all 17,000+ of them.
/v1/scrape/categorycurl -H "Authorization: Bearer cs_live_REPLACEWITHYOURKEY" \
"https://api.compsniper.com/v1/scrape/category?categoryId=9355&count=100"Required parameter
categoryId (string) is required. All other parameters from the GET /v1/scrape table are supported, with the exception of keyword.
Batch card pricing
Submit up to 100 sports cards or trading cards in one HTTP request. CompSniper builds precise search keywords from structured card fields, processes the cards as a background job, and preserves your reference value in every result. You can also provide an exact keyword when you already know the query you want.
Each successfully processed card consumes one normal monthly request. Processing is sequential, cached results are reused, quota and upstream failures stop the unfinished portion safely, and completed results remain available for 30 days.
/v1/cards/batchcurl -X POST "https://api.compsniper.com/v1/cards/batch" \
-H "Authorization: Bearer cs_live_REPLACEWITHYOURKEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: inventory-run-2026-08-29" \
-d '{
"cards": [
{
"reference": "inventory-001",
"year": 2023,
"set": "Topps Chrome",
"player": "Victor Wembanyama",
"cardNumber": "1",
"parallel": "Refractor",
"grader": "PSA",
"grade": 10
},
{
"reference": "inventory-002",
"keyword": "1999 pokemon base set charizard 4/102 psa 9"
}
],
"options": {
"count": 60,
"outputMode": "summary",
"relevance": true
}
}'Card fields
Use subject for any card, or the convenient aliases player and character. Add year, set, cardNumber, parallel, grader, and grade to narrow the generated query. An explicit keyword overrides those generated fields.
Poll or cancel
curl -H "Authorization: Bearer cs_live_REPLACEWITHYOURKEY" \
"https://api.compsniper.com/v1/cards/batch/JOB_ID"GET /v1/cards/batch/:jobIdreturns progress plus every completed card result.DELETE /v1/cards/batch/:jobIdsafely stops the unfinished portion.outputMode: summarysupports 100 cards;compactalso returns sold rows and is capped at 25 cards with 60 rows each.- Each compact sold row includes
bestOfferAccepted:truewhen eBay marked an accepted Best Offer, otherwisenull. Because eBay does not disclose the accepted amount, exclude rows where the value istruewhen exact sale price matters. - Use
Idempotency-Keywhen retrying submissions so network retries cannot create duplicate jobs.
MCP connector for Claude, ChatGPT, and more
Query eBay sold prices straight from your AI tools. The CompSniper MCP server plugs into Claude, ChatGPT, Codex, Cursor, Claude Code, and other MCP clients, so you can just ask “what does a used iPhone 14 Pro sell for?” and get the median, the realistic range, and the comps behind it. Hosted clients connect through secure CompSniper sign-in, while local clients can continue using an API key.
Tools
search_sold_comps- sold listings for a keyword plus a price summary.get_price_summary- just the priced answer (median, range, average, sample size).- Batch and comparison tools - price product lists or compare marketplaces.
- Usage and status tools - inspect quota, rate limits, and service health.
- Max Mode tools - start, poll, cancel, and export deep searches.
Search tools support the 8 marketplaces, condition, and price filters, and eBay search operators like -case.
Add to your client
For hosted clients, add the URL below and sign in to CompSniper when prompted. Your password and API keys are never shared with the AI client.
https://mcp.compsniper.com/mcp
For local Claude Desktop, Claude Code, or Cursor setup, use the npm configuration:
{
"mcpServers": {
"compsniper": {
"command": "npx",
"args": ["-y", "compsniper"],
"env": { "COMPSNIPER_API_KEY": "cs_live_REPLACEWITHYOURKEY" }
}
}
}Example prompts
“What does a used Shure SM7B sell for on eBay? Remove irrelevant accessories.”
A cleaned median, realistic range, raw comparison, sample size, and matching sold listings.
“Compare used iPhone 15 Pro sold prices in the US, UK, and Germany.”
A marketplace-by-marketplace comparison with median, range, sample size, and currency.
“Price these five products and tell me which has the highest resale value.”
A bounded batch search with independent results, failures, and quota usage for each product.
Max Mode & RapidAPI
Max Mode (async sweeps)
For large pulls, Max Mode runs server-side pagination as a background job so you do not have to loop through pages yourself. Submit once, poll for progress, then read the items inline or download a CSV. Each scraped page debits one request from your quota. One job runs at a time per account (a second submit returns 409 with the active job). Pass maxPages (up to 100) to cap it.
POST /v1/scrape/max- enqueue, returnsjobIdGET /v1/scrape/max/results/:jobId- poll (5s); terminal: done / maxPages_reached / cancelled / failedDELETE /v1/scrape/max/:jobId- cancelGET /v1/scrape/max/:jobId/download.csv?token=- signed CSV
RapidAPI channel
Prefer to bill through RapidAPI? The same engine is available there with an identical response shape. Instead of the bearer token, authenticate with X-RapidAPI-Key and X-RapidAPI-Host. Everything else in this reference applies unchanged.
Machine-readable spec
A full OpenAPI document is available at https://api.compsniper.com/openapi.json for generating typed clients.