# eBay Listings Scraper & Price Tracker (`myth84/ebay-search-scraper`) Actor

eBay listings scraper and price tracker: eBay prices from search results on 9 marketplaces — price, strike-through price, condition, buying format, shipping, ships-from country, units sold, rating and image. Track price changes between runs. One flat row per listing, no personal data.

- **URL**: https://apify.com/myth84/ebay-search-scraper.md
- **Developed by:** [Flatrow Data Studio](https://apify.com/myth84) (community)
- **Categories:** E-commerce, Automation, AI
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.69 / 1,000 products

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

### eBay Listings Scraper & Price Tracker – prices and sold counts, no personal data

eBay listings scraper and eBay price tracker: get eBay prices, condition, shipping, units sold and ratings for any keywords on 9 eBay marketplaces, as a clean table or through an API — no login. Use it as an eBay scraper for resale research, sourcing, competitor price monitoring and price alerts.

**No login, no cookies, no personal data:** nothing to sign in to, no cookies to paste, nothing about sellers, buyers or bidders in the output.

- **What you get:** one flat row per eBay listing for any keywords on 9 marketplaces: price and currency, strike-through price and discount, condition, buying format, shipping, ships-from country, units sold, rating, image, sponsored flag and link; a price-monitoring mode that returns only what changed since your previous run. No personal data.
- **For whom:** resellers and sourcing teams, price-tracking and competitive-intelligence tools, market analysts, AI agents through MCP.
- **How much:** from $2.50 per 1,000 listings on the Free plan, lower on paid plans; Apify's free monthly credit of $5 covers about 1,999 listings. A fixed price per listing saved and nothing else (compute and proxy traffic included, see the Pricing tab); `maxResults` caps every run.

**Sample output**: one real row from a "wireless earbuds" search on ebay.com, key columns only (the full row is under *Output example*):

```json
{
  "platformProductId": "157236783307",
  "title": "Bluetooth Wireless Earbuds Headset Headphones Compatible with iPhone and Samsung",
  "currency": "USD",
  "price": 17.99,
  "soldCount": 866,
  "shippingFree": true,
  "shipsFrom": "US",
  "attributes": {
    "condition": "new",
    "conditionText": "Brand New",
    "listingFormat": "buy-it-now",
    "leafCategoryId": "80077"
  },
  "isSponsored": true,
  "searchRank": 1
}
```

Scrape eBay search results for any keywords on **9 marketplaces** (ebay.com, ebay.co.uk, ebay.de, ebay.fr, ebay.it, ebay.es, ebay.nl, ebay.ca, ebay.com.au) and get **one flat row per listing**: price and currency, strike-through or list price and discount, condition (a normalized grade and eBay's label), buying format (Buy It Now, Best Offer, auction with bids and time left), shipping cost or free shipping, delivery estimate, ships-from country, units sold, product rating and number of ratings, main image, sponsored flag and position in the results. Sort by price, filter by condition, buying format, free shipping, price range, category, excluded words or item location, or paste your own eBay search URLs with their filters, and **monitor prices**: in price-monitoring mode a scheduled run returns only the new listings and the price changes since your previous run. This eBay scraper reads the public search pages without logging in, keeps one browsing session per search, removes duplicates and stops exactly at the number of listings you ask for.

**No personal data, by design.** eBay search results name sellers; this scraper does not. Seller pseudonyms, feedback scores, store names and anything about buyers or bidders are never collected: the rows contain listing facts only (the seller kind, business or private, when the marketplace shows it). Every row goes through a privacy filter before it is saved. The dataset is safe to store, share and feed to AI agents.

### What can you do with eBay data?

- **Track prices and discounts** for a product on one or several marketplaces: price tracking, competitive intelligence, deal detection. With `monitorPrices` and `onlyChanges` on, a daily schedule gives you only the price drops, rises and new listings, and you pay only for those rows.
- **Source products and spot winners**: units sold, ratings, ships-from country, condition and buying format tell you what sells, from where and at what price, on ebay.com as on ebay.de.
- **Study a market**: what eBay ranks first for a keyword, how many listings are sponsored, the share of auctions, the price distribution and the shipping costs.
- **Feed AI agents, spreadsheets and BI tools** with a stable, documented schema shared by the other scrapers of this e-commerce studio (AliExpress Scraper uses the same columns), so listings from several marketplaces have the same columns.

### What data does eBay Scraper extract?

Every row has every column: a value eBay does not show on the search card is `null` (or `[]` / `{}` for lists and objects), so CSV and Excel exports keep the same columns from run to run.

| Group | Columns |
|---|---|
| Identity | `schemaVersion`, `platform`, `platformProductId`, `variantId`, `url`, `title`, `brand`, `category`, `categoryPath` |
| Prices | `currency`, `price`, `originalPrice`, `discountPercent`, `inStock`, `stockQuantity` |
| Popularity | `rating`, `ratingMax`, `reviewCount`, `soldCount`, `isBestSeller`, `isChoice`, `listedAt` |
| Seller | `storeType`, `storeName`, `storeId`, `storeUrl`, `storeRating` |
| Shipping | `shippingFree`, `shippingCost`, `deliveryEstimate`, `shipsFrom` |
| Media and attributes | `imageUrl`, `imageCount`, `attributes` |
| Search context | `isSponsored`, `searchQuery`, `searchRank`, `searchPage`, `viewCountryCode`, `scrapedAt` |
| Price monitoring | `changeType`, `previousPrice`, `priceDelta`, `priceDeltaPercent` (`null` unless `monitorPrices` is on, see below) |

Search result cards show a subset of what a listing page holds. For this Actor, `category`, `categoryPath`, `stockQuantity`, `isBestSeller`, `isChoice` and `listedAt` are always empty, `brand` is filled only when the card's subtitle opens with the brand (phones, consoles: "Apple · iPhone 13 · 128 GB" gives `Apple`) and the title repeats it, `inStock` is always `true` (a listing in the results is active) and the seller columns stay empty by design (`storeType` is `business` or `individual` when ebay.de or ebay.fr say so on the card, `unknown` elsewhere; `storeName`, `storeId`, `storeUrl`, `storeRating` are always `null`). `rating` and `reviewCount` are filled on catalogued products only (about one card in four). `variantId` (the variation whose price is shown) and `isSponsored` come from a tracking block of the page that ebay.fr does not carry: both are `null` on French results.

`attributes` holds the facts of the card as flat strings, only the keys the card shows: `condition` (normalized grade: `new`, `open_box`, `used`, `refurbished` or `for_parts`) and `conditionText` (eBay's label as displayed: "Brand New", "Pre-Owned", "Certified - Refurbished", "Neu (Sonstige)", "Neuf"…), both absent when the card shows no condition label, `listingFormat` (`buy-it-now`, `best-offer` or `auction`), `bids` and `timeLeft` (auctions), `priceMax` (upper bound of a price range, `price` being the lower one), `watchers`, `scarcity` ("Last one", "Almost gone"), `multiBuyDiscount`, `importFeesIncluded` (`true` / `false`), `freeReturns`, `priceWithCoupon`, `brandBadge` ("Direct from Anker" gives `Anker`), `authorizedDealerBadge`, `refurbishedProgram` ("eBay Refurbished"), `topRatedBadge`, `dealBadge` ("GREAT PRICE") and `leafCategoryId` (eBay's numeric leaf category).

### How to scrape eBay listings

1. **Enter search keywords**, one per line ("wireless earbuds", "laptop stand"), or paste **eBay search URLs** copied from your browser, with their category and filters (the URL's domain decides the marketplace).
2. **Choose the marketplace**: ebay.com (USD), ebay.co.uk (GBP), ebay.de, ebay.fr, ebay.it, ebay.es, ebay.nl (EUR), ebay.ca (CAD) or ebay.com.au (AUD). Prices, currency, shipping lines and the language of the cards are those of the site: prices come in the marketplace currency, because the proxy exits in the marketplace's country.
3. **Set the maximum number of listings per search**, the sort order (Best Match or price + shipping lowest first) and, if needed, the condition, buying format, free-shipping, price-range, category, excluded-words and item-location filters.
4. **Run** and open the **Output** tab: the *Overview*, *Prices*, *Shipping* and *Ranking* views show the main columns; export everything as JSON, CSV, Excel or through the API.
5. **To follow prices**, schedule the run and turn on `monitorPrices` with `onlyChanges`: every run after the first returns only what changed (see *Price monitoring* below).

### Input

Give at least one keyword in `searchQueries` or one URL in `startUrls` (with nothing at all, "wireless earbuds" is searched). Field names and defaults are those of the input form.

The input of our validation run: the first 60 listings for "wireless earbuds" on ebay.com in eBay's Best Match order, read with the large page size (every other field keeps its default):

```json
{
  "searchQueries": [
    "wireless earbuds"
  ],
  "marketplace": "US",
  "sortBy": "best-match",
  "resultsPerPage": "240",
  "maxResults": 60
}
```

Every field, with its type and default:

| Field | Type | Default | Description |
|---|---|---|---|
| `searchQueries` | string\[] | `["wireless earbuds"]` | Search keywords, one search per entry, as typed in the eBay search box. |
| `startUrls` | string\[] | `[]` | eBay search result URLs copied from your browser (`/sch/i.html?_nkw=...`, any eBay domain); their category and filter parameters are kept, the URL's domain decides the marketplace. Sorting and filters below are applied on top only when set. |
| `marketplace` | string | `"US"` | eBay site: `US`, `UK`, `DE`, `FR` (validated), `IT`, `ES`, `NL`, `CA`, `AU` (to confirm at the first runs). Decides the domain, the currency and the language of the results. |
| `maxResults` | integer | `100` | Stop each search after this many unique listings (1 to 1,000). You are never charged for more. |
| `sortBy` | string | `"best-match"` | `best-match` (eBay default) or `price-asc` (price + shipping, lowest first), both validated; `price-desc`, `newly-listed` and `ending-soonest` use eBay's URL parameters and are not verified yet. |
| `condition` | string | `"any"` | eBay's condition filter: `any`, `new` (validated) or `used` (not verified yet). Each card's condition is in `attributes.condition` (normalized grade) and `attributes.conditionText` (as displayed). |
| `buyingFormat` | string | `"all"` | eBay's buying-format filter: `all`, `buy-it-now` (validated) or `auction` (not verified yet). The format of each card is always in `attributes.listingFormat`. |
| `freeShippingOnly` | boolean | `false` | eBay's free-shipping filter (not verified yet). `shippingFree` tells what each card shows. |
| `priceMin` | number | — | Minimum price in the marketplace currency (eBay's filter, not verified yet). |
| `priceMax` | number | — | Maximum price in the marketplace currency (eBay's filter, not verified yet). |
| `categoryId` | integer | — | Search one eBay category only: its numeric id, e.g. `112529` (Headphones on ebay.com), the number after `_sacat=` or in `/b/<name>/<id>` of an eBay URL; `attributes.leafCategoryId` gives each row's leaf category. eBay's filter, not verified yet. A pasted URL keeps its own category unless you set one. |
| `excludeWords` | string\[] | `[]` | Words or phrases the listings must not contain, one per entry (`case`, `screen protector`): added to the search words with eBay's minus sign (`-case -"screen protector"`), on pasted URLs too. eBay's search syntax, not verified yet. |
| `itemLocation` | string | `"any"` | Where the items are located: `any` (eBay default, no filter), `domestic` (the marketplace's country), `continent` (its continent or region) or `worldwide`. eBay's filter, not verified yet; `shipsFrom` tells where each listing ships from. A pasted URL keeps its own location filter unless you set one. |
| `includeSponsored` | boolean | `true` | Keep the sponsored listings mixed into the results (flagged in `isSponsored`) or skip them. On ebay.fr every listing is kept (no flag). |
| `resultsPerPage` | string | `"240"` | Listings requested per page: `240` (fewer requests, faster run), `120`, `60` or `default` (eBay's mobile page of 32). A search whose first page with this setting is unusable twice falls back to `default` on its own. |
| `maxPages` | integer | `50` | Safety cap on result pages read per search. |
| `monitorPrices` | boolean | `false` | Compare every listing with the previous run of the same search (by listing id and marketplace) and fill `changeType`, `previousPrice`, `priceDelta`, `priceDeltaPercent`. `maxResults` then counts the listings compared per search. |
| `onlyChanges` | boolean | `false` | With `monitorPrices`: return and charge only the new listings and the price changes; a run without any change succeeds with an empty dataset. Off: every compared listing with its `changeType`. |
| `monitorStoreName` | string | `"ebay-price-monitor"` | Key-value store of your account that keeps the previous prices (one record per search). One name per schedule when two schedules run the same search with different settings; letters, digits and dashes. |
| `proxyConfiguration` | object | `{"useApifyProxy":true,"apifyProxyGroups":["RESIDENTIAL"]}` | The default proxy configuration (Apify proxies, `RESIDENTIAL` group) is the only one validated for eBay and its traffic is included in the price: keep it and leave the proxy country empty, the proxy then exits in the marketplace's country (a country you set is kept as is). Your own proxy URLs of the same kind are accepted. |
| `maxRequests` | integer | `500` | Safety cap on result pages fetched in one run, all searches included. |
| `delayMs` | integer | `1500` | Pause between two page requests of the same search, in milliseconds. |
| `debugStoreHtml` | boolean | `false` | Debugging only: store every fetched page in the key-value store. |
| `debugSendAccept` | boolean | `false` | Debugging only: send a browser-like `Accept` header (the validated requests sent only `Accept-Language`). |

### Output example

One real row, produced by the Actor from a "wireless earbuds" search on ebay.com (the first, sponsored card of the results page). All columns are present, in this order, for every listing:

```json
{
  "schemaVersion": "ecommerce-product/1.1",
  "platform": "ebay",
  "platformProductId": "157236783307",
  "variantId": null,
  "url": "https://www.ebay.com/itm/157236783307",
  "title": "Bluetooth Wireless Earbuds Headset Headphones Compatible with iPhone and Samsung",
  "brand": null,
  "category": null,
  "categoryPath": [],
  "currency": "USD",
  "price": 17.99,
  "originalPrice": null,
  "discountPercent": null,
  "inStock": true,
  "stockQuantity": null,
  "rating": null,
  "ratingMax": 5,
  "reviewCount": null,
  "soldCount": 866,
  "storeType": "unknown",
  "storeName": null,
  "storeId": null,
  "storeUrl": null,
  "storeRating": null,
  "shippingFree": true,
  "shippingCost": null,
  "deliveryEstimate": null,
  "shipsFrom": "US",
  "imageUrl": "https://i.ebayimg.com/images/g/0O8AAeSwoYFomlt2/s-l500.jpg",
  "imageCount": 1,
  "attributes": {
    "condition": "new",
    "conditionText": "Brand New",
    "listingFormat": "buy-it-now",
    "leafCategoryId": "80077"
  },
  "isSponsored": true,
  "isBestSeller": null,
  "isChoice": null,
  "listedAt": null,
  "searchQuery": "wireless earbuds",
  "searchRank": 1,
  "searchPage": 1,
  "viewCountryCode": "US",
  "scrapedAt": "2026-10-20T00:00:00.000Z",
  "changeType": null,
  "previousPrice": null,
  "priceDelta": null,
  "priceDeltaPercent": null
}
```

`price` is the price displayed on the card (Buy It Now price, current bid of an auction, or the lower bound of a range whose upper bound is `attributes.priceMax`), shipping not included; `originalPrice` is the strike-through or list price ("UVP" on ebay.de) when shown, `discountPercent` the percentage off computed from it. `soldCount` is filled whenever the card shows a sales figure ("1,498 sold", "253 verkauft", "4 vendus"), as a lower bound when eBay rounds ("1,000+ sold" gives 1000), and `null` on cards without one (single items often have none). The last four columns are filled in price-monitoring runs only.

The **Output** tab offers five views of the same rows: **Overview** (title, price, currency, original price, discount, free shipping, shipping cost, units sold, rating, ships from, sponsored, link, image), **Prices** (listing and variant ids, price, original price, discount, currency, attributes with condition and format, seller kind, link), **Shipping** (free shipping, shipping cost, delivery estimate, ships from, marketplace country), **Ranking** (query, page, rank, sponsored flag, units sold, rating, ratings, marketplace country) and **Changes** (price-monitoring runs: change type, price, previous price, difference, currency, condition and format, units sold, query, marketplace, link).

### Price monitoring

Turn on `monitorPrices` and `onlyChanges`, then **schedule the run** (daily is typical). The scraper keeps the last displayed price of every listing it compared in a key-value store of your account (`monitorStoreName`, `ebay-price-monitor` by default), with one record per search (same keywords or URL, marketplace, sort order, condition, buying format, free-shipping and price filters); a listing is identified by its item id and its marketplace. The first run of a search is the reference: every listing comes back with `changeType` = `"new"` (and `RUN_STATS.monitorBaseline` is true). From the second run on, with `onlyChanges` the dataset holds **only the new listings and the listings whose price changed**, with the previous price and the difference, and **only those rows are charged**; a run where nothing changed succeeds with an empty dataset. Without `onlyChanges`, every compared listing is returned with its `changeType` (`new`, `price_changed` or `unchanged`). In both cases `maxResults` is the number of listings compared per search.

A scheduled input that watches the 100 first "wireless earbuds" results of ebay.com in Best Match order:

```json
{
  "searchQueries": ["wireless earbuds"],
  "marketplace": "US",
  "maxResults": 100,
  "monitorPrices": true,
  "onlyChanges": true,
  "monitorStoreName": "earbuds-daily"
}
```

A `price_changed` row, as the Actor produces it from the row of the output example when the previous run had recorded 19.99 USD (the other columns are the same as above):

```json
{
  "platformProductId": "157236783307",
  "title": "Bluetooth Wireless Earbuds Headset Headphones Compatible with iPhone and Samsung",
  "currency": "USD",
  "price": 17.99,
  "changeType": "price_changed",
  "previousPrice": 19.99,
  "priceDelta": -2,
  "priceDeltaPercent": -10
}
```

What to know before relying on it:

- **The record is written only after a search that ended cleanly.** A blocked, failed or empty search, a run stopped by its budget or an interrupted run never overwrites the reference, so a bad day cannot erase your prices; the log says when a record was kept unchanged (`RUN_STATS.monitor` counts the records written and skipped).
- **Prices are compared as displayed, in the same currency.** The Buy It Now price, the current bid of an auction or the lower bound of a price range is what gets compared, shipping excluded: an auction's bids therefore show up as price changes (filter on `attributes.listingFormat` if you track fixed prices). Every marketplace has one currency; a listing seen in another currency is reported `unchanged` and the new currency becomes the reference.
- **Rankings move.** eBay reshuffles results between sessions, so the set of compared listings (the first `maxResults` of each search) drifts a little from run to run; a listing that drops out of the results and comes back later is not reported as new (its last price is kept for 90 days). Sort by price or use a narrow search to get a stable set.
- **One store per schedule when searches collide.** Runs sharing a `monitorStoreName` share the records: two schedules with the same keywords, marketplace, sort and filters would overwrite each other's reference, so give them different store names. Changing the keywords, marketplace, sort or filters of a search starts a new reference for it. Delete the key-value store in Apify Console to start over.

### How much does it cost to scrape eBay?

This Actor uses **pay-per-event pricing: you pay a fixed price per listing** saved to your dataset, and nothing else. On the Free plan, listings cost $2.50 per 1,000, so Apify's free monthly credit of $5 covers about 1,999 listings; paid plans pay less per listing, and the price of every plan tier is on the **Pricing tab** of this page.

| What you pay for | How it is counted | Price |
|---|---|---|
| `result` event: one listing saved to your dataset | once per unique listing of the run, capped by `maxResults` | Pricing tab, per plan tier |
| Run start | a symbolic amount, once per run | Pricing tab |
| Compute, proxies, storage and data transfer | included: no platform usage is passed on to you | — |

The cost of a run is known before you start: 1,000 listings cost 1,000 times the `result` price of your tier, whatever the number of pages fetched. In price-monitoring runs with `onlyChanges`, only the new listings and the price changes are charged. A page takes about 2 to 3 seconds; a run of a few hundred listings takes under a minute with the default page size.

### Limits and behaviour worth knowing

- **Search results only.** The Actor reads search result cards, not listing pages: item specifics, full descriptions, seller details, stock quantities and additional photos are not available (see the column notes above). Listing pages, sold / completed items (eBay shows them only to signed-in users, see the FAQ), category browsing pages, reviews and seller profiles are out of scope.
- **32 listings per page by default on eBay, 240 with the default setting.** eBay's mobile page holds 32 listings (about 7 of them sponsored) plus two "Shop on eBay" placeholder cards that are skipped and counted in `RUN_STATS.extra.decoysSkipped`. With `resultsPerPage` at `240` the page holds about 207 listings; the 240 setting was validated once at build time and is confirmed at the first runs, with an automatic fallback to the default page size when the first page of a search with it is unusable twice (`RUN_STATS.extra.ipgFallbacks`).
- **Rankings move and sponsored cards repeat.** eBay reshuffles results between sessions (25 to 29 listings in common between two first pages of the same search) and repeats 3 to 4 sponsored cards from one page to the next. The scraper keeps one session per search (same proxy session, same cookies), removes duplicates by item id across the whole run, and stops at the last page, when two consecutive pages bring nothing new, or at `maxResults` / `maxPages`. `searchRank` counts every card of the page, sponsored ones included; `RUN_STATS.extra.sponsoredSeen` counts the sponsored cards seen.
- **Sponsored flag.** `isSponsored` comes from a tracking block of the page, not from the "Sponsored" label (which eBay prints on every card in an obfuscated form). ebay.fr pages carry no such block: their rows have `isSponsored` and `variantId` at `null`, and `includeSponsored: false` skips nothing there (`RUN_STATS.extra.pagesWithoutContext` counts those pages).
- **The marketplace changes everything.** The catalogue, the prices, the currency, the shipping lines and the language of the cards are those of the site: rows from different marketplaces are not comparable, and `viewCountryCode` tells which site each row comes from.
- **Prices in the marketplace currency.** eBay may convert prices into the currency of the visitor's country (www.ebay.com was once shown in KRW to a South Korean IP), so the proxy exits in the marketplace's country unless you set a proxy country. A page that still comes in another currency is asked again on a new session, 3 times at most; if it stays in that currency, its rows keep the currency they show and the log says so once per search (`RUN_STATS.extra.currencyRotations`, `RUN_STATS.extra.pagesInOtherCurrency`). Search URLs of several eBay sites in one run exit in the country of the first search.
- **Sorting by price includes shipping.** `price-asc` is eBay's "Price + Shipping: lowest first": the sum of `price` and `shippingCost` never decreases down the page, the price alone can (a range card sorts on its lower bound).
- **Not verified yet.** `price-desc`, `newly-listed`, `ending-soonest`, `condition: used`, `buyingFormat: auction`, `freeShippingOnly`, `priceMin` / `priceMax`, `categoryId`, `excludeWords`, `itemLocation` and the IT, ES, NL, CA and AU marketplaces use eBay's public URL conventions and were not exercised at build time (the start of the run log marks the new filters "to confirm at the first run"): they are marked as such in the input form and confirmed at the first runs. Check `attributes.condition`, `attributes.listingFormat`, `attributes.leafCategoryId`, `shipsFrom`, `shippingFree`, `price` and the titles in the output if you rely on them.
- **Prices are as displayed.** `price` does not include shipping; a price range gives its lower bound in `price` and its upper bound in `attributes.priceMax`; a coupon price is kept in `attributes.priceWithCoupon`; an auction's `price` is the current bid.
- **Blocking.** eBay decides request by request whether to serve a page, and its policy may change. If a page is unusable (403 "Error Page", "Checking your browser" challenge, page without results), the scraper opens a new session and retries once, then gives up on that search and reports it in the log. If a whole run is blocked, keep the default proxy configuration, raise `delayMs` and try again later; persistent blocking is fixed as a priority, open an issue with your run id.
- **No empty successes.** A run that finds no listing ends as failed with an explicit message (keywords misspelt, filters too strict, blocked, a search that eBay keeps for signed-in users, or a proxy configuration error), so that your schedules and integrations notice. The one exception is a price-monitoring run that compared listings and found no change: it succeeds with an empty dataset.

### Integrations and API

Start the Actor from Apify Console, schedule it, or call it from the [Apify API](https://docs.apify.com/api/v2) with any language (the **API** tab shows ready-made code for Node.js, Python and curl). Webhooks notify your systems when a run finishes; the dataset can be pulled as JSON, CSV, Excel, XML or RSS, or pushed to Google Sheets, Make, Zapier, n8n, Slack, Airtable, Google Drive and more through the Apify integrations.

**Support:** for bugs or questions, open an issue in the **Issues** tab of this Actor with your run id and input. Page-layout changes and blocking are fixed as a priority; feature requests (listing pages, other marketplaces of the studio) are welcome.

### Use it from your AI assistant

Add this Actor as a tool to Claude, ChatGPT, Cursor, VS Code or any MCP client with the Apify MCP server URL below (sign in with your Apify account when asked):

`https://mcp.apify.com?tools=myth84/ebay-search-scraper`

Then ask in plain words. Copy this prompt and change the details:

```text
Use myth84/ebay-search-scraper to find 20 brand-new laptop stands on eBay UK, cheapest first, prices in pounds. Input: {"searchQueries": ["laptop stand"], "marketplace": "UK", "condition": "new", "sortBy": "price-asc", "maxResults": 20}. Show the title, price, shipping, condition, units sold and link of each listing.
```

### For AI agents (MCP)

Through the [Apify MCP server](https://docs.apify.com/mcp) this Actor is a tool that answers questions such as *"what do wireless earbuds cost on eBay UK, in pounds, shipping included?"* or *"find brand-new laptop stands with Buy It Now on eBay US under 30 dollars"*.

**Minimal input** (every other field keeps its default; raise `maxResults` only when the question needs more rows):

```json
{
  "searchQueries": [
    "laptop stand"
  ],
  "marketplace": "UK",
  "maxResults": 20
}
```

- **Key output fields**: `platformProductId`, `title`, `url`, `price`, `originalPrice`, `discountPercent`, `currency`, `attributes.condition`, `attributes.listingFormat`, `shippingFree`, `shippingCost`, `shipsFrom`, `soldCount`, `rating`, `isSponsored`, `viewCountryCode`; in price-monitoring runs `changeType`, `previousPrice`, `priceDelta`. Every column is always present, in a fixed order; `null` means eBay did not show the value. The full list is in *What data does eBay Scraper extract?* above.
- **Use it when** you need current eBay search results for keywords on a given marketplace: prices, discounts, condition, buying format, shipping, ships-from country, units sold, ratings, images, sponsored flags. Pass `searchQueries` (or `startUrls`), `marketplace`, optionally `sortBy`, `condition`, `buyingFormat`, `freeShippingOnly`, `priceMin` / `priceMax`, `categoryId`, `excludeWords`, `itemLocation`, and a small `maxResults` (20–60 is enough for an answer and fits in one page). To report price changes between two calls, pass `monitorPrices: true`, `onlyChanges: true` and a stable `monitorStoreName`.
- **Do not use it for** listing pages, item specifics, seller information, sold or completed items (eBay shows them only to signed-in users), reviews or anything behind a login: it only reads public search results and collects no personal data.
- **Cost per 1,000 results**: 1,000 `result` events, $2.50 per 1,000 on the Free plan and less on paid plans (Pricing tab); nothing else is charged (in price-monitoring runs with `onlyChanges`, unchanged prices are free).
- **Typical time**: 2 to 3 seconds per page: 20 listings in a few seconds, a few hundred in under a minute.

### Alternatives and switching

- **Official API.** eBay's official developer APIs need a developer account, application keys and OAuth tokens, with call limits. This Actor reads what any visitor sees in eBay search results (public prices, condition, shipping, units sold, sponsored flags) with no account, no keys and no approval.
- **Your own scraper.** Nothing to build or maintain: sessions, retries, pagination, deduplication and eBay page changes are handled for you. The columns stay the same from run to run, a run that finds nothing fails with an explicit message instead of succeeding empty, and a scheduled test run catches eBay changes early.
- **Another scraper from the Store.** The input names differ from one Actor to the next; the usual ones map to this Actor as follows:

| Usual input | Here | Note |
|---|---|---|
| `search`, `query`, `keyword` | `searchQueries` | Several keywords per run |
| `startUrls` | `startUrls` | eBay search URLs, filters included |
| `maxItems`, `limit` | `maxResults` | Unique listings per search, never charged beyond |
| `site`, `domain`, `country` | `marketplace` | `US`, `UK`, `DE`, `FR`, `IT`, `ES`, `NL`, `CA`, `AU` |
| `sort` | `sortBy` | `best-match`, `price-asc`, `price-desc`, `newly-listed`, `ending-soonest` |
| `condition`, `listingType` | `condition`, `buyingFormat` | `new` or `used`; `buy-it-now` or `auction` |
| `minPrice`, `maxPrice` | `priceMin`, `priceMax` | In the marketplace currency |
| `category` | `categoryId` | eBay's numeric category id |
| `excludeKeywords` | `excludeWords` | One word or phrase per entry |
| `locatedIn` | `itemLocation` | `domestic`, `continent` or `worldwide` |
| `sold`, `soldItems` | — | Not available: eBay shows sold listings only to signed-in users |
| `proxy` | `proxyConfiguration` | Keep the default: proxies are included in the price |

### FAQ

#### Does eBay Scraper collect personal data?

No. Seller pseudonyms, feedback scores, store names, buyers and bidders are never collected, and the parts of the page that show them (related-item carousels) are never read. The rows contain listing facts only: prices, condition, format, shipping, units sold, ratings, images, position in the results, plus the seller kind (business or private) when the marketplace prints it. Every row goes through a privacy filter before it is saved.

#### Is it legal to scrape eBay?

The Actor only reads pages that eBay serves to any visitor without an account, and it stores no personal data. Scraping public data is generally lawful, but eBay's terms of use and robots policy restrict automated access, and you are responsible for how you use the results and for complying with the laws that apply to you (including copyright on listing texts and images). Use the data for analysis, not to republish listings.

#### Do I need proxies, and do I pay for them?

No extra setup: the default proxy configuration is the only one validated for eBay, and its traffic is included in the price per listing: you pay per listing, nothing more. Keep the default and leave the proxy country empty: the proxy then exits in the marketplace's country, so that prices come in the marketplace currency. Your own proxy URLs of the same kind are accepted too.

#### Which marketplaces, currencies and languages are supported?

Nine sites, one per `marketplace` value: ebay.com (USD, English), ebay.co.uk (GBP, English), ebay.de (EUR, German), ebay.fr (EUR, French), ebay.it (EUR, Italian), ebay.es (EUR, Spanish), ebay.nl (EUR, Dutch), ebay.ca (CAD, English) and ebay.com.au (AUD, English). The first four were validated at build time; the others follow the same page layout and are confirmed at the first runs. Titles and attribute texts are in the language of the site.

#### What are sponsored listings, and why is `isSponsored` sometimes null?

eBay mixes about 7 sponsored (promoted) listings into each page of 32 and repeats some of them on the next pages; they are flagged in `isSponsored` from the page's tracking block and can be skipped with `includeSponsored`. ebay.fr pages carry no tracking block, so French rows have `isSponsored` at `null` and every listing is kept.

#### What do the price, condition, units-sold and shipping columns mean?

`price` is the amount displayed on the card, shipping excluded: the Buy It Now price, the current bid of an auction, or the lower bound of a range ("$12.99 to $24.99") whose upper bound is `attributes.priceMax`. `originalPrice` is the strike-through price (or the "UVP" list price on ebay.de) when the card shows one, and `discountPercent` the percentage off computed from it. Coupon prices ("$123.29 with coupon") are kept in `attributes.priceWithCoupon`.

`attributes.condition` is the same on every marketplace: `new`, `open_box` ("Open Box", "New (Other)", "Neu (Sonstige)"), `used` ("Pre-Owned", "Gebraucht", "Occasion"), `refurbished` (every refurbished grade) or `for_parts`; `attributes.conditionText` keeps eBay's wording. On phones and consoles the card's subtitle often lists the brand and model instead ("Apple · iPhone 13 · 128 GB"): the brand then goes to `brand`, and the condition is left out rather than guessed when no condition label is shown. `soldCount` is the "N sold" figure of the card, `null` when the card shows none.

`shippingCost` is the delivery cost eBay shows to a buyer in the marketplace's country: listings sent from abroad can cost far more to ship. `shipsFrom` gives the country each listing ships from; to keep only local listings, set `itemLocation` to `domestic` (eBay's own filter, not verified yet) or paste a search URL with eBay's item location filter into `startUrls` (its filters are kept).

#### Can I filter by category or excluded words, or use my own eBay search URL?

Yes. `categoryId` limits a search to one eBay category (its numeric id, e.g. `112529` for Headphones on ebay.com; `attributes.leafCategoryId` of a row shows the id of its category), `excludeWords` removes the listings that contain a word or phrase ("case", "screen protector") with eBay's minus sign, and `itemLocation` keeps the items located in the marketplace's country, its continent or anywhere; these three filters are eBay's own and are confirmed at the first runs. You can also paste search result URLs (`/sch/i.html?_nkw=...`) from any eBay site into `startUrls`: their category and filter parameters are kept, the URL's domain decides the marketplace, and the filters you set in the input are applied on top. `searchQueries` can stay empty.

#### Can I get sold listings?

No. eBay shows sold and completed listings only to signed-in users, and this Actor never signs in: it reads only what eBay shows to any visitor, with no eBay account and no login cookies. A search URL with eBay's "Sold items" or "Completed items" filter (`LH_Sold=1`, `LH_Complete=1`) is refused at the start of the run with that explanation, and a search that eBay answers with its sign-in page is stopped with the same message. For what is selling now, `soldCount` gives the units sold of each active listing ("1,498 sold"), and price monitoring follows the prices of active listings from run to run.

#### Why do I get fewer listings than eBay announces, or the same listing twice across runs?

eBay announces tens of thousands of results but the usable depth of a search is smaller, its ranking changes between sessions and sponsored cards repeat from page to page: the scraper removes duplicates within a run by item id, which is why a page may yield fewer new listings than it holds, and stops when two pages bring nothing new. Across separate runs, use `platformProductId` to deduplicate.

#### How often should I run it, and how do I get only price changes?

Every run fetches live search results. Prices, auctions and rankings change by the hour, so schedule the Actor as often as your use case needs (daily is typical for price tracking) and compare runs by `platformProductId`, `viewCountryCode` and `currency`, or let the Actor compare them for you: turn on `monitorPrices` and `onlyChanges`, keep the default `monitorStoreName` (or one per schedule) and schedule the run. The first run records every listing of the search and returns it as `new`; each later run returns only the listings that appeared since and the listings whose displayed price changed, with `previousPrice`, `priceDelta` and `priceDeltaPercent`, and charges only those rows. A run without any change succeeds with an empty dataset. Keep the same keywords, marketplace, sort and filters from run to run. Turn `onlyChanges` off to get every listing with its `changeType`.

### More scrapers by Flatrow Data Studio

The e-commerce scrapers of the studio share the same columns, so products from several marketplaces land in one comparable table:

- [AliExpress Products Scraper & Price Tracker](https://apify.com/myth84/aliexpress-search-scraper): AliExpress prices, discounts, units sold and shipping for any keywords and delivery country, with price-drop alerts.
- [Etsy Listings Scraper & Price Tracker](https://apify.com/myth84/etsy-search-scraper): Etsy prices, sale prices, ratings and review counts for any common search term, with price monitoring.

Did this scraper give you the data you needed? A rating on its Store page helps other people find it. For a bug or a missing field, open an issue in the **Issues** tab.

### Legal note

This Actor reads publicly available eBay search pages without logging in and collects no personal data. You are responsible for your use of the data and for complying with eBay's terms and the laws that apply to you.

Not affiliated with, endorsed or sponsored by eBay. eBay is a trademark of eBay Inc.

# Changelog

This Actor's version history is a separate document: https://apify.com/myth84/ebay-search-scraper/changelog.md

# Actor input Schema

## `searchQueries` (type: `array`):

Enter one search per line, as you would type it in the eBay search box, e.g. <code>wireless earbuds</code> or <code>laptop stand</code>. Each search gets its own results, up to the maximum number of listings below. Default <code>wireless earbuds</code>: it only serves runs started without any keyword or URL; with search URLs alone, only the URLs are searched.

## `startUrls` (type: `array`):

Paste one or more eBay search result URLs copied from your browser (<code>https://www.ebay.com/sch/i.html?\_nkw=...</code>, any eBay domain); their category and filter parameters are kept and the URL's domain decides the marketplace. The sorting and filters below are applied on top only when you set them. Default empty: keywords are enough for most searches.

## `marketplace` (type: `string`):

eBay site to search: the domain, the currency and the language of the results. US, UK, Germany and France were validated at build time; the other sites follow the same page layout and will be confirmed at the first runs. Default US: ebay.com in US dollars.

## `maxResults` (type: `integer`):

Stop each search (keywords or URL) after this many unique listings, from 1 to 1,000; you are never charged for more. A result page holds up to 240 listings (see Listings per page). Default 100: the top of the results, usually in a single page; the form is prefilled with 20 for a quick first run.

## `sortBy` (type: `string`):

Order of the results. Best Match (eBay's default) and price + shipping lowest first were validated at build time; price highest first, newly listed and ending soonest use eBay's public URL parameters and are not verified yet. Default best-match: eBay's own order, the one a shopper sees.

## `condition` (type: `string`):

eBay's condition filter: any, new (validated at build time) or used (not verified yet). Each card's condition is in <code>attributes.condition</code> (normalized grade: new, open_box, used, refurbished or for_parts) and <code>attributes.conditionText</code> (eBay's label as displayed); both are absent when the card shows no condition label. Default any: no condition filter.

## `buyingFormat` (type: `string`):

eBay's buying-format filter: all listings, Buy It Now only (validated at build time) or auctions only (not verified yet). The format of each card is always in <code>attributes.listingFormat</code>. Default all: no buying-format filter.

## `freeShippingOnly` (type: `boolean`):

Keep only listings with free shipping (eBay's own filter, not verified yet). The free-shipping flag of each card is always in the <code>shippingFree</code> column. Default off: every listing, whatever its shipping cost.

## `priceMin` (type: `number`):

Keep only listings priced at least this amount, in the marketplace currency, decimals allowed, e.g. <code>10</code> for 10 USD on ebay.com (eBay's own filter <code>\_udlo</code>, not verified yet). Leave empty for no minimum.

## `priceMax` (type: `number`):

Keep only listings priced at most this amount, in the marketplace currency, decimals allowed, e.g. <code>50</code> for 50 USD on ebay.com (eBay's own filter <code>\_udhi</code>, not verified yet); it must be at least Min price. Leave empty for no maximum.

## `categoryId` (type: `integer`):

Search only one eBay category: its numeric id, e.g. <code>112529</code> (Headphones on ebay.com), the number after <code>\_sacat=</code> or in <code>/b/\<name>/\<id></code> in an eBay URL; each row's leaf category is in <code>attributes.leafCategoryId</code>. eBay's own filter (<code>\_sacat</code>), not verified yet. Leave empty for all categories; a pasted URL keeps its own category unless you set one here.

## `excludeWords` (type: `array`):

Words or phrases that the listings must not contain, one per line, e.g. <code>case</code> or <code>screen protector</code>: each one is added to the search words with eBay's minus sign (<code>-case</code>, <code>-"screen protector"</code>), also on pasted URLs. eBay's own search syntax, not verified yet. Default empty: nothing is excluded.

## `itemLocation` (type: `string`):

Where the items are located, eBay's own filter (<code>LH_PrefLoc</code>, not verified yet): anywhere (eBay default, no filter), in the marketplace's country only, in its continent or region, or worldwide. The ships-from country of each listing is always in the <code>shipsFrom</code> column. A pasted URL keeps its own location filter unless you set one here. Default any: no location filter.

## `includeSponsored` (type: `boolean`):

eBay mixes about 7 sponsored listings into each page of 32. Keep them (they are flagged in the isSponsored column) or skip them. On ebay.fr the page carries no sponsored flag: every listing is kept and isSponsored is null. Default on: sponsored listings are real offers shown in eBay's results.

## `resultsPerPage` (type: `string`):

Listings requested per result page: 240, 120, 60 or eBay's default page of 32. Default 240: about 7 times fewer pages than eBay's page of 32, so fewer requests and a faster run; if the first page of a search with this setting is unusable twice, the search falls back to eBay's page size on its own. Choose eBay default to reproduce exactly what the mobile site shows.

## `maxPages` (type: `integer`):

Safety cap on the number of result pages read for one search, from 1 to 200. The scraper also stops at the last page, at maxResults, or when two consecutive pages bring no new listing. Default 50: enough for 1,000 listings even with eBay's page of 32.

## `monitorPrices` (type: `boolean`):

Off (default): every listing is returned and the four monitoring columns stay null. On: every listing is compared with the previous run of the same search, by listing id and marketplace, and maxResults becomes the number of listings compared per search.

## `onlyChanges` (type: `boolean`):

With price monitoring on: return and charge only the new listings and the listings whose price changed since the previous run; a run without any change succeeds with an empty dataset. Default off: every compared listing is returned with its changeType (new, price_changed, unchanged). Turn it on for scheduled runs.

## `monitorStoreName` (type: `string`):

Name of the key-value store, in your account, that keeps the previous prices (one record per search), e.g. <code>earbuds-daily</code>. Runs sharing a name share the records; use one name per schedule when two schedules run the same search with different settings. Letters, digits and dashes only, at most 63 characters; upper case is lowered. Default ebay-price-monitor: one store for all your monitoring runs.

## `proxyConfiguration` (type: `object`):

Apify residential proxies (default, required for eBay). Leave the proxy country empty: the proxy then exits in the marketplace's country, so that prices come in the marketplace currency (eBay converts prices into the currency of the visitor's country). A country set here is kept as is. You can also provide your own residential proxy URLs.

## `maxRequests` (type: `integer`):

Safety cap on the number of result pages fetched in one run, all searches included, from 1 to 5,000. Default 500: a cap against runaway runs, far above what a search needs (1,000 listings take about 5 pages of 240).

## `delayMs` (type: `integer`):

Pause between two page requests of the same search, in milliseconds, from 0 to 30,000. Increase it if you get blocked; 0 disables the pause. Default 1500 (1.5 s): the pace of the validation runs.

## `debugStoreHtml` (type: `boolean`):

Debugging only: save every fetched page in the run's key-value store. Slows the run down and uses storage. Default off: only useful to investigate a parsing problem.

## `debugSendAccept` (type: `boolean`):

Debugging only: send a browser-like Accept header with every request. Off by default because the validated requests sent only Accept-Language; turn it on only to test eBay's reaction.

## Actor input object example

```json
{
  "searchQueries": [
    "wireless earbuds"
  ],
  "startUrls": [],
  "marketplace": "US",
  "maxResults": 20,
  "sortBy": "best-match",
  "condition": "any",
  "buyingFormat": "all",
  "freeShippingOnly": false,
  "excludeWords": [],
  "itemLocation": "any",
  "includeSponsored": true,
  "resultsPerPage": "240",
  "maxPages": 50,
  "monitorPrices": false,
  "onlyChanges": false,
  "monitorStoreName": "ebay-price-monitor",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  },
  "maxRequests": 500,
  "delayMs": 1500,
  "debugStoreHtml": false,
  "debugSendAccept": false
}
```

# Actor output Schema

## `products` (type: `string`):

All extracted listings as dataset items, one flat row per listing. Append ?format=csv or ?format=xlsx for spreadsheets. The Overview view is selected by default; Prices, Shipping, Ranking and Changes (price-monitoring runs) views are also available.

## `runStats` (type: `string`):

Requests made, bytes transferred, blocked pages, listings seen (sponsored and decoy cards counted), listings pushed, pages without sponsored flags, page-size fallbacks, the price-monitoring summary (listings compared, new, price changes, records updated) and the estimated platform cost of the run (residential proxy traffic included).

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "searchQueries": [
        "wireless earbuds"
    ],
    "maxResults": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("myth84/ebay-search-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "searchQueries": ["wireless earbuds"],
    "maxResults": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("myth84/ebay-search-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "searchQueries": [
    "wireless earbuds"
  ],
  "maxResults": 20
}' |
apify call myth84/ebay-search-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,myth84/ebay-search-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/ILilQqqfaVXXxPOBx/builds/sBDggL6fWBbbWHxvA/openapi.json
