# eBay Scraper (`mlg14/ebay-search-scraper`) Actor

Search active and sold eBay listings by keyword or URL. Export prices, conditions, shipping, seller details, images, and optional product details.

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

## Pricing

$4.00 / 1,000 results

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

Search eBay listings by keyword or search URL and export public listing data to JSON, CSV, or Excel. The intended output covers active and sold search results, displayed prices, conditions, shipping, seller summaries, images, and optional product-page details.

**Development status:** The required live test has not passed. Recent runs received eBay's error page on every search request and produced zero rows. The input and output contract below describes the implemented behavior, but this actor should not be used for production collection until a live test returns the required listings. It has not been published.

### What data can you extract from eBay?

Each dataset row represents one listing. The actor uses the public search result card as its main source. When item-page details are enabled for active listings, it also reads public structured product data from that page. Empty fields are represented by `null` when the page does not show a value or the detail request fails.

| Field | Description | Example |
| --- | --- | --- |
| `itemId` | Numeric listing identifier | `178506808902` |
| `url` | Direct item link on the selected marketplace | `https://www.ebay.com/itm/178506808902` |
| `title` | Listing title displayed in search | `AULA F75 75% Wireless Mechanical Gaming Keyboard` |
| `price` | Price as displayed on the result card | `$45.89` |
| `priceValue` | Numeric first price in the displayed range | `45.89` |
| `currency` | Currency inferred from the displayed price | `USD` |
| `originalPrice` | Crossed-out price text, when present | `$82.99` |
| `originalPriceValue` | Numeric crossed-out price | `82.99` |
| `condition` | Displayed condition label | `Open Box` |
| `shipping` | Delivery or shipping text from the card | `Free delivery in 2-4 days` |
| `itemLocation` | Displayed item location | `United States` |
| `availability` | Search mode for this row | `ACTIVE` |
| `image` | Primary listing image URL | `https://i.ebayimg.com/images/...` |
| `marketplace` | Regional domain used for the request | `ebay.com` |
| `sellerName` | Public seller username, when shown | `bullseye_deals` |
| `sellerFeedback` | Feedback summary displayed beside the seller | `99.5% positive (1.5M)` |
| `buyingFormat` | Purchase format text from the card | `or Best Offer` |
| `bidCount` | Bid count for a displayed auction | `7` |
| `timeLeft` | Time remaining when shown | `3d 4h left` |
| `sold` | Whether the row came from sold search | `false` |
| `soldDate` | Displayed sale date, when eBay supplies it | `Sold Sep 25, 2026` |
| `soldCount` | Displayed count of units sold | `29` |
| `returnsAccepted` | Whether the card explicitly states returns are accepted | `true` |
| `searchQuery` | Keyword or input URL that produced the row | `mechanical keyboard` |
| `scrapedAt` | UTC collection timestamp | `2026-09-26T13:00:00+00:00` |
| `brand` | Brand from product-page structured data | `AULA` |
| `color` | Color from product-page structured data | `Cream` |
| `galleryImages` | Product-page gallery image URLs | `[...]` |
| `listingStatus` | Product-page availability status | `ACTIVE` |

Search cards vary by category, marketplace, purchase format, and seller. A fixed schema makes exports easier to compare across runs, while the null values preserve the difference between a missing page field and a real numeric zero. `soldCount` is a quantity label shown on some active listings; it is not a sold-comps count for an entire query. `sold` identifies the search mode that produced a row. It does not verify a transaction independently of eBay's result page.

### How to scrape eBay

1. Enter a keyword in `searchQuery`, or paste a public eBay search URL into `startUrls`. An individual `/itm/` item URL is also accepted.
2. Select a regional marketplace for keyword searches. Start URLs keep their own regional domain.
3. Choose active results, sold results, or both. Add price, condition, sorting, seller, category, shipping, returns, or purchase-format filters if needed.
4. Set `maxItems` and `maxPagesPerSearch` to bound the number of listings and pages. Leave product-page details off for a search-card export; turn them on only when brand, color, and gallery images matter.
5. Run the actor, inspect its run status and dataset, and export the dataset in the format you need. A zero-row failed run is an access failure to investigate, not evidence that the market has no matching listings.

The actor stops after the requested total number of rows or after the configured number of pages per source and mode. It removes duplicate item IDs within each mode. The same item may appear once as active and once as sold if the two searches expose it under both states. When `mode` is `both`, `maxItems` applies to the combined dataset rather than separately to active and sold results.

### Input

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `searchQuery` | string | empty | Keyword search. Can be used alongside start URLs. |
| `startUrls` | URL list | empty | Public search or item URLs on supported regional domains. |
| `marketplace` | string | `ebay.com` | Regional domain for keyword searches. |
| `mode` | string | `active` | `active`, `sold`, or `both`. |
| `condition` | string | `any` | Keyword-search condition filter. |
| `minPrice` | number | empty | Minimum item price in the marketplace currency. |
| `maxPrice` | number | empty | Maximum item price in the marketplace currency. |
| `sortBy` | string | `best_match` | Best match, ending soon, newest, lowest price, or highest price. |
| `listingType` | string | `all` | Auction, Buy It Now, Best Offer, or all formats. |
| `freeShipping` | boolean | `false` | Request free-shipping listings for keyword searches. |
| `returnsAccepted` | boolean | `false` | Request listings with accepted returns. |
| `seller` | string | empty | Public seller username for a keyword search. |
| `category` | string | empty | Numeric category ID for a keyword search. |
| `detailedItems` | boolean | `false` | Attempt an extra item-page request for each active listing. |
| `maxItems` | integer | `100` | Total row cap across all sources and modes; zero removes the cap. |
| `maxPagesPerSearch` | integer | `10` | Page cap per input search and mode, up to 50. |

A keyword example for a bounded active-listing export:

```json
{
  "searchQuery": "mechanical keyboard",
  "marketplace": "ebay.com",
  "mode": "active",
  "condition": "used",
  "minPrice": 20,
  "maxPrice": 100,
  "sortBy": "price_asc",
  "maxItems": 100,
  "maxPagesPerSearch": 3,
  "detailedItems": false
}
```

To preserve a filter combination you already set on the website, paste the search URL into `startUrls`. Filters in the URL remain attached to that search. The named filters above are applied to keyword searches, not overlaid on pasted search URLs. An item URL creates a single product row from public structured data when that page is reachable. Store pages, seller profile pages, feedback pages, and category browse pages are outside this build's accepted URL types. For seller inventory, use a keyword search with the `seller` input or a public `/sch/` URL containing the seller filter.

### Output example

No row from a successful golden run is available. The required golden test produced zero rows because the site returned an error page. The following JSON shows the **expected mapping of a real public search card observed during source discovery**. It is an illustration of the schema, not a verified actor output item; live prices and listing state will change.

```json
{
  "itemId": "178506808902",
  "url": "https://www.ebay.com/itm/178506808902",
  "title": "AULA F75 75% Wireless Mechanical Gaming Keyboard-4000m Bat Creamy HotSwap",
  "price": "$45.89",
  "priceValue": 45.89,
  "currency": "USD",
  "originalPrice": "$82.99",
  "originalPriceValue": 82.99,
  "condition": "Open Box",
  "shipping": "Free delivery in 2-4 days",
  "itemLocation": "United States",
  "availability": "ACTIVE",
  "marketplace": "ebay.com",
  "sellerName": "bullseye_deals",
  "sellerFeedback": "99.5% positive (1.5M)",
  "sold": false,
  "soldDate": null,
  "returnsAccepted": true,
  "brand": null,
  "galleryImages": null
}
```

The full dataset schema includes additional fields such as the image URL, collection timestamp, buying format, bid count, and time remaining. Optional product-page fields are null in fast mode. A direct item URL can have fields populated from product structured data while search-card-only fields remain null.

### Use cases

- **Resale price research:** Compare the prices and visible conditions of active offers for a specific model, size, or configuration. Use sold search to gather the site's displayed sold comps when access is available.
- **Inventory monitoring:** Schedule the same query, keep item IDs in your own storage, and identify newly visible listings or listings no longer returned. The actor does not maintain monitoring state itself.
- **Seller assortment checks:** Apply a seller username to a keyword search and compare titles, prices, shipping, and condition within the visible search results.
- **Shipping comparison:** Examine item price beside the delivery text. The `priceValue` field is the listing price, not a guaranteed total landed cost.
- **Category research:** Restrict a keyword search to a category ID and export a bounded sample to study naming, condition mix, price bands, and purchase formats.
- **Cross-marketplace research:** Repeat equivalent keywords on regional domains and retain `marketplace` and `currency` in downstream analysis. Translation, conversion, and regional availability require separate interpretation.

Results are snapshots of public listing pages. Price, quantity, seller feedback, shipping estimates, and ranking can change. For a defensible time series, retain `scrapedAt` and the input query for every export, and compare by `itemId` plus marketplace rather than by title alone.

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

The configured pay-per-result price for this build is **$4.00 per 1,000 dataset rows**, or $0.004 per row. A 100-row export would cost $0.40 in result charges; 500 rows would cost $2.00; 1,000 rows would cost $4.00. These examples describe row charges only. Platform usage and residential traffic may add cost, especially when product-page details cause extra requests. A failed run with zero output rows does not deliver the promised data, even if it consumes platform resources.

Set a modest `maxItems` while validating a query. A higher cap does not create more matching listings; it allows the actor to visit more pages and collect more distinct cards if the site supplies them. `detailedItems` adds an item-page request for each active row and can be much slower and more expensive than a result-card-only export. Sold rows are not enriched by item-page requests in this version.

### Tips for best results

Use specific model identifiers, sizes, and condition filters to reduce unrelated matches. For example, a product family alone may include accessories, compatible parts, and bundles; a model number plus a category filter is more focused. Inspect the first few rows before scaling a run. Title matching is governed by the site's search behavior; this actor does not impose a stricter title requirement after fetching results.

For price comparisons, keep one marketplace and one currency per analysis or convert explicitly downstream. A currency symbol on a result card is parsed into `currency`, and regional formatting can vary. `priceValue` is the first displayed numeric price when the card contains a range. Shipping may be free, a separate amount, an estimate, or absent. Review the raw `price` and `shipping` strings alongside numeric prices before computing final totals.

A page can contain sponsored cards, ordinary listings, and widgets. The actor ignores cards without a valid numeric listing ID and a real title, then deduplicates the remaining items. The page cap applies per search chain. If a broad search reaches the cap before `maxItems`, narrow the query or add category and price slices, then combine the exports by listing ID. The actor does not automatically subdivide very large searches.

### Limits

**The current blocker is access:** the golden test failed with zero items. The actor's own test runs received a short eBay error page on search requests, including after address rotation and browser attempts. A separate source-discovery run did obtain full US and German search pages and one item page, so the parser has a concrete page shape to target, but that does not establish reliable production access. Do not interpret this build as a working scraper until a golden run passes.

The source is server-rendered search HTML. The public page did not expose an accessible guest search JSON route in the tested probes. The HTML layout can change and make selectors stale. Product-page details depend on a separate page and its structured product block; an inaccessible item page leaves those fields null. Seller username and feedback appear on some cards but not all. Sold date may be absent even when sold search returns a card. Auction fields only appear on appropriate listings.

Search results are limited by what eBay shows to an anonymous visitor in the selected region. Country, delivery destination, cookies, marketplace, and ranking can change prices or item visibility. The actor rotates request sessions and can escalate proxy type, but the recent test showed that neither route guaranteed access. The actor does not log in, extract private account data, read buyer feedback, calculate sell-through rate, or maintain persistent price-monitoring state.

### Automated workflows

A workflow can request: “Collect up to 100 used mechanical keyboards between 20 and 100 in the US marketplace, sorted by lowest price, and return the title, price, condition, shipping, seller, and URL.” Another can request: “Collect sold results for a specific model from a German search URL, keep the item ID and displayed sold date, and export the rows for comparison.” These are input recipes, not a claim that the current build can complete them while eBay returns error pages.

Structured input and a fixed dataset schema make it possible to wire runs into scheduled research once access is resolved. Keep failure detection in the consuming workflow: check both the run status and the row count before updating a report. A completed schedule with no data should trigger investigation rather than overwrite a previous valid snapshot with empty findings.

### FAQ

**Is it legal to collect these listings?** The actor is designed for public listing pages. Follow the site's terms, applicable law, and privacy requirements. Do not use it to collect private account data or misuse personal information.

**Do I need to supply a proxy?** This build selects remote network routes internally. The latest golden runs were blocked despite those routes. A different proxy address is not a guaranteed fix, and residential traffic can cost more.

**How fast is a run?** Fast mode uses search pages; detail mode adds one item-page request per active row. Runtime also depends on retries and site response. No successful production-speed figure is available because the golden test failed.

**Can I schedule it or send results elsewhere?** The platform supports schedules, dataset exports, webhooks, and downstream integrations. Validate a successful run before using its results in an automated report.

**Can I export to a spreadsheet?** Dataset exports can be downloaded in CSV or Excel-compatible formats. Nested `galleryImages` may need flattening for a tabular spreadsheet.

**Why is a field empty?** The source card may omit it, the listing may not use that format, or a product page may be unavailable. Null does not mean zero. Inspect the row's URL and the source mode before filling missing values.

**Does sold search prove a sale price?** It reflects what the public sold-results page displays. It is not an independent transaction record, and a missing sold date should not be guessed.

### Integrations

Once the access issue is resolved and a live test passes, dataset rows can be retrieved through the platform API, exported as JSON or CSV, sent by webhook, or connected to a spreadsheet and an automation service. Use `itemId`, `marketplace`, and `scrapedAt` as stable comparison keys. Reject failed or zero-row runs before propagating data to a dashboard or pricing process.

### Support

Open an issue on the Issues tab; we reply within 24h and add fields on request. Include the input, run ID, marketplace, and whether the run returned an error page or ordinary search results. The current known issue is the blocking error page in the golden test.

# Actor input Schema

## `searchQuery` (type: `string`):

Keywords to search, such as mechanical keyboard. Combine with the marketplace and filters below.

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

Optional eBay search URLs or individual item URLs. Regional eBay domains are supported.

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

Regional eBay site for keyword searches. A start URL uses its own domain.

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

Collect active listings, sold listings, or both. Sold listings are shown as eBay reports them on search pages.

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

Condition filter for keyword searches. Start URLs retain their own filters.

## `minPrice` (type: `number`):

Minimum price in the marketplace currency for keyword searches.

## `maxPrice` (type: `number`):

Maximum price in the marketplace currency for keyword searches.

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

Result order for keyword searches.

## `listingType` (type: `string`):

Auction, Buy It Now, or Best Offer filter for keyword searches.

## `freeShipping` (type: `boolean`):

Request listings marked for free shipping in keyword searches.

## `returnsAccepted` (type: `boolean`):

Request listings with accepted returns in keyword searches.

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

Restrict a keyword search to one public seller username.

## `category` (type: `string`):

Restrict a keyword search to a numeric eBay category ID.

## `detailedItems` (type: `boolean`):

Visit each active item page to add brand, color, gallery images, and listing status when its structured product data is available. Slower and uses more requests.

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

Total cap across all searches and modes. Set 0 for no cap.

## `maxPagesPerSearch` (type: `integer`):

Maximum search result pages to request for each source and listing mode.

## Actor input object example

```json
{
  "searchQuery": "mechanical keyboard",
  "startUrls": [
    {
      "url": "https://www.ebay.com/sch/i.html?_nkw=mechanical+keyboard"
    }
  ],
  "marketplace": "ebay.com",
  "mode": "active",
  "condition": "any",
  "sortBy": "best_match",
  "listingType": "all",
  "freeShipping": false,
  "returnsAccepted": false,
  "detailedItems": false,
  "maxItems": 100,
  "maxPagesPerSearch": 10
}
```

# Actor output Schema

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

Collected listings in the default dataset.

# 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 = {
    "searchQuery": "mechanical keyboard",
    "startUrls": [
        {
            "url": "https://www.ebay.com/sch/i.html?_nkw=mechanical+keyboard"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("mlg14/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 = {
    "searchQuery": "mechanical keyboard",
    "startUrls": [{ "url": "https://www.ebay.com/sch/i.html?_nkw=mechanical+keyboard" }],
}

# Run the Actor and wait for it to finish
run = client.actor("mlg14/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 '{
  "searchQuery": "mechanical keyboard",
  "startUrls": [
    {
      "url": "https://www.ebay.com/sch/i.html?_nkw=mechanical+keyboard"
    }
  ]
}' |
apify call mlg14/ebay-search-scraper --silent --output-dataset

```

## MCP server setup

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