One endpoint for real eBay sold prices
Everything you need to integrate CompSniper: authentication, limits, error handling, and the complete 26-field response schema. Every route is a single GET request that returns clean JSON. No OAuth, no XML, no SDK required.
https://api.compsniper.comVersioned under /v1Overview
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.
Quickstart
Pull the last 10 sold iPhone 15 Pro listings. Swap in your key and you are done. Pick your language:
curl -H "Authorization: Bearer cs_YOUR_KEY_HERE" \
"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.
Authentication
Every request is authenticated with a bearer token. Send your key in the Authorization header. CompSniper keys always start with cs_ 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 -H "Authorization: Bearer cs_live_9f2c...a71b" \
"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.
Rate and usage headers ship on every response (including successful ones), so you can meter without waiting for 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. Present on 429 responses.
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_ and live in your dashboard. |
| 429 | rate_limited | You exceeded your per-minute request limit. | Back off and retry after the Retry-After header (seconds). Standard plans allow 60 requests per minute. |
| 429 | quota_exceeded | You used every request in your monthly plan. | Wait for the reset_at date or upgrade your plan. 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 support@compsniper.com. |
| 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 | Concurrency limit reached for your key. | Reduce parallel requests and retry after a short pause. |
429 · rate_limited
{
"error": "Rate limit exceeded. Retry after 12 seconds.",
"code": "rate_limited",
"retry_after": 12
}429 · quota_exceeded
{
"error": "Monthly request quota exceeded. Resets 2026-09-01.",
"code": "quota_exceeded",
"reset_at": "2026-09-01T00: 00: 00.000Z"
}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 15 pro",
"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 15 Pro 256GB Natural Titanium - Unlocked",
"condition": "Pre-Owned",
"conditionId": 3000,
"buyingFormat": "buyItNow",
"bestOfferAccepted": false,
"bidCount": null,
"categoryId": "9355",
"listingType": "sold",
"endedAt": "2026-03-10",
"soldPrice": "899.99",
"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 keyword is omitted. 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_YOUR_KEY_HERE" \
"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.
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.