# eBay Scraper - Products, Sellers, Prices & Monitoring (`abotapi/ebay-com-scraper`) Actor

Scrape eBay by keyword, category, seller or pasted link across 16 country storefronts. Returns id, title, condition, price, buying format, delivery, returns, seller feedback and more, with optional item specifics and full descriptions. Incremental mode tracks changes.

- **URL**: https://apify.com/abotapi/ebay-com-scraper.md
- **Developed by:** [Abot API](https://apify.com/abotapi) (community)
- **Features:** Limited permissions
- **Stats:** 8 total users, 2 monthly users, 88.2% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.20 / 1,000 listing results

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 Marketplace Scraper: Listings, Prices, Sellers & Item Details

eBay Marketplace Scraper turns eBay into structured listing data across sixteen country storefronts. Browse a category id with eBay's own filters, pull a seller's whole store, or paste category, seller and item links you already have; they can be mixed in one run. Every listing comes back as one flat row with the price, the buying format, delivery and condition, and, when you ask for it, the seller with their feedback score, the full image gallery, the item specifics table and the seller's own description. Export to JSON, CSV or Excel, or pull the results straight into your app through the API.

#### What it reads

Three kinds of input, mixed freely:

- **A category.** Give a `categoryId` (the prefill is 177, PC laptops; an empty search-mode run browses it too). It is read from that category's own page, with the price band, condition, buying format, free delivery, returns accepted, item location and sort order applied by eBay itself. Free returns only is the one filter category pages do not offer; it is skipped with a note in the log.
- **A seller.** `sellerUsernames` and pasted `/str/` or `/usr/` links walk the seller's whole inventory, newest first (the only order in which those pages cover every listing). A seller with no store is read from their profile page.
- **Item links**, in every usual form.

Keyword search is not offered: eBay refuses its search result pages to automated visitors on every storefront. Saved tasks and API calls that still send `queries` or `keywords`, a keyword result link or a `/p/` catalog link get one attempt each; when eBay refuses it, that source returns nothing with the message "Keyword search is currently unavailable on eBay", nothing is charged for it, and a run with nothing else to read fails with that message instead of finishing empty.

Category cards do not show the seller. Turn on **Read item pages** to add the seller, their feedback and the item details to every row (one Item page event per row whose item page was read).

### Why This Scraper?

- **Sixteen marketplaces, each checked live.** United States, United Kingdom, Germany, Australia, Canada, France, Italy, Spain, Ireland, Austria, Switzerland, Netherlands, Poland, Hong Kong, Singapore and Malaysia. Every one was fetched and confirmed to return real, id-bearing listings before it was offered as an option, and prices come back in that storefront's own currency. (The Philippines is not offered: `ebay.ph` redirects to the United States storefront, so listing it separately would be an option that quietly does something else.)
- **Around 45 fields per listing**, including the low and high end of a variant price range, the pre-discount price and computed discount percentage, bid count and time left on auctions, the sold count, the item location, and the seller's positive-feedback percentage and score. Most of that arrives from the result page itself, so it costs nothing extra.
- **Several ways in.** A category id, a whole seller's store, or pasted category, seller and item links. Filters use the marketplace's own native filters (condition, buying format, price band, delivery, returns, item location, sort order) so they narrow the result set at the source rather than throwing rows away after you have paid for them.
- **Only real listings.** Category pages mix catalog product tiles and promotional carousels into the page; only the listing grid is returned.
- **Incremental mode for recurring runs.** Schedule the same search daily and get only what is NEW or UPDATED by default, with the changed field names listed on every updated row; turning on Emit expired also gets you EXPIRED and, on a later run, REAPPEARED. Auction countdowns and walk bookkeeping never count as a change, so a ticking clock or a re-ordered page never bills you for a change that did not happen.
- **Resume a large pull** from a previous run id without re-returning or re-charging for listings you already have.

### Use Cases

- **Resellers and arbitrage sellers:** track prices, discounts and sold counts across marketplaces to spot underpriced inventory worth flipping.
- **Price monitoring tools:** feed a comparison site or a price-tracking bot with live listings converted to each storefront's own currency.
- **Sourcing and catalog research:** pull a whole seller's store with item specifics and full descriptions before deciding what to list elsewhere.
- **Listing and brand monitoring:** watch a category or a seller for new and changed listings on a recurring schedule.
- **Market and demand research:** track sold counts and auction activity in a category over time with Incremental mode.

### Data You Get

> Sample shape, values are illustrative placeholders, not from a live listing.

| Field | Example |
| --- | --- |
| `itemId` | `"000000000001"` |
| `url` | `"https://www.ebay.com/itm/000000000001"` |
| `title` | `"Sample Listing Title"` |
| `condition` | `"Used"` |
| `brand` | `"Sample Brand"` |
| `marketplace` | `"com"` |
| `price` | `100.0` |
| `currency` | `"USD"` |
| `listingType` | `"buy_it_now"` (also `"auction"`, `"best_offer"`) |
| `shippingText` / `freeShipping` | `"Free delivery"` / `true` |
| `itemLocation` | `"United States"` |
| `returnsText` / `freeReturns` | `"Free returns"` / `true` |
| `soldCount` | `42` |
| `sellerName` | `"examplestore"` |
| `sellerFeedbackPercent` / `sellerFeedbackCount` | `98.5` / `1234` |
| `badges` | `["Sample Badge"]` |
| `imageUrl` | `"https://i.ebayimg.com/images/g/000000000000/s-l500.webp"` |
| `attributes` | `["Buy It Now", "Free delivery", "Located in United States", "Free returns", "42 sold"]` |
| `itemSpecifics` | `{"Processor": "Sample Processor", "SSD Capacity": "1 TB"}` (when Read item pages is on) |
| `description` | `"Full seller description text appears here when Read item pages is on."` |
| `detailIncomplete` | `true` (only when an item page, or just its separate description document, could not be read after a retry; see "Reading item pages and the surcharge" below) |
| `changeType` | `"NEW"` (also `"UPDATED"`, `"UNCHANGED"`, `"REAPPEARED"`, `"EXPIRED"`; Incremental mode only) |

Every listing also carries `subtitle` (the seller's own strapline, kept separate from the condition row), `conditionId` (eBay's numeric condition code, populated only when the condition wording is in English), `marketplaceDomain`, `priceMax` and `priceText` for variant price ranges, `originalPrice` and `discountPercent` when the card shows a strikethrough price, `bidCount` and `timeLeft` on auctions, `shippingCost` alongside `shippingText`, `sellerUrl`, `searchQuery`, `sourceUrl` and `pageNumber` for provenance, and `scrapedAt` for the run's own timestamp. Reading item pages additionally fills `images` (the full gallery), `categoryPath`, `mpn`, `model`, `color`, `gtin`, `availability`, `quantityAvailable`, `quantitySold`, `paymentMethods`, `shippingDetails`, `returnPolicy`, `listPrice`, `listedAt`, `updatedAt` and `sellerNotes`; all of these stay `null` unless **Read item pages** is on. Incremental mode adds `changedFields` (the field names that changed on an UPDATED row), `firstSeenAt` and `lastSeenAt`.

### How to Use

1. Pick a **mode**: `search` (by category id, seller username and native filters) or `url` (paste links).
2. Fill in the fields for that mode, add any filters and a sort order, and turn on **Read item pages** if you want the image gallery, item specifics and full description.
3. Set **Max listings** to control run size and cost (`0` for unlimited), then click **Start**.
4. Download the dataset as JSON, CSV or Excel, or read it through the API.

**Browse one category:**

```json
{
  "mode": "search",
  "marketplace": "com",
  "categoryId": "177",
  "maxItems": 50
}
```

**Browse a category with the marketplace's own filters, including a multi-select condition:**

```json
{
  "mode": "search",
  "marketplace": "co.uk",
  "categoryId": "175672",
  "condition": ["new", "used"],
  "buyingFormat": "buy_it_now",
  "minPrice": 10,
  "maxPrice": 60,
  "freeShippingOnly": true,
  "sortBy": "newly_listed",
  "minSellerFeedbackPercent": 98,
  "maxItems": 100
}
```

**Pull a whole seller store with item pages:**

```json
{
  "mode": "search",
  "marketplace": "de",
  "sellerUsernames": ["examplestore"],
  "fetchDetails": true,
  "maxItems": 200
}
```

**Paste links, mixing marketplaces and link kinds:**

```json
{
  "mode": "url",
  "urls": [
    "https://www.ebay.com/str/examplestore",
    "https://www.ebay.co.uk/usr/examplestore",
    "https://www.ebay.de/itm/000000000001",
    "https://www.ebay.com/b/PC-Laptops-Netbooks/177",
    "https://www.ebay.com/sch/177/i.html"
  ],
  "maxItems": 150
}
```

Supported links: `/b/` category pages (as pasted, filters included), result links without a keyword that name a category (`/sch/<categoryId>/i.html`, `?_sacat=`; read from that category's page with the same filters), `/str/` and `/usr/` seller links and seller result links (`/sch/<seller>/m.html`, `?_ssn=`; read from the seller's store, newest first) and item links in any of the usual forms. The `urls` field is pre-filled with example links covering every shape (item variants such as a title slug, a tracking query or an `m.` host point at the same listing, and category-id or seller-path links lead to a page already listed, so they are scraped once). If you switch to URL mode and leave it untouched, the distinct examples (category, seller, item, country host) are scraped, so replace it with your own links.

#### Run it from your code

Python:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("abotapi/ebay-com-scraper").call(
    run_input={"mode": "search", "categoryId": "177", "maxItems": 20})
for listing in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(listing["title"], listing["price"], listing["currency"])
```

JavaScript:

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

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('abotapi/ebay-com-scraper').call({
  mode: 'search', categoryId: '177', maxItems: 20,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the **Integrations** tab.

#### Reading item pages and the surcharge

Turning on **Read item pages** adds one extra request per listing and bills one Item page event for every listing that is both pushed to the dataset and had its own item page successfully read. A listing whose item page is refused, and stays refused after one retry pass, ships with its result-card fields only, carries `detailIncomplete: true`, and is not charged the surcharge. When the item page itself is read but only its separate description document keeps failing, the listing keeps its item-specifics, images and category trail, is still stamped `detailIncomplete: true` for the missing description, and is still charged: the surcharge follows whether the item page was read, not whether every last field on it came back. A pasted item link has no result card behind it, so its item page is always read regardless of the **Read item pages** toggle, and is billed the Item page event whenever that read succeeds, even with the toggle off; it follows the same refused-item-page and description-only rules above. An UNCHANGED row suppressed by Incremental mode is never billed the surcharge, even on a run where its item page had to be read to compute the fingerprint. Seller feedback comments (`reviews`, see **Max seller feedback comments per listing**) are off by default and ride on the same event when you turn them on: the seller's feedback page is read once per seller per run, and each started block of 10 delivered comments adds one more Item page event (up to 5 events for 50 comments), billed once per seller on the first of that seller's listings that is pushed with its item page read. Later listings from the same seller carry the same comments and add nothing. A listing with no readable feedback, a suppressed UNCHANGED row, or `maxReviewsPerProduct` left at `0` adds nothing. `reviews` is not part of change detection in Incremental mode, because the newest comments move constantly.

#### Max listings, Max pages and page size

**Max listings** is the whole run's cap; **Max pages** limits how many pages are read per category, seller or link before moving on, independent of the cap. When the whole run fits inside one small result page, the actor asks eBay for a 60-item page instead of a 240-item one, since a 240-item page is around 4.7 MB of transfer to keep 20 rows. Raising **Max listings** past 60 switches it back to the large page automatically.

#### Resume and recurring updates

- **Resume** (`resumeFromRunId`) continues one interrupted run: paste its run or dataset ID and the actor skips every listing id it already returned there, so you don't pay twice. It cannot be combined with Incremental mode once that mode already has saved state for the same search: the run fails with a clear message rather than guessing which one you meant.
- **Incremental mode** (`incrementalMode`) is for scheduled runs over the same search. The first run returns every matching listing as `NEW`. Later runs classify each listing `NEW`, `UPDATED` (with `changedFields`), or `UNCHANGED` (suppressed and not billed unless `emitUnchanged` is on). `EXPIRED` rows are only produced once a run has scanned the tracked search to its natural end, with no cap, no resume and at least one listing seen, and only with `emitExpired` on; a capped, resumed or empty-scan run keeps the existing state untouched instead of guessing. `REAPPEARED` is never a default outcome of turning Incremental mode on: it only fires for a listing that was previously tombstoned `EXPIRED` by that same complete-scan process and is then seen again on a later run. `stateKey` names or shares one monitoring campaign; left empty, the actor derives one from the mode, marketplace, category, sellers, links and filters, so different searches never collide. Incremental mode also excludes `timeLeft`, `attributes`, `scrapedAt`, `updatedAt`, `sourceUrl`, `pageNumber` and `searchQuery` from change detection, because an auction countdown or a walk artifact would otherwise mark a listing changed on every single run; every real change still surfaces through a tracked structured field such as `listingType`, `bidCount` or `shippingCost`. Add your own noisy fields to **Ignore fields when detecting changes** (`ignoreFieldsForChanges`, for example `soldCount`): a plain name is ignored at any depth, a dotted path such as `shippingDetails.cost` targets one nested field. Ignored fields stay in the output but never trigger `UPDATED` or appear in `changedFields`. The comparison re-reads the stored listing with the current ignore list, so editing it causes no one-time wave of `UPDATED` rows. A pasted link that starts after page 1 (`_pgn` above 1) never produces `EXPIRED` rows, because the earlier pages were not scanned.

### Send results into your apps (MCP connectors)

Results can also be piped straight into the apps you already use, over Model Context Protocol. Authorize a connector under Apify, Settings, API & Integrations, then select it in the
**Export to your apps (MCP connectors, optional)** section of the input.

- `mcpConnectors` picks one or more authorized connectors. Leave it empty and nothing happens: the export code never runs and the dataset is byte for byte identical.
- `notionParentPageUrl` is required for the Notion connector, which gets one page per listing, and is ignored by every other connector.
- `maxNotifyListings` caps how many listings are written to each connector per run. It does not affect the dataset.

What gets written is a **condensed, human readable summary** per listing: a title plus the key fields flattened to plain text. Nested objects collapse to their main value and arrays are trimmed to a few entries. The complete record always stays in the Apify dataset.

If a connector write fails, the run logs a warning and carries on. Scrape data is never affected.

### Input Parameters

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `mode` | string | `search` | `search` or `url`. |
| `sellerUsernames` | array | (none) | Seller usernames whose store (or, without a store, profile) pages are walked, newest first. Search mode only. |
| `marketplace` | string | `com` | Which of the sixteen storefronts to search. Search mode only; a pasted link carries its own. |
| `categoryId` | string | (none) (prefill: `177`) | Numeric category id from the marketplace's own category links, read from that category's page with your filters. With no category, seller or filter at all, search mode browses 177. A category name is not accepted: it is ignored with a warning in the run log. |
| `condition` | array | (none) | New, Open box, the four refurbished grades, Seller refurbished, Used, For parts. Multi-select. |
| `buyingFormat` | string | `any` | Any, Buy It Now, Auction, Accepts offers. |
| `minPrice` | integer | (none) | Keep only listings priced at or above this amount, in the marketplace's own currency. |
| `maxPrice` | integer | (none) | Keep only listings priced at or below this amount, in the marketplace's own currency. |
| `freeShippingOnly` | boolean | `false` | Keep only listings flagged as free delivery. |
| `freeReturnsOnly` | boolean | `false` | Keep only listings offering free returns. Not offered by category pages, so it is not applied there (the log says so). |
| `returnsAcceptedOnly` | boolean | `false` | Keep only listings that accept returns at all. |
| `itemLocation` | string | `default` | Marketplace default, marketplace country only, same continent, or worldwide. |
| `sortBy` | string | `best_match` | Best match, ending soonest, newly listed, price and delivery lowest or highest, or distance nearest (with a postal code). Seller stores are always read newest first. |
| `sortOrder` | string | none | Alias of `sortBy` (`timeNewlyListed`, `pricePlusPostageLowest`, `pricePlusPostageHighest`, `distanceNearest`). |
| `postalCode` | string | none | Origin for the distance-nearest sort. |
| `ebaySite` | string | none | Alias of `marketplace`, for example `ebay.co.uk`. |
| `subcategoryId` | string | none | Numeric category id that overrides `categoryId`. |
| `itemCondition` | string | `any` | `any`, `new` or `used`, used when `condition` is empty. |
| `conditionId` | integer | none | Marketplace condition id, overrides `itemCondition`. |
| `aspectFilter` | object | none | Item-specific facets such as `{"Brand": "Nike"}`. |

| `urls` | array | one example of each supported link shape | Category (`/b/...`, `/sch/<categoryId>/i.html`, `?_sacat=`), seller (`/str/`, `/usr/`, `/sch/<seller>/m.html`, `?_ssn=`) and item links (bare id, title slug, tracking query, `m.` host, country hosts). URL mode only. Leaving the field untouched scrapes the distinct example links (category, seller, item, country host), so clear it or paste your own. |
| `minSellerFeedbackPercent` | integer | (none) | Applies in both modes. Sellers with no score yet are kept. |
| `fetchDetails` | boolean | `false` | Read each listing's item page. Adds one Item page event per listing whose item page is actually read. Refused item pages get a second pass; a listing that still cannot be read ships without details, flagged `detailIncomplete`, unbilled. |
| `maxReviewsPerProduct` | integer | `0` | With item pages read, attach up to this many of the seller's most recent public feedback comments to each listing as `reviews` (reviewer, rating, date, text, item, price; `helpful` is always null because the site has no helpful votes). `0` (the default) turns it off, maximum `50`. The seller's feedback page is read once per seller per run; every started block of 10 delivered comments adds one Item page event, billed once per seller on that seller's first returned listing, and later listings from the same seller reuse the comments at no extra charge. |
| `maxItems` | integer | `20` | The run's cap. `0` means unlimited. |
| `maxPages` | integer | `0` | Pages per category, seller or link. `0` means no limit of your own: the run still stops at Max listings, at the end of results, or at a built-in ceiling of 100 pages. |
| `resumeFromRunId` | string | (none) | Continue one interrupted run from its run or dataset id. |
| `incrementalMode` | boolean | `false` | Return only what changed since the last run of the same search. |
| `ignoreFieldsForChanges` | array | none | Field names or dotted paths excluded from change detection (incremental mode only). |
| `stateKey` | string | (none) | Name a monitoring campaign, or deliberately share state. |
| `emitUnchanged` | boolean | `false` | Also return, and bill, UNCHANGED rows. |
| `emitExpired` | boolean | `false` | Also return, and bill, EXPIRED rows. |
| `proxy` | object | Apify Proxy, residential | Connection configuration. Residential is the default; free plans continue on a backup connection. |
| `mcpConnectors` | array | (none) | Optional MCP connectors to also write results into. |
| `notionParentPageUrl` | string | (none) | Notion parent page id or URL, Notion connector only. |
| `maxNotifyListings` | integer | `50` | Cap on items written to each connector per run. |

**Sold and completed listings.** The marketplace shows sold and completed listings only to signed-in visitors: anonymous requests for them are answered with an interruption page even on a warm residential session (checked 2026-10-19). This actor does not sign in, so it returns active listings only and has no sold price or end date fields. API callers migrating from other actors can still send `daysToScrape`, `includeCompletedListings`, `sortOrder: endedRecently`, `buyingFormat` values `all`, `buyItNow` and `acceptsOffers`, and `itemLocation: domestic`: they are accepted in code (the sold-history ones log a warning and return active listings), but they are not offered in the Console form.

**Additional output fields.** Each row also carries `keyword`, `itemNumber`, `buyingFormat`, `bids`, `thumbnailUrl`, `fullResThumbnailUrl`, `sellerUsername`, `sellerFeedbackScore`, `sellerPositivePercent`, `shippingPrice`, `shippingCurrency`, `shippingType`, `totalPrice` (price plus shipping), `category`, `categoryId` and `breadcrumbs`, `productDetails`, `descriptionIframeSrc` and `sellerDescriptionText`. Most repeat values the row already holds under these names; `sellerUsername` is the seller handle (read from the item page's seller links or the seller-page card, never the display name), while `categoryId` and `descriptionIframeSrc` are new data read from the item page (empty when item pages are not read). All of them are empty when the source has no value. They are excluded from change detection in monitoring mode, as is `reviews`; the tracked fields `quantityAvailable`, `quantitySold`, `sellerName`, `sellerFeedbackPercent` and `sellerFeedbackCount` are compared, and a first appearance of one of them on a listing that held no value before never counts as a change.

**Views.** The dataset has four views: Overview (the listing card), Item details (gallery, specifics, seller card, quantity available and sold), Changes (Incremental mode state, `changedFields`) and Seller feedback (`reviews`). Select one with the `view` parameter in the dataset API or the tabs in the Console.

### Output Example

> Sample shape, values are illustrative placeholders, not from a live listing. Shown with **Read item pages** and **Incremental mode** both on so every field appears; a default run omits `changeType`, `changedFields`, `firstSeenAt` and `lastSeenAt`, and the item-page-only fields stay `null`.

```json
{
  "itemId": "000000000001",
  "url": "https://www.ebay.com/itm/000000000001",
  "title": "Sample Listing Title",
  "subtitle": "Sample seller strapline",
  "condition": "Used",
  "conditionId": "3000",
  "brand": "Sample Brand",
  "marketplace": "com",
  "marketplaceDomain": "www.ebay.com",
  "price": 100.0,
  "priceMax": 150.0,
  "priceText": "$100.00 to $150.00",
  "currency": "USD",
  "originalPrice": 125.0,
  "discountPercent": 20.0,
  "listingType": "buy_it_now",
  "bidCount": null,
  "timeLeft": null,
  "shippingCost": null,
  "shippingText": "Free delivery",
  "freeShipping": true,
  "itemLocation": "United States",
  "returnsText": "Free returns",
  "freeReturns": true,
  "soldCount": 42,
  "sellerName": "examplestore",
  "sellerUrl": "https://www.ebay.com/usr/examplestore",
  "sellerFeedbackPercent": 98.5,
  "sellerFeedbackCount": 1234,
  "badges": ["Sample Badge"],
  "imageUrl": "https://i.ebayimg.com/images/g/000000000000/s-l500.webp",
  "attributes": ["Buy It Now", "Free delivery", "Located in United States", "Free returns", "42 sold"],
  "images": ["https://i.ebayimg.com/images/g/000000000000/s-l1600.jpg"],
  "itemSpecifics": { "Processor": "Sample Processor", "SSD Capacity": "1 TB", "Seller Notes": "Sample seller notes." },
  "categoryPath": ["Electronics", "Computers", "Laptops"],
  "description": "Full seller description text appears here when Read item pages is on.",
  "sellerNotes": "Sample seller notes.",
  "mpn": "SAMPLE-MPN",
  "model": "Sample Model",
  "color": "Black",
  "gtin": null,
  "listPrice": 125.0,
  "availability": "In Stock",
  "quantityAvailable": 5,
  "quantitySold": 42,
  "paymentMethods": ["PayPal", "Visa"],
  "shippingDetails": "Shipping: Sample shipping line",
  "returnPolicy": "Returns: 30 day returns accepted",
  "listedAt": "2026-01-01T00:00:00Z",
  "updatedAt": "2026-01-01T00:00:00Z",
  "searchQuery": null,
  "sourceUrl": "https://www.ebay.com/b/177?_ipg=240&_pgn=1",
  "pageNumber": 1,
  "scrapedAt": "2026-01-01T00:00:00Z",
  "changeType": "NEW",
  "changedFields": [],
  "firstSeenAt": "2026-01-01T00:00:00Z",
  "lastSeenAt": "2026-01-01T00:00:00Z"
}
```

### Plan Requirement

A paid Apify plan with residential proxies is required for reliable results. In testing, eBay answered datacenter connections with an HTTP 403 "Error Page | eBay" and no listings, while residential connections loaded pages (sometimes after one refused first attempt in the same session, which the actor retries). The Proxy & connection section therefore defaults to residential.

Free Apify plans do not include residential proxies. On those plans the run continues on a backup connection with a note in the log, but pages may still be unavailable. If no page loads, the run fails with: "No results: residential proxies are unavailable on this run (Apify free plans do not include them), and the backup connection could not load the requested eBay pages. Upgrade at https://apify.com/pricing or supply your own proxy URLs, then try again." If the proxy itself refuses the account, the message says your Apify plan does not include proxy access. Any other total failure reads "Could not load any results page (the requests failed), so nothing was collected. This is a connection problem, not an empty search." You can also supply your own proxy URLs. No eBay account, API key or other credential is required. The **Pricing** tab shows the current per-event rates for this actor.

### FAQ

#### How much does it cost?

This actor uses Apify's pay-per-event pricing: one event per listing returned, one extra event per listing whose item page you had read, and one one-time event when the run starts. The **Pricing** tab shows the current rates. Use **Max listings** to cap the cost of any run.

#### Is it legal to scrape eBay?

This actor collects only publicly available listing data. You are responsible for how you use it: follow eBay's terms and the laws that apply to you, and get legal advice if you plan commercial redistribution. Listing facts such as price and availability are generally not protected, but photos and seller-written descriptions may be subject to third-party rights.

#### Can I get only new or changed listings on a schedule?

Yes. Schedule the actor from the **Schedules** tab and turn on **Incremental mode**. Each run then returns new and updated listings; with Emit expired on, a complete scan also returns expired listings, and a listing that later comes back is returned as reappeared. Unchanged listings are suppressed and not billed by default.

#### Why can't I filter for sold or completed listings?

Both eBay's `LH_Sold=1` and `LH_Complete=1` search filters were tested live and answered with eBay's sign-in page instead of results: those views sit behind an eBay account and cannot be read without one. This scraper does not offer a sold-listings toggle rather than offer one that would silently return nothing. If a pasted link carries either filter and hits the sign-in page, the log warns that the address was answered with a sign-in page; the run still finishes, with no rows from that link.

#### Why did my run fail instead of returning an empty dataset?

If every page was refused, the run stops with a clear message so "no listings matched" is never confused with "nothing could be read". If the only sources were legacy keyword inputs, the message says keyword search is currently unavailable on eBay. Otherwise run it again in a few minutes; on a free plan, supply your own proxy URLs or upgrade (see Plan Requirement). A category that genuinely has no matching listings still succeeds with zero rows.

#### Can I search by keyword?

No. eBay currently refuses its search result pages to automated visitors on every storefront, whatever the connection, so keyword search was removed from the input form. Browse a category id or a seller instead, or paste category, seller and item links. Older saved tasks that still send keywords get one attempt and a plain message, and nothing is charged for them.

#### Can I use it with AI agents or MCP?

Yes. Call it from any Apify integration or MCP client, and use the connector field to push results into Notion, Linear or Airtable.

### 🔗 More scrapers you might like

Pair this actor with these related scrapers from the same team:

<table>
<tr><td>🛒 <a href="https://apify.com/abotapi/muji-scraper"><b>MUJI Scraper</b></a><br>Scrape MUJI products from the United States, Canada and Australia storefronts. Search by...</td><td>🏷️ <a href="https://apify.com/abotapi/vinted-marketplace-scraper"><b>Vinted Multi-Country Scraper</b></a><br>Scrape Vinted across 27 country marketplaces: keyword search with brand, price and...</td></tr>
<tr><td>🛒 <a href="https://apify.com/abotapi/depop-scraper"><b>Depop Scraper</b></a><br>Scrape Depop search results, listings, shops, and reviews to JSON, CSV, or Excel. Extract...</td><td>🚗 <a href="https://apify.com/abotapi/trademe-co-nz-scraper"><b>Trade Me NZ Scraper</b></a><br>Scrape trademe.co.nz across all five sections: Marketplace goods and auctions, Property...</td></tr>
<tr><td>⚽ <a href="https://apify.com/abotapi/whatnot-scraper"><b>Whatnot Scraper</b></a><br>Scrape Whatnot live and upcoming shows, queued items and lots, and seller profiles...</td><td>🛒 <a href="https://apify.com/abotapi/uniqlo-com-scraper"><b>UNIQLO Scraper</b></a><br>Scrape UNIQLO products across 21 country storefronts in local currency. Price with pre...</td></tr>
</table>

👉 [Browse all abotapi scrapers](https://apify.com/abotapi)

### 💬 Support

- 🐞 **Found a bug or a missing field?** Open a ticket on the [Issues tab](https://apify.com/abotapi/ebay-com-scraper/issues/open). We usually reply within hours.
- 🛠️ **Need another site, extra fields or a private build?** Email <contact@abotapi.com>.
- ⭐ **Enjoying it?** A quick review on the actor page helps other users find it.

# Actor input Schema

## `mode` (type: `string`):

Choose 'search' to browse a category or a seller's store with filters, or 'url' to scrape pasted links.

## `sellerUsernames` (type: `array`):

Only used when mode = search, ignored in url mode. One or more seller usernames, for example 'examplestore'. Each seller's store (or, when the seller has no store, their profile page) is walked newest first, which covers the whole inventory.

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

Only used when mode = search, ignored in url mode where the marketplace is taken from each pasted link's own address. Every option here was checked live and returns real results. Prices come back in that marketplace's own currency.

## `categoryId` (type: `string`):

Numeric id of the marketplace category to browse, as it appears in the marketplace's own category links (for example 177 for PC laptops). The filters below are applied by the marketplace itself, except Free returns only, which category pages do not offer. A category name is not accepted: it is ignored with a warning in the run log.

## `condition` (type: `array`):

Optional. Keep only listings in these conditions. Selecting several returns listings in any of them. Applied by the marketplace itself, so it narrows the result count at the source.

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

Optional. Keep only listings sold in this format. Applied by the marketplace itself.

## `minPrice` (type: `integer`):

Optional. Keep only listings priced at or above this amount, in the selected marketplace's own currency. Applied by the marketplace itself.

## `maxPrice` (type: `integer`):

Optional. Keep only listings priced at or below this amount, in the selected marketplace's own currency. Applied by the marketplace itself.

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

Optional. Keep only listings the marketplace flags as free delivery. Applied by the marketplace itself.

## `freeReturnsOnly` (type: `boolean`):

Optional. Keep only listings that offer free returns. Applied by the marketplace itself.

## `returnsAcceptedOnly` (type: `boolean`):

Optional. Keep only listings that accept returns at all, whether or not the buyer pays for them. Applied by the marketplace itself.

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

Optional. Keep only listings held in this region, as the marketplace defines it relative to the storefront you selected. Applied by the marketplace itself.

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

The order the marketplace returns results in. All five are the marketplace's own orderings, applied at the source. Promoted listings can still appear ahead of the requested order.

## `sortOrder` (type: `string`):

Same orderings under alternative names; used only when Sort by is left at its default.

## `ebaySite` (type: `string`):

Same as Marketplace, used only when Marketplace is empty. Accepts 'ebay.co.uk' or 'co.uk'.

## `subcategoryId` (type: `string`):

Numeric category id that overrides Category ID when set.

## `itemCondition` (type: `string`):

Quick condition filter, used only when the Condition list is empty.

## `conditionId` (type: `integer`):

Marketplace condition id (1000 New, 3000 Used, 2000 Certified refurbished, 7000 For parts...). Overrides Item condition.

## `aspectFilter` (type: `object`):

Item-specific facets as an object, for example {"Brand": "Nike"}. A list of values is OR-ed.

## `postalCode` (type: `string`):

Origin for the 'Distance: nearest first' sort.

## `urls` (type: `array`):

Only used when mode = url, ignored in search mode. Mix freely: item pages such as /itm/123456789012 , category pages such as /b/PC-Laptops-Netbooks/177 or /sch/177/i.html , and seller pages such as /str/examplestore , /usr/examplestore or /sch/examplestore/m.html .

## `minSellerFeedbackPercent` (type: `integer`):

Optional. Keep only listings from sellers with at least this positive feedback percentage. Applied to the results in both modes. A seller too new to have a score yet is kept rather than dropped.

## `fetchDetails` (type: `boolean`):

Read each listing's own item page for the full image gallery, the item specifics table, the category trail, brand, MPN, model, quantity available and sold, payment methods and the seller's full description. One extra request per listing, billed as one Item page event per listing returned. Pasted item links always read their item page, since a pasted link has no result card behind it. If the marketplace refuses some item pages, every affected listing is retried once before the run ends; a listing that still could not be read ships with its result-card fields, carries detailIncomplete: true, and is not billed the item page event.

## `maxReviewsPerProduct` (type: `integer`):

How many of the seller's most recent public feedback comments to attach to each listing as `reviews` (reviewer, rating, date, text). Off by default (0). Only applies when item pages are read. Limited to 50, one feedback page, because the marketplace shows no more per page. The seller's feedback page is read once per seller per run, and every started block of 10 delivered comments adds one Item page event once per seller, on the first listing of that seller that is returned; later listings from the same seller carry the same comments at no extra charge.

## `maxItems` (type: `integer`):

Maximum number of listings to return across the whole run. This is the run's cap. Use 0 for unlimited.

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

Maximum number of result pages read per category, seller or link. 0 means no limit set by you, which is the default: the run then stops at Max listings, at the end of the results, when a page returns nothing new, or at a built-in ceiling of 100 pages per category, seller or link, whichever comes first.

## `proxy` (type: `object`):

Apify Proxy is recommended. Residential is the default and gives the most reliable results; free Apify plans continue on a backup connection.

## `resumeFromRunId` (type: `string`):

Paste a previous run ID or dataset ID to continue a large pull without returning or charging for listings already collected there. Use this after an interrupted run. For recurring monitoring of the same search, use Incremental mode below instead.

## `incrementalMode` (type: `boolean`):

Turn this on for daily or recurring monitoring. The first run returns every matching listing as NEW. Later runs normally return only NEW, UPDATED and REAPPEARED listings. A listing counts as UPDATED when any output field changes (price, bids, shipping, seller, title, condition, sold count and so on), and changedFields names them; scrapedAt, timeLeft, attributes, updatedAt, sourceUrl, pageNumber and searchQuery are excluded because they change on every fetch, and you can exclude more with Ignore fields. Turn on Emit unchanged or Emit expired only when you also want those rows returned (and billed). State is kept separately for each search, filter and lookup setup; use State key to name or deliberately share a monitoring campaign.

## `stateKey` (type: `string`):

Optional. Name this monitoring campaign to keep its state stable, or to deliberately share state across differently configured runs. Leave empty to let the actor derive a key automatically from the mode, marketplace, category, sellers, links and filters, so different searches never mix state with each other.

## `emitUnchanged` (type: `boolean`):

Off by default. Turn on to also return listings that have not changed since the last run, marked UNCHANGED. This returns, and bills, extra rows you already have, so leave it off unless you want the full snapshot every run.

## `emitExpired` (type: `boolean`):

Off by default. Turn on to also return listings that were present in a previous run but are no longer found, marked EXPIRED. Only produced once a run has fully scanned the tracked search, so never when Max listings capped it, when Resume was used, or when a pasted link starts after page 1. This returns, and bills, extra synthetic rows, so leave it off unless you need expiry tracking.

## `ignoreFieldsForChanges` (type: `array`):

Field names that must NOT count as a change when Incremental mode compares a listing with the last run, for example soldCount or imageUrl. A plain name is ignored at any depth; use a dotted path such as shippingDetails.cost for one nested field. The fields still appear in the output and are simply left out of the comparison and out of changedFields. Changing this list never causes a one-time wave of UPDATED rows. Built in, always ignored: scrapedAt, timeLeft, attributes, updatedAt, sourceUrl, pageNumber and searchQuery.

## `mcpConnectors` (type: `array`):

Optionally send results into the apps you already use, via Model Context Protocol (MCP) connectors. Authorize one under Apify, Settings, API & Integrations, then select it here. Notion gets a rich page-per-item export; other connectors get a best-effort write or digest. Leave empty to skip; never changes the dataset output. Supported: Notion (https://mcp.notion.com/mcp), Linear (https://mcp.linear.app/sse), Airtable (https://mcp.airtable.com/mcp), Apify (https://mcp.apify.com).

## `notionParentPageUrl` (type: `string`):

URL or id of the Notion page under which item pages are created. Required to enable the Notion export; ignored by other connectors.

## `maxNotifyListings` (type: `integer`):

Cap on items written to each connector per run. Does not affect the dataset.

## Actor input object example

```json
{
  "mode": "search",
  "marketplace": "com",
  "categoryId": "177",
  "buyingFormat": "any",
  "freeShippingOnly": false,
  "freeReturnsOnly": false,
  "returnsAcceptedOnly": false,
  "itemLocation": "default",
  "sortBy": "best_match",
  "itemCondition": "any",
  "urls": [
    "https://www.ebay.com/b/PC-Laptops-Netbooks/177/bn_317584",
    "https://www.ebay.com/sch/177/i.html",
    "https://www.ebay.com/sch/i.html?_ssn=ansonelectronicsurplus",
    "https://www.ebay.com/sch/ansonelectronicsurplus/m.html",
    "https://www.ebay.com/itm/286601719329",
    "https://www.ebay.com/itm/some-title-slug/286601719329",
    "https://www.ebay.com/itm/286601719329?hash=item123&_trksid=p1",
    "https://m.ebay.com/itm/286601719329",
    "https://www.ebay.co.uk/itm/286601719329",
    "https://www.ebay.com/str/ansonelectronicsurplus",
    "https://www.ebay.com/usr/ansonelectronicsurplus",
    "https://www.ebay.com/sch/i.html?_sacat=177",
    "https://www.ebay.com/b/PC-Laptops-Netbooks/177",
    "https://www.ebay.de/itm/267276756654",
    "https://ebay.com/itm/267276756654"
  ],
  "fetchDetails": false,
  "maxReviewsPerProduct": 0,
  "maxItems": 20,
  "maxPages": 0,
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  },
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "maxNotifyListings": 50
}
```

# Actor output Schema

## `overview` (type: `string`):

No description

## `detail` (type: `string`):

No description

## `changes` (type: `string`):

No description

## `reviews` (type: `string`):

No description

# 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 = {
    "mode": "search",
    "marketplace": "com",
    "categoryId": "177",
    "urls": [
        "https://www.ebay.com/b/PC-Laptops-Netbooks/177/bn_317584",
        "https://www.ebay.com/sch/177/i.html",
        "https://www.ebay.com/sch/i.html?_ssn=ansonelectronicsurplus",
        "https://www.ebay.com/sch/ansonelectronicsurplus/m.html",
        "https://www.ebay.com/itm/286601719329",
        "https://www.ebay.com/itm/some-title-slug/286601719329",
        "https://www.ebay.com/itm/286601719329?hash=item123&_trksid=p1",
        "https://m.ebay.com/itm/286601719329",
        "https://www.ebay.co.uk/itm/286601719329",
        "https://www.ebay.com/str/ansonelectronicsurplus",
        "https://www.ebay.com/usr/ansonelectronicsurplus",
        "https://www.ebay.com/sch/i.html?_sacat=177",
        "https://www.ebay.com/b/PC-Laptops-Netbooks/177",
        "https://www.ebay.de/itm/267276756654",
        "https://ebay.com/itm/267276756654"
    ],
    "proxy": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    },
    "incrementalMode": false,
    "emitUnchanged": false,
    "emitExpired": false
};

// Run the Actor and wait for it to finish
const run = await client.actor("abotapi/ebay-com-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 = {
    "mode": "search",
    "marketplace": "com",
    "categoryId": "177",
    "urls": [
        "https://www.ebay.com/b/PC-Laptops-Netbooks/177/bn_317584",
        "https://www.ebay.com/sch/177/i.html",
        "https://www.ebay.com/sch/i.html?_ssn=ansonelectronicsurplus",
        "https://www.ebay.com/sch/ansonelectronicsurplus/m.html",
        "https://www.ebay.com/itm/286601719329",
        "https://www.ebay.com/itm/some-title-slug/286601719329",
        "https://www.ebay.com/itm/286601719329?hash=item123&_trksid=p1",
        "https://m.ebay.com/itm/286601719329",
        "https://www.ebay.co.uk/itm/286601719329",
        "https://www.ebay.com/str/ansonelectronicsurplus",
        "https://www.ebay.com/usr/ansonelectronicsurplus",
        "https://www.ebay.com/sch/i.html?_sacat=177",
        "https://www.ebay.com/b/PC-Laptops-Netbooks/177",
        "https://www.ebay.de/itm/267276756654",
        "https://ebay.com/itm/267276756654",
    ],
    "proxy": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
    "incrementalMode": False,
    "emitUnchanged": False,
    "emitExpired": False,
}

# Run the Actor and wait for it to finish
run = client.actor("abotapi/ebay-com-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 '{
  "mode": "search",
  "marketplace": "com",
  "categoryId": "177",
  "urls": [
    "https://www.ebay.com/b/PC-Laptops-Netbooks/177/bn_317584",
    "https://www.ebay.com/sch/177/i.html",
    "https://www.ebay.com/sch/i.html?_ssn=ansonelectronicsurplus",
    "https://www.ebay.com/sch/ansonelectronicsurplus/m.html",
    "https://www.ebay.com/itm/286601719329",
    "https://www.ebay.com/itm/some-title-slug/286601719329",
    "https://www.ebay.com/itm/286601719329?hash=item123&_trksid=p1",
    "https://m.ebay.com/itm/286601719329",
    "https://www.ebay.co.uk/itm/286601719329",
    "https://www.ebay.com/str/ansonelectronicsurplus",
    "https://www.ebay.com/usr/ansonelectronicsurplus",
    "https://www.ebay.com/sch/i.html?_sacat=177",
    "https://www.ebay.com/b/PC-Laptops-Netbooks/177",
    "https://www.ebay.de/itm/267276756654",
    "https://ebay.com/itm/267276756654"
  ],
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  },
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false
}' |
apify call abotapi/ebay-com-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,abotapi/ebay-com-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/l8gab5G2r8y7hrgVN/builds/xqPsg0yU594PoCi8L/openapi.json
