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

Scrape eBay browse pages: listing title, price, strikethrough was-price with discount percent, item condition, shipping cost and image. Reads the listing grid, not the promoted carousel.

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

## Pricing

Pay per event

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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## eBay Scraper

Scrape **eBay browse pages**: listing title, price, the strikethrough was-price
with the discount worked out, item condition, shipping cost and image. Point it
at a category and it pages through.

### eBay was on our "closed" list, and the verdict was wrong

It was recorded as throwing a transport error to bot user agents. That was true
of the agents tried, and false as a conclusion about the site.

**8 of 8 consecutive fetches returned about 1.8 MB
carrying 67 listings.** No 403, no captcha, no drift between runs. eBay is one
of the more stable targets in this collection.

The referer is part of that. Arriving from a search result is the most ordinary
way a person reaches a category page, and it is in the profile that measured
clean.

### Browse pages work; search pages do not

Six attempts each, through rotating exits:

| Surface | Result |
|---|---|
| `/b/Laptops-Netbooks/175672` | 200, 67 listings, every attempt |
| `/sch/i.html?_nkw=...` | **1,986-byte 403, every attempt** |

A 403 that survives six different exits is a wall rather than luck, so this
Actor **refuses a search URL up front** with a message, instead of costing you
five retries to find out.

### Two traps in this page, and why the output is trustworthy

**The JSON-LD is a decoy.** The page publishes an `ItemList`, which is the
obvious thing to parse. It holds **8 entries against 67 real listings**: it
describes a promoted carousel, not the grid. Parsing it returns a confident,
tiny, unrepresentative slice — which is worse than returning nothing, because
nothing about the result looks wrong. This Actor reads the grid markup instead.

**The page opens with catalogue tiles that are not listings.** They carry a
title and no item link, no price and no condition. Anchoring the parse on the
title element looks tidier and produces rows of nulls at the top of every run.
Anchoring on each listing's own `/itm/<id>` link fixes it, and the tests pin
that so it stays fixed.

There is also a third detail worth stating: eBay ships **three** price classes,
not one — `displayprice`, `newprice` and `usedprice`. On a sample page those
were 81, 15 and 11 respectively. Matching only the first silently drops a
quarter of the grid to a null price.

### Discounts are only reported when real

`on_sale` is set only when a strikethrough price exists **and is above** the
current price. `discount_percent` prefers eBay's own stated percentage and falls
back to computing it from the pair.

A strikethrough that is not higher is a formatting artefact, and treating it as
a markdown would invent discounts. When it does not qualify, `was_price` comes
back `null` rather than being filled in anyway.

On a test run of 120 laptops, 51 carried a genuine markdown, with eBay's stated
percentages ranging from 12% to 43%.

### Fields

| Field | What it is |
|---|---|
| `item_id` | eBay's own id. Stable, join runs on it |
| `title` | Listing title |
| `price`, `currency` | Current price |
| `was_price` | Strikethrough price, only on a real markdown |
| `on_sale`, `discount_percent` | Whether it is discounted and by how much |
| `condition` | eBay Refurbished, Open Box, Pre-Owned and similar |
| `shipping_cost` | When the card states one |
| `image` | Listing image |
| `url` | Listing page |

### Input reference

| Field | Type | Default |
|---|---|---|
| `browse_url` | eBay browse page | Laptops & Netbooks |
| `limit` | 1-2000 | 100 |
| `retries` | 1-8 | 4 |

Pagination uses eBay's own `_pgn` parameter.

### Typical uses

- **Price and discount monitoring.** Run a category on a schedule and join
  snapshots on `item_id`. `discount_percent` shows how hard a category is being
  marked down.
- **Refurbished market research.** `condition` separates eBay Refurbished,
  Open Box and Pre-Owned, which is the split that matters when pricing
  second-hand stock.
- **Competitive pricing.** Price distribution within a browse category, with
  shipping cost included where stated.
- **Deal hunting.** Filter on `on_sale` and sort by `discount_percent`.

### Notes on behaviour

Pages are paced with a short
randomised gap. A page that loads but carries no listing cards is reported as
`no_listings` rather than returned as an empty result.

Prices are from ebay.com in US dollars. Other eBay domains use a different
markup in places, so treat this Actor as US-site only until measured elsewhere.

Shipping cost is captured only when the card states a figure. Many listings show
"Free shipping" or nothing at all, and those come back `null` rather than zero,
because free and unstated are different facts.

# Actor input Schema

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

An eBay browse page, for example https://www.ebay.com/b/Laptops-Netbooks/175672. Search URLs are refused: eBay answers 403 to those from every exit tested.

## `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.

## Actor input object example

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

# 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/fVuuDcUwI605dpK5j/openapi.json
