# eBay Scraper - Category Listings, Prices & Discounts (`s-r/ebay-scraper`) Actor

eBay scraper and price tracker for category pages. An eBay web scraper returns one row per product listing: item ID, title, price, was-price, discount percent, condition, shipping cost, image and URL, in page order. Scrape eBay listings with no login or API key, for price monitoring and resellers.

- **URL**: https://apify.com/s-r/ebay-scraper.md
- **Developed by:** [SR](https://apify.com/s-r) (community)
- **Categories:** E-commerce, Business
- **Features:** Limited permissions
- **Stats:** 1 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 run start fees

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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 Scraper - Category Listings, Prices and Discounts

This eBay scraper turns an eBay category page into structured listing data for price monitoring,
reselling and market research: current price, genuine discounts, item condition, shipping cost
and images when the listing card provides them. Provide an eBay category URL and choose how many
listings to return. Each listing becomes one dataset row, with a stable eBay item ID and its
listing URL. No eBay account, login or developer key is needed, and results download as JSON,
CSV, Excel or through the Apify API.

### What does this eBay scraper do?

It reads the main listing grid of an eBay browse page (an address such as
`https://www.ebay.com/b/Laptops-Netbooks/175672`), follows the next pages until your limit is
reached, and returns one clean row per listing. It keeps the listings in the order eBay shows
them, removes repeated item IDs, and only marks a listing as on sale when the previous price is
really above the current price. Promoted carousels at the top of a category are not mixed into
the results.

### eBay scraper input

All input fields are optional in the input schema, but a run needs a `browse_url` or a `keyword`.
Browse URLs are the supported route. The default maximum is 100 listings, with up to four attempts
per page. The supported range is 1–2000 listings and 1–8 attempts. Listings remain in page order
and repeated item IDs appear once.

This input was used for the measured example below:

```json
{
  "browse_url": "https://www.ebay.com/b/Laptops-Netbooks/175672",
  "limit": 100,
  "retries": 4
}
```

Use a category page whose path begins with `/b/`. Mobile category addresses work as well:
`https://m.ebay.com/b/Laptops-Netbooks/175672` and the mobile Cell Phones & Smartphones category
each returned 20 listings in a 20-listing run on 3 October 2026, priced in US dollars. Addresses
outside eBay, and addresses with a login part, a port or a protocol other than HTTP(S), are
refused with an explanatory error record. A category hub can be a valid eBay page while
containing no listing grid; that produces an empty result. A deleted category also produces an
empty result with an error record explaining why no listings were returned.

`keyword` takes precedence over `browse_url`. Keyword search is currently unavailable in this
Actor and can return `search_unavailable` with zero listings. Use `browse_url` for supported
collection. A direct `/sch/` search URL passed in `browse_url` is not supported.

The existing keyword options remain accepted: `site`, `sold_listings`, `buying_format`,
`condition`, `min_price`, `max_price`, `free_shipping`, `returns_accepted`, `seller`,
`category_id`, `sort_by` and `title_and_description`. These options describe the keyword
search and do not filter a browse category locally. The default marketplace is `us`, buying
format is `all`, sorting is `best_match`, and the boolean options default to false.

### eBay scraper output

The default dataset contains one row per listing. The existing twelve output fields remain
available with their existing types:

| Field | Meaning |
|---|---|
| `item_id` | eBay listing ID, as a string |
| `title` | Listing title as shown on its card |
| `price` | Current numeric price, or null when absent |
| `currency` | Currency of the numeric price |
| `was_price` | Previous numeric price, only when above the current price |
| `on_sale` | Whether the card provides a genuine markdown |
| `discount_percent` | Stated percentage off, or calculated from the price pair |
| `condition` | Card condition label, or null |
| `shipping_cost` | Stated numeric shipping cost, or null |
| `image` | Listing image URL, or null |
| `url` | Direct listing page URL |
| `position` | One-based position across the delivered rows |

Additional fields include `source`, `listing_id`, `image_url`, `fetched_at` and `country`.
`listing_id` matches `item_id`, while `image_url` matches `image`. `fetched_at` records when
that page was collected and `country` identifies the marketplace.

Here is the first real row from a 100-listing laptop run on 3 October 2026:

```json
{
  "item_id": "236631062029",
  "title": "Lenovo Legion Go 2 Handheld Gaming PC AMD Ryzen Z2 32GB 1TB 8.8\" OLED + WARRANTY",
  "price": 1299.0,
  "currency": "USD",
  "was_price": null,
  "on_sale": false,
  "discount_percent": null,
  "condition": "eBay Refurbished",
  "shipping_cost": null,
  "image": null,
  "url": "https://www.ebay.com/itm/236631062029",
  "position": 1,
  "source": "ebay-scraper",
  "fetched_at": "2026-10-03T18:05:29.064Z",
  "country": "US",
  "listing_id": "236631062029",
  "image_url": null
}
```

A discounted listing from the same run looks like this. The previous price of $699.99 is above
the current $479.99, so `on_sale` is true and `discount_percent` is 31.4:

```json
{
  "item_id": "198532859679",
  "title": "Acer - Aspire Go 15 15.6\" FHD Laptop AMD Ryzen 7 7730U 2023 AMD Radeon ...",
  "price": 479.99,
  "currency": "USD",
  "was_price": 699.99,
  "on_sale": true,
  "discount_percent": 31.4,
  "condition": "eBay Refurbished",
  "shipping_cost": null,
  "image": null,
  "url": "https://www.ebay.com/itm/198532859679",
  "position": 8,
  "source": "ebay-scraper",
  "fetched_at": "2026-10-03T18:05:29.064Z",
  "country": "US",
  "listing_id": "198532859679",
  "image_url": null
}
```

In that run, 97 of the 100 listings carried a price and a condition, and 29 were genuine
markdowns. A 30-listing run on the Cell Phones & Smartphones category returned 30 priced
listings, five of them on sale and five with an image URL.

The key-value store includes `summary`, with the delivered listing count, pages fetched,
price and condition coverage, sale count and source information. When a source error occurs,
`errors` contains a code and explanatory message. Empty results are successful runs; source
errors do not create invented dataset rows. If a later page fails after earlier pages succeeded,
the earlier listings remain available and the error describes the failed page.

### Use cases for eBay listing data

- **Price monitoring.** Run the same category every day and join the rows on `item_id` to see
  price drops, new markdowns and listings that disappear.
- **Reselling and sourcing.** Filter on `discount_percent` and `condition` to find refurbished or
  open-box stock priced well below its previous price, then open the `url` to check the seller.
- **Market and assortment research.** Count listings per condition, compare price bands across
  categories, or track how much of a category is on sale over time.
- **Competitor tracking for sellers.** See which prices and shipping costs appear next to your own
  listings in the categories you sell in.
- **Data pipelines.** Schedule the Actor, send the dataset to a spreadsheet, database or BI tool, and
  feed the rows into dashboards or alerts.

### eBay scraper vs. the eBay API and other tools

The official eBay developer program offers APIs for buyers and sellers, but it requires an
application, keys, OAuth tokens and call limits, and it returns item data in its own nested
format. This Actor needs only a category URL and returns a flat row per listing that you can open
in a spreadsheet straight away.

Browser extensions and copy-paste tools work for a single page, but they break on pagination and
do not run on a schedule. Writing your own script means maintaining parsers whenever the page
layout changes. Other eBay scrapers in the Apify Store focus on keyword search or sold listings;
this one is built for category pages, with careful discount detection and per-listing pricing
that charges nothing for empty results.

### Pricing

The price is unchanged: **$0.001 per run** plus **$0.0015 per delivered listing**. The run fee
also applies to runs that return no listings. Empty results incur no listing charges.
A run returning 100 listings therefore costs $0.151 in Actor events. A 30-listing run costs
$0.046. Charges follow delivered dataset rows, including the free-plan limit.

Users without a paid Apify plan receive at most ten listings per run. Paid users can request the
full supported limit. The dataset rows are delivered before listing events are charged. There
are no separate enrichment events for the optional fields in a listing row.

### FAQ

#### Can I use keyword search or sold listings?

Those input options remain available, but the keyword route is currently unavailable. A sold
listing filter belongs to keyword search and does not turn a browse category into a sales-history
source. Use an eBay browse category for the supported output.

#### Why is a price or image null?

The Actor reports the information carried by the listing card. Some cards show only a previous
price and no current price; a previous price alone is insufficient to infer a current offer.
Images may be absent under the existing extraction rule. Null is preserved rather than filled
with an assumed value.

#### Does free shipping mean shipping_cost is zero?

The existing output represents free shipping as null. `shipping_cost` contains a number when the
card explicitly states a paid shipping amount. This behavior remains compatible with prior runs.

#### Will I be charged for an empty dataset?

Only the run fee applies. No listing events are charged when zero listings are delivered.
Check the `errors` record to distinguish an empty category from an unavailable source page.

#### Do I need an eBay account or developer key?

No. The Actor reads the public category pages that any visitor can open. You only need an Apify
account to run it and to download the dataset.

#### Which eBay marketplaces are supported?

Category pages on www.ebay.com and m.ebay.com, priced in US dollars, are the measured route.
Category URLs from the other eBay marketplaces (for example the UK, Germany, Ireland, Australia or
Canada) are accepted and keep their own country and currency, with prices read correctly in both
`EUR 249.99` and `EUR 1.540,00` notation. Some of those marketplaces use a different page layout
that currently returns no listings; such a run ends as a successful empty run with only the run
fee. The `country` field on every row tells you which marketplace a listing came from.

#### How do I export the data to CSV or Excel?

Open the run's dataset in Apify Console and choose JSON, CSV, Excel, XML or HTML, or fetch the
same dataset through the Apify API from your own code or an integration.

#### Can I collect more than one page?

Yes. The Actor follows the category pages automatically up to the requested limit, with an
upper bound of thirty pages. Listings are deduplicated by eBay item ID and positions remain
continuous. Availability and page order can change as sellers update their listings.

# Actor input Schema

## `browse_url` (type: `string`):

An eBay browse URL such as https://www.ebay.com/b/<Category>/<id>. This is the supported path today and is what the prefill uses.

## `limit` (type: `integer`):

How many listings to return. Roughly 60 per page.

## `retries` (type: `integer`):

Retries with backoff before a page is reported as an error. Each retry uses a fresh exit IP.

## `keyword` (type: `string`):

What you would type into eBay's search box, which builds the URL for you. IMPORTANT: eBay currently refuses keyword search from Apify's network with HTTP 403, so on the platform this path reports `search_unavailable` and returns nothing. Browse URLs are unaffected. Use browse_url until this says otherwise.

## `site` (type: `string`):

Which eBay marketplace to search. Prices come back in that market's own currency. Only applies to keyword search, which is currently unavailable from the Apify platform.

## `sold_listings` (type: `boolean`):

Completed sales rather than what is currently for sale. This is the resale-pricing view: what things actually went for, not what sellers are asking. Only applies to keyword search, which is currently unavailable from the Apify platform.

## `buying_format` (type: `string`):

Auctions and fixed-price listings price very differently, so mixing them blurs any average you compute. Only applies to keyword search, which is currently unavailable from the Apify platform.

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

Any of new, opened, used, parts_only, refurbished_certified, refurbished_excellent, refurbished_very_good, refurbished_good, refurbished_seller. Only applies to keyword search, which is currently unavailable from the Apify platform.

## `min_price` (type: `integer`):

In the marketplace's own currency. Only applies to keyword search, which is currently unavailable from the Apify platform.

## `max_price` (type: `integer`):

In the marketplace's own currency. Only applies to keyword search, which is currently unavailable from the Apify platform.

## `free_shipping` (type: `boolean`):

Only listings eBay marks as free delivery. Only applies to keyword search, which is currently unavailable from the Apify platform.

## `returns_accepted` (type: `boolean`):

Only listings whose seller accepts returns. Only applies to keyword search, which is currently unavailable from the Apify platform.

## `seller` (type: `string`):

Limit to one seller's listings by their eBay username. Only applies to keyword search, which is currently unavailable from the Apify platform.

## `category_id` (type: `string`):

eBay's numeric category id, to narrow a broad keyword. Only applies to keyword search, which is currently unavailable from the Apify platform.

## `sort_by` (type: `string`):

eBay sorts before it pages, so this changes which listings a capped run returns, not just their order. Only applies to keyword search, which is currently unavailable from the Apify platform.

## `title_and_description` (type: `boolean`):

Match the search term against listing descriptions as well as titles. Widens a narrow term considerably. Only applies to keyword search, which is currently unavailable from the Apify platform.

## Actor input object example

```json
{
  "browse_url": "https://www.ebay.com/b/Laptops-Netbooks/175672",
  "limit": 100,
  "retries": 4,
  "keyword": "nintendo switch oled",
  "site": "us",
  "sold_listings": false,
  "buying_format": "all",
  "condition": [
    "new",
    "used"
  ],
  "free_shipping": false,
  "returns_accepted": false,
  "seller": "bestbuy",
  "category_id": "139971",
  "sort_by": "best_match",
  "title_and_description": false
}
```

# Actor output Schema

## `listings` (type: `string`):

One row per eBay listing.

## `summary` (type: `string`):

Counts, price coverage and how many listings are discounted.

## `errors` (type: `string`):

Failures with a code and a redacted message.

# 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 = {
    "browse_url": "https://www.ebay.com/b/Laptops-Netbooks/175672",
    "limit": 100,
    "retries": 4
};

// Run the Actor and wait for it to finish
const run = await client.actor("s-r/ebay-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 = {
    "browse_url": "https://www.ebay.com/b/Laptops-Netbooks/175672",
    "limit": 100,
    "retries": 4,
}

# Run the Actor and wait for it to finish
run = client.actor("s-r/ebay-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 '{
  "browse_url": "https://www.ebay.com/b/Laptops-Netbooks/175672",
  "limit": 100,
  "retries": 4
}' |
apify call s-r/ebay-scraper --silent --output-dataset

```

## MCP server setup

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