eBay Sold Listings Scraper
Pricing
from $0.70 / 1,000 sold listing returneds
eBay Sold Listings Scraper
Sold and completed eBay listings: sale price, currency, shipping, sold date, condition, buying format, bids and location, on nine marketplaces. eBay shows sold results only to a signed-in account, so you paste in your own eBay cookie header. A run that gets nothing charges nothing.
Pricing
from $0.70 / 1,000 sold listing returneds
Rating
0.0
(0)
Developer
Dami's Studio
Maintained by CommunityActor stats
0
Bookmarked
1
Total users
0
Monthly active users
2 days ago
Last modified
Categories
Share
eBay Sold Listings Scraper — sold comps from your own eBay session
Search eBay's sold and completed listings and get them back as rows: item id, title, sale price, currency, shipping cost, sold date, condition, buying format, seller location and image.
This is what you use to price something. What did this actually sell for, how many times, at what spread, in the last N days.
Read this first: sold listings need your eBay session
As of 21 August 2026, eBay shows sold and completed results only to a signed-in eBay account. Any search carrying the sold filter is redirected to
signin.ebay.comand answered with a sign-in page instead of results.So this Actor takes your eBay session. You paste the
Cookieheader from a browser where you are already signed in to eBay, into thesessionCookieinput, and every request the run makes carries it. There is no eBay developer account, no OAuth and no app key involved — it is the session your own browser is already holding. How to copy it.Without a session, the run does not pretend. It stops at the first redirect, writes one diagnostic row with the exact URL eBay sent it to, and finishes failed. No listing rows are charged, because no listing rows are delivered. Same if the session you paste has expired.
This was measured on 21 August 2026, from a clean residential address with a warmed session:
Request Result /sch/i.html?_nkw=nintendo+switchHTTP 200, 2.7 MB, live listings returned the same search + a condition, Buy-It-Now, or auction filter HTTP 200, live listings returned the same search + LH_Sold=1and/orLH_Complete=1redirected to signin.ebay.com/ws/eBayISAPI.dll?SignIn&…&sgfl=srch, sign-in page returnedReproduced on all nine marketplaces this Actor supports (
ebay.com,.ca,.co.uk,.de,.com.au,.fr,.it,.es,.ie), onwww.and onm., and across_sop,_fsrpandrt=ncvariations. Only the sold and completed filters trigger it.It is a sign-in requirement, not an anti-bot block. Please do not buy proxies for this — a different address gets the same sign-in page. Being signed in is the only thing that changes it.
Bring your own eBay session
- Sign in to eBay in your normal browser, on the marketplace you want to search.
- Open the developer tools (F12), go to the Network tab, and reload the page.
- Click the first request in the list, find Request Headers, and copy the whole Cookie value.
- Paste all of it into the
sessionCookieinput.
Copy the whole header, not a few cookies you picked out. eBay does not publish which cookie carries the session and it renames them from time to time, so this Actor never asks for cookies by name and never guesses — it sends back what you gave it, verbatim.
sessionCookie is a secret input: Apify stores it encrypted, it is not shown in the run log, and it
never appears in the dataset. Runs only send it to eBay hostnames — if a redirect ever left eBay,
the jar hands over nothing. The run log records how many cookies were read, never their values.
A few honest notes about using your own account:
- It is your account. Whether you point it at an automated run is your call, under your agreement with eBay. This Actor ships no shared account of its own, and it never will — an eBay account is tied to real money and nobody should be sharing one.
- Sessions expire. eBay logs you out on its own schedule, and also when you sign out, change
your password, or eBay ends the session from another device. When that happens the run fails with
EBAY_SESSION_REJECTEDand charges nothing. Paste a fresh header and run it again. - eBay decides what your account sees. It applies its own limits. If it stops serving results mid-run you get an uncharged diagnostic row, not a half-empty dataset presented as the answer.
- If you would rather your session did not come from a rotating datacenter address, set
proxyConfiguration.proxyUrlsto an address you control.
Output
| Field | Notes |
|---|---|
itemId, title, listingUrl | the listing |
salePrice, currency | the price it sold for, parsed to a number, plus the marketplace currency |
shippingCost, shippingText | shipping parsed to a number; Free delivery becomes 0. shippingText keeps eBay's raw string |
soldDate, soldDateText | ISO timestamp and eBay's own label ("Aug 9, 2026" / "10 Aug 2026") |
condition | "Pre-Owned", "Brand New", "Open Box", "Parts Only", "Very Good - Refurbished" … |
buyingFormat | "Buy It Now", "or Best Offer", "Best offer accepted", "4 bids" |
bids | number of bids, when the listing was an auction |
itemLocation | where the seller shipped from |
imageUrl | the gallery thumbnail URL (the image is not downloaded) |
page, source, scrapedAt | provenance |
A real row, as it lands in the dataset:
{"ok": true,"_sample": false,"itemId": "396512340987","title": "Nintendo Switch OLED Console White Joy-Con — Very Good Condition","listingUrl": "https://www.ebay.com/itm/396512340987","salePrice": 249.99,"currency": "USD","shippingCost": 0,"shippingText": "Free delivery","soldDate": "2026-08-09T00:00:00.000Z","soldDateText": "Aug 9, 2026","condition": "Pre-Owned","buyingFormat": "Buy It Now","bids": null,"itemLocation": "United States","imageUrl": "https://i.ebayimg.com/images/g/AbCdEfGh/s-l500.jpg","page": 1,"source": "ebay.com","scrapedAt": "2026-08-10T12:41:07.882Z"}
Auction rows look the same but carry "buyingFormat": "12 bids" and "bids": 12.
Measured field coverage
Measured on a real 300-row platform run on 2026-08-10: query nintendo switch, 2 pages,
ebay.com. 300/300 rows real, 0 diagnostics, 54 s.
| Field | Filled |
|---|---|
itemId, title, listingUrl, salePrice, currency, soldDate, soldDateText, condition, buyingFormat, itemLocation, imageUrl | 100% |
shippingCost, shippingText | 99.7% |
bids | 19.0% |
bids is only present on auction listings — 19% of that sample. No column was empty on every row.
Median sale price in the sample: $126.75.
Input
query— keywords. Leave it empty and you get one labelled, uncharged sample row.sessionCookie— theCookieheader from a browser signed in to eBay. Required for sold results; see above. Secret, encrypted, never logged.site—ebay.com,.ca,.co.uk,.de,.com.au,.fr,.it,.es,.ie. Sign in on the same marketplace you set here.categoryId— eBay category ID (e.g.625for cameras). Verified to change results.condition—new,open box,refurbished,used,parts. eBay applies it server-side.minPrice/maxPrice— applied by eBay and re-checked on the parsed price.soldFrom/soldTo— date window, applied after parsing.currency— keep only listings in one currency.maxItems(default 120),maxPages(default 3, ~200 listings per page),maxSessions(default 8),requestTimeoutSecs.proxyConfiguration— optional; leave it empty for the tuned default.
Every filter above was run live before being documented. Rows a filter drops are neither returned nor charged.
Nothing here is named cleverly: query, maxItems and proxyConfiguration mean what you would
expect them to mean.
A full example: everything sold in the last month, used or better, between $150 and $400, on the UK site.
{"query": "nintendo switch oled","sessionCookie": "dp1=...; nonsession=...; ebay=...","site": "ebay.co.uk","condition": "used","minPrice": 150,"maxPrice": 400,"soldFrom": "2026-07-15","maxItems": 300,"maxPages": 3}
The minimum viable input is a query and a session:
{ "query": "nintendo switch oled", "sessionCookie": "…" }Reliability
eBay refuses a large share of automated traffic outright, and it does not announce it: an interstitial arrives as HTTP 200 with a small body and zero listings. So this Actor judges the body content, never the status code — a 200 that contains no result cards is treated as a refusal, not as an empty search.
Sessions are warmed before they are used. A request fired straight at a search URL succeeds far less often than one made by a session that first visited the marketplace homepage and kept the cookies it was handed, then asked for the search page with a same-origin referer. Warmed sessions also paginate several pages deep without being challenged, where cold ones are stopped almost immediately. Those two numbers were measured, not assumed, on real platform runs.
Your sessionCookie goes into the jar before the warm-up request, so the very first thing the
run does is already signed in. eBay's own cookies then land on top, the way they would in a browser.
A session that starts being refused is discarded and a fresh one is warmed on a new address — a
burned eBay session stays burned, so retrying on the same one only wastes time. Each page gets up to
maxSessions attempts. Egress is rotating datacenter addresses; no residential bandwidth is used, so
there is no per-gigabyte surprise on the bill.
eBay also changes its result-card markup periodically, which silently turns a working parser into one that returns zero rows. The parser here accepts both the current and the previous card structure, and a page that genuinely yields nothing produces an uncharged diagnostic row rather than a silent empty dataset.
Billing
$0.70 per 1,000 sold listings ($0.0007 each), plus $0.001 each time a run starts. That is a flat rate on every Apify plan — no volume tiers and no plan gates.
Pay per event, event name listing. One event per unique real listing. Duplicates across pages are
charged once. Never charged: the empty-input sample, filtered-out rows, and every diagnostic row —
EBAY_LOGIN_REQUIRED, EBAY_SESSION_REJECTED, EBAY_BLOCKED, NO_LISTINGS, NO_MATCHES. A run
with no session, or with an expired one, costs the $0.001 start fee and nothing more.
What a run actually costs:
| Run | Real listings returned | Total |
|---|---|---|
| Quick price check | 50 | $0.036 |
| Default settings | 120 | $0.085 |
| Full comp pull, 2 pages | 300 | $0.211 |
| Maximum, 10 pages | 2,000 | $1.401 |
Because charging happens per row after the row is pushed, a run that is blocked or matches nothing
costs you the $0.001 start fee and nothing else. Filters are the cheap way to work: minPrice,
condition and soldFrom drop rows before they are charged, so a narrow query is genuinely cheaper
than a broad one you post-process yourself.
What this does not do
- No seller username or feedback score. eBay removed those from the search-result card; this actor will not fabricate them. Use a seller-profile scraper if you need them.
- No per-item detail pages, so no item specifics, description HTML, or full photo set.
- No live/active listings — this is the sold and completed view only.
- No eBay API, no OAuth, no application key — but sold results do need your signed-in session.
- No shared eBay account. You supply your own or the run fails, uncharged.
- No sign-in on your behalf. It does not take an eBay username or password, and it cannot log in, solve a two-factor prompt, or refresh a session that has expired.
- No residential proxy.
- eBay caps a search at roughly 200 listings per page; very deep pagination eventually stops
returning new items and the actor writes an uncharged
NO_LISTINGSrow rather than looping. - Non-US marketplaces publish fewer attributes on the result card, so
conditionanditemLocationare sparser there than the 100% measured onebay.com. - Sold-listing coverage on eBay itself is roughly the last 90 days.
soldFromcannot reach further back than eBay retains.
FAQ
How do I find what something sold for on eBay?
Put the product name in query, set soldFrom to the start of the window you care about, and read
salePrice and soldDate from each row.
How many sold listings per run?
About 200 per page. maxPages up to 20, maxItems up to 2,000. A 300-row run took 54 seconds.
Do I need an eBay API key?
No. Nothing to register, nothing to authorise. You do need to be signed in to eBay in a browser and
to paste that session into sessionCookie, because that is what eBay asks for on sold results.
Why do you need my cookie? Because eBay will not show sold and completed listings to anyone who is not signed in. The cookie header is how a signed-in session is carried. It is stored encrypted as a secret input, sent only to eBay hostnames, and never written to the log or the dataset — the log records the number of cookies read, nothing else.
Why the whole header and not just the login cookie? Because nobody outside eBay knows reliably which one it is, and eBay renames them. Asking for named cookies would look tidier and would break on a day nobody could predict. Pasting the header keeps that from ever being a problem.
My session stopped working after a while.
That is eBay expiring it, which it does on its own schedule and whenever you sign out or change your
password. The run fails with EBAY_SESSION_REJECTED and charges nothing. Copy a fresh Cookie
header and run it again. If you schedule this, expect to refresh it periodically.
Can it log in for me if I give it my password? No, and it will not ask. It takes a session you already have, nothing else.
Can I search a non-US eBay site?
Yes — set site. Prices come back in that marketplace's currency and soldDateText in its date
format; soldDate is normalised to ISO either way. Note that non-US sites publish fewer attributes,
so condition is much sparser there than the 100% measured on ebay.com.
Does it include shipping in the price?
No. salePrice is the item price and shippingCost is separate, so you can add them yourself or
compare item-only prices.
How do I get a median or a price range out of this?
Run the query, export the dataset as CSV or JSON, and aggregate salePrice yourself — the rows are
one listing each, already parsed to numbers, so a median is a one-liner in a spreadsheet or pandas.
Use currency to avoid mixing marketplaces in the same average.
What happens if eBay blocks it?
Each page gets up to maxSessions freshly warmed sessions on new addresses. If all of them are
refused you get one uncharged EBAY_BLOCKED row saying so, and zero listing charges. A sign-in
redirect is a different thing and is reported as one: it stops on the first response instead of
retrying, because every address gets the same sign-in page.
Is the sample row charged?
No. Empty input returns one _sample: true row and zero billable events.
Can I schedule it?
Yes — a daily or weekly schedule on the same query gives you a rolling comp history. Each run is
independent, so deduplicate on itemId if you merge datasets across runs. Watch for
EBAY_SESSION_REJECTED in the scheduled runs: that is your sessionCookie having expired, and it
is the one thing a schedule cannot refresh for you.