CompSniper Docs

Complete API reference

Full CompSniper endpoint, parameter, sold-item, active-item, response, and error reference.

Searchable endpoint index

MethodEndpointPurpose
GET/v1/scrapeKeyword sold search or active listing search
GET/v1/scrape/categoryBrowse sold listings by eBay category
GET/v1/poshmark/soldSearch public Poshmark sold listings
GET/v1/mercariSearch public Mercari sold or active listings
GET/v1/summaryCleaned price intelligence summary
GET/v1/account/usagePlan, quota, and remaining usage
POST/v1/shipping/ratesGet US shipping-rate estimates from route and package details
GET/v1/shipping/usageGet the independent Shipping Rates plan and quota
POST/v1/bulk-searchStream up to 20 independent keyword searches
GET/v1/bulk-search/:runId/download.csvExport one keyword or the combined bulk run
POST/v1/cards/batchStart an asynchronous card-pricing batch
GET/v1/cards/batch/:jobIdPoll a card-pricing batch
DELETE/v1/cards/batch/:jobIdCancel unfinished card work
POST/v1/scrape/maxStart Max Mode
GET/v1/scrape/max/results/:jobIdPoll Max Mode results
DELETE/v1/scrape/max/:jobIdCancel Max Mode
GET/v1/scrape/max/:jobId/download.csvDownload a signed Max Mode CSV

Keyword search parameters

keyword, page, count, ebaySite, categoryId, sortOrder, minPrice, maxPrice, itemLocation, itemCondition, conditionId, buyingFormat, sellerType, includeCompleteListing, sold, soldAfter, soldBefore, aspectFilter, exactMatch, relevance, and bestOfferPrice (true adds the real accepted price on Best Offer sales, see Accepted Best Offer prices).

Marketplace selection

Set ebaySite explicitly when you need sold listings from a local marketplace. If it is omitted, the API searches ebay.com. CompSniper does not guess location for API traffic because server locations, VPNs, and hosted applications may not match the customer being served.

MarketebaySite value
United Statesebay.com
United Kingdomebay.co.uk
Germanyebay.de
Franceebay.fr
Italyebay.it
Spainebay.es
Canadaebay.ca
Australiaebay.com.au

eBay's own marketplace IDs are accepted as aliases, so EBAY_US, EBAY_GB (or EBAY_UK), EBAY_DE, EBAY_FR, EBAY_IT, EBAY_ES, EBAY_CA and EBAY_AU work, as do country codes such as US or UK.

Only the marketplace changes. Authentication, quota usage, response fields, and parsing remain the same.

Poshmark and Mercari use dedicated response schemas because their native fields differ from eBay. See Poshmark and Mercari APIs for filters, response fields, pagination, examples, and limitations.

Request billing

After a full-price plan upgrade, any unused monthly requests become temporary carryover and are used first. Successful searches then use the new monthly allowance and automatically fall back to non-expiring purchased credits. Send the optional X-Credit-Source: credits header to use credits first; when they run out, the request falls back to your monthly allowance. Responses expose X-Credit-Source, X-Rollover-Balance, and X-Credit-Balance; quota errors include rollover_balance, credit_balance, and purchase_credits_url. Failed searches release the reservation and are not charged.

Shipping-rate quotes use a separate subscription and quota. They never use sold-comps monthly requests, carryover, or purchased credits. See Shipping Rates API for the POST body, response fields, idempotency behavior, and quote-specific errors.

Performance timing

Successful API responses can include a standard Server-Timing header. When present, it separates queue wait, account lease, pacing, eBay fetch, parsing, baseline cleanup, relevance processing, and total engine time. The response body and result fields are unchanged.

exactMatch parser option

exactMatch is an optional boolean with a default of false. When eBay does not find enough listings that contain every word of your keyword, it adds a Results matching fewer words section. exactMatch=true removes that section and everything after it, before any other cleanup.

Use exactMatch=true whenLeave it off (the default) when
Your keyword is short and distinctive and you apply your own matching with relevance=falseYour keyword is long or very specific: full card numbers such as 303/466, full set names, model numbers
You never want near-match listings, even if that means fewer resultsYou want every sale of the item. The default cleanup already removes other cards, models and accessories

Long, precise searches often have no exact matches on eBay, because sellers rarely write the full card number and set name the way a database does. With exactMatch=true those searches can return an empty items array even when the item sold many times. For example, Blind Obedience 303/466 Ravnica Remastered MTG returned 0 sales with exactMatch=true and 13 sales of that exact card and set without it. If many of your searches come back empty, remove exactMatch first. See also narrow searches and graded cards.

When the section is removed, hasNextPage becomes false because eBay has no more exact matches. The default and explicit exactMatch=false keep the existing parser behavior: eBay's full results, with the standard cleanup deciding what matches.

Facets, seller type, and condition IDs

aspectFilter accepts JSON keyed by the human-readable facets shown in the selected eBay marketplace, for example {"Model":"Apple iPhone 15 Pro","Storage Capacity":["256 GB","512 GB"]}. A value can be a string, pipe-delimited string, or string array. CompSniper validates and canonicalizes the object before encoding the exact eBay facet parameters. Unknown facets are passed through and may be ignored by eBay.

sellerType=private and sellerType=business filter labeled rows on ebay.de, ebay.fr, ebay.it, and ebay.es. The four other marketplaces do not expose this classification, so the filter is ignored there. On EU sites, rows without a recognizable seller label are excluded when the filter is active.

Returned rows preserve eBay's localized condition text and add a best-effort numeric conditionId for known labels such as New, Open box, Used, localized refurbished tiers, and For parts. Unknown or category-specific labels remain null; callers should use condition as the authoritative value.

Baseline listing hygiene

All keyword and category responses remove malformed non-listing rows and duplicate item IDs before optional relevance cleanup. A sold item must have a real item ID, a matching /itm/ URL, a positive sold price, and a sold date. An active item must have the same ID and URL checks plus a positive current price.

relevance=false disables query-aware pricing checks and semantic cleanup. Baseline listing hygiene still applies. The count parameter is therefore a ceiling, and valid responses can contain fewer rows after baseline validation, date filters, or relevance cleanup.

With relevance enabled, deterministic title checks first reject explicit pricing mismatches such as unrequested lots, multipacks, bundles, selection listings, modified or non-original items, prototypes, damaged or parts-only units, standalone accessories, and conflicting years, capacities, sizes, quantities, grading details, or catalog numbers. Explicit query intent takes priority, so a keyword that requests a lot, bundle, damaged unit, prototype, modification, or other special case keeps that type of listing. A single bounded semantic classification stage then handles less explicit product and model mismatches.

These checks do not change API keys, authentication headers, parameter names, quotas, response fields, or the top-level response envelope used by existing integrations.

Narrow searches and graded cards

count sets how many eBay sold results are checked, and cleanup then removes the ones that do not match your search. For broad searches you get close to count results. For narrow searches, such as a specific card and grade (1979 Topps Ozzie Smith PSA 8) or an exact model, eBay mixes in many other grades and models, so only some of the checked results are matches. A request for 20 results can return 2 matching sales.

  • Use count=240, the maximum, so one request checks as many sales as possible.
  • When hasNextPage is true, request page=2 to check the next 240 results.
  • To receive eBay's results exactly as listed, with no filtering, add relevance=false.
  • Leave exactMatch off for long, specific keywords like these. It can remove every result. See exactMatch.
Every PSA 8 sale for one card
curl -H "Authorization: Bearer cs_live_REPLACEWITHYOURKEY" \
  "https://api.compsniper.com/v1/scrape?keyword=1979+topps+ozzie+smith+psa+8&count=240&page=1"

Sold item fields

itemId, url, thumbnailUrl, fullResThumbnailUrl, epid, title, condition, conditionId, sellerType, buyingFormat, bestOfferAccepted, bidCount, categoryId, listingType, shippingPrice, shippingCurrency, shippingType, totalPrice, sellerUsername, sellerPositivePercent, sellerFeedbackScore, productRating, productReviewCount, itemLocation, scrapedAt, endedAt, soldPrice, and soldCurrency.

With bestOfferPrice=true, rows where bestOfferAccepted is true also carry acceptedOfferPrice (the price the buyer actually paid) and acceptedOfferPriceStatus (found, pending or not_available). soldPrice is never changed. See Accepted Best Offer prices.

epid is usually null: eBay rarely shows a catalog product ID on sold results.

productRating and productReviewCount describe the shared eBay catalog product, not the individual seller. Both are null when eBay does not show catalog review data on that result card.

Active listing fields

With sold=false, sold-only fields are replaced by currentPrice, currentPriceMax, currentCurrency, watcherCount, unitsSold, acceptsOffers, and timeLeft.

Active searches omit eBay's completed and sold flags. currentPrice is required after baseline listing validation. The other demand fields are best-effort values from the visible search card and remain null when eBay does not display them. summary is null for active searches because sold-price statistics do not apply to asking prices.

The detailed reference below preserves all examples, tables, response envelopes, MCP instructions, and batch documentation from the original single-page reference.

Introduction

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.

  1. 01

    Copy your API key

    Get it from Dashboard → API keys.

  2. 02

    Replace one placeholder

    Replace cs_live_REPLACEWITHYOURKEY with the full key.

  3. 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.

3 years of sold history

About 90 days on /v1/scrape, up to 3 years on /v1/research/sold.

Introduction

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
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
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
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
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 query-aware pricing checks and semantic cleanup when you want accessories and nearby models too. Baseline validation still removes malformed non-listing rows.

cURL
curl --fail-with-body -H "Authorization: Bearer cs_live_REPLACEWITHYOURKEY" "https://api.compsniper.com/v1/scrape?keyword=iphone+15+pro&relevance=false&count=240"

Keep only eBay's exact section

Opt in to removing every listing after eBay's explicit Results matching fewer words notice before normal relevance cleanup.

cURL
curl --fail-with-body -H "Authorization: Bearer cs_live_REPLACEWITHYOURKEY" "https://api.compsniper.com/v1/scrape?keyword=garmin+8612xsv&exactMatch=true&count=240"

Use eBay facets

Narrow by the same human-readable model and storage facets shown in eBay's sidebar.

cURL
curl --get --fail-with-body -H "Authorization: Bearer cs_live_REPLACEWITHYOURKEY" --data-urlencode "keyword=iphone 15 pro" --data-urlencode 'aspectFilter={"Model":"Apple iPhone 15 Pro","Storage Capacity":"256 GB"}' "https://api.compsniper.com/v1/scrape"

Check account usage

Read the plan, monthly quota, remaining requests, reset period, and per-minute limit.

cURL
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.

Introduction

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.

Authenticated request
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.

Introduction

Rate limits

Two independent limits apply to every key. The first is a per-minute rate limit: 30 requests per minute on Basic, 60 on Starter, Growth, and Scale, and 120 on high-volume plans. Paid subscriptions and purchased-credit accounts also receive priority in the live-search queue during busy periods. 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:

Response headers
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: 8412
X-RateLimit-Limit / -Remaining

Requests allowed and left in the current per-minute window.

X-RateLimit-Reset

Unix epoch (seconds) when the per-minute window resets.

X-Usage-Limit / -Remaining

Requests allowed and left in your monthly plan quota.

Retry-After

Seconds 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")
Introduction

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.

StatusCodeMeaningWhat to do
400invalid_paramsA query parameter is missing or malformed.Check the parameter table below. Most often keyword is missing or count is outside 1-240.
401unauthorizedThe API key is missing, malformed, or revoked.Send a valid Authorization: Bearer header. Keys start with cs_live_ and live in your dashboard.
402payment_requiredThe subscription's last renewal payment failed, so the account is on hold. Nothing is charged while on hold.Do not retry in a loop. Pay the open invoice or update the card on the dashboard Subscription page (the billingUrl in the response). Access resumes automatically as soon as the payment succeeds.
403unauthorizedThe 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.
404not_foundThe 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.
409conflictAnother 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.
429rate_limitedYou exceeded your per-minute request limit.Wait for Retry-After, add a small jitter, and retry with a bounded attempt count. Basic allows 30 requests per minute, paid standard plans allow 60, and high-volume plans allow 120.
429quota_exceededYou 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.
500server_errorAn unexpected error occurred on our side.Retry with backoff. If it persists, email [email protected].
502upstream_blockedeBay blocked or refused the upstream fetch.Transient. Retry with backoff. We rotate infrastructure automatically, so most retries succeed.
503server_busyThe 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 · JSON
{
  "error": "Per-minute rate limit exceeded.",
  "code": "rate_limited",
  "retry_after": 12
}

429 · quota_exceeded

Error · JSON
{
  "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"
}
Introduction

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 after validation and filtering (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.

Page 2 · JSON
{
  "keyword": "iphone 15 pro",
  "page": 2,
  "totalItems": 240,
  "totalResults": "14,000+",
  "hasNextPage": true,
  "autoSelectedCategory": { "id": "9355", "name": "Cell Phones & Smartphones" },
  "items": [ /* ... */ ]
}
eBay

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.

GET/v1/scrape

Query 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
Maximum listings to return, from 1 to 240. It is a ceiling, so fewer valid rows may remain after baseline validation, date filters, or relevance cleanup.
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 to private or business sellers on ebay.de, ebay.fr, ebay.it, and ebay.es. Ignored on the other marketplaces; unlabeled EU rows are excluded while filtering.
includeCompleteListingboolean·true
Sold-search compatibility option. Completed-sale searches always use eBay's completed flag. Ignored when sold=false.
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
JSON of human-readable eBay sidebar facet names. Values may be strings, pipe-delimited strings, or string arrays. Unknown facets may be ignored by eBay.
exactMatchboolean·false
When true, removes every listing after eBay's explicit Results matching fewer words boundary before other relevance checks. When the boundary is absent, results are unchanged.
relevanceboolean·true
Runs query-aware deterministic pricing checks and one bounded semantic cleanup stage. Set false for broad valid eBay matches after baseline hygiene.

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.

Baseline validation removes malformed navigation or recommendation rows and duplicate item IDs in every mode. Sold rows require a real item ID, matching item URL, positive sold price, and sold date. With relevance enabled, query-aware checks remove explicit pricing mismatches before one bounded semantic classification stage. Setting relevance=false disables both cleanup stages but does not bypass baseline validation.

Response · JSON
{
  "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.
totalItemsValid listings on this page after filtering (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
Best-effort numeric mapping of the localized condition label. Null for unknown or category-specific labels.
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.
productRatingnumber | null
eBay catalog star rating from 0 to 5 when shown on the result card.
productReviewCountnumber | null
Number of eBay catalog ratings behind productRating when shown.
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.

Visible demand signals can be null when eBay omits them from the search card. The top-level sold-price summary is null for active searches because asking prices are not completed sales.

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.

GET/v1/scrape/category
Terminal
curl -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

Stream up to 20 unique keyword searches through one authenticated request. CompSniper runs at most three searches concurrently and emits each independent result as soon as it finishes. Every successful keyword consumes one normal request; failed searches preserve quota.

Results can finish out of input order. Use the event index to restore input order, and see the bulk-search guide for the complete cURL request, idempotent replay contract, authenticated combined and per-keyword CSV exports, Node reader, and safe reconnect behavior.

POST/v1/bulk-search
eBay

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, quota and upstream failures stop the unfinished portion safely, and completed results remain available for 30 days.

POST/v1/cards/batch
Submit · cURL
curl -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

Poll · cURL
curl -H "Authorization: Bearer cs_live_REPLACEWITHYOURKEY" \
  "https://api.compsniper.com/v1/cards/batch/JOB_ID"
  • GET /v1/cards/batch/:jobId returns progress plus every completed card result.
  • DELETE /v1/cards/batch/:jobId safely stops the unfinished portion.
  • outputMode: summary supports 100 cards; compact also returns sold rows and is capped at 25 cards with 60 rows each.
  • Each compact sold row includes bestOfferAccepted: true when eBay marked an accepted Best Offer, otherwise null. Set options.bestOfferPrice: true to add acceptedOfferPrice, the real accepted amount, on US-dollar sales; otherwise exclude rows where the value is true when exact sale price matters.
  • Use Idempotency-Key when retrying submissions so network retries cannot create duplicate jobs.
More

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:

mcp config
{
  "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.

More

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, returns jobId
  • GET /v1/scrape/max/results/:jobId - poll (5s); terminal: done / maxPages_reached / cancelled / failed
  • DELETE /v1/scrape/max/:jobId - cancel
  • GET /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.

On this page