# eBay Search Results Scraper - Prices & Sold Data (`valev-lab/ebay-search-scraper`) Actor

Turn eBay searches into clean JSON: total price (item + shipping), condition, seller ratings, sold counts, images, search rank. Active or sold & completed listings across 8 marketplaces. Fast, no browser - built for price monitoring, repricing, and market research.

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

## Pricing

from $3.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/platform/actors/running/actors-in-store#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 Search Results Scraper - Prices & Sold Data

Turn any eBay search into clean, structured data. Give the Actor keywords (or paste eBay search URLs) and it returns every listing on the results pages - title, price, shipping, condition, seller rating, sold counts, and more - ready for price monitoring, market research, repricing, and dropshipping.

**Search results only.** The Actor reads each results page in a single request and never opens product, seller, or any secondary pages. That keeps it fast, cheap, and gentle - while still extracting 30+ fields from every listing card.

***

### Try it in 30 seconds

1. **Enter a keyword** - e.g. `iphone 15` (or paste any eBay search URL).
2. **Pick filters** *(optional)* - marketplace, condition, price range, sold & completed.
3. **Start** - results land in your dataset as JSON, CSV, or Excel.

```json
{ "searchQueries": ["iphone 15"], "maxProductsPerSearch": 20 }
```

Every listing comes back like this (30+ fields in total):

```json
{
  "title": "Apple iPhone 15 128GB Unlocked",
  "totalPrice": 412.49,
  "condition": "Pre-Owned",
  "soldCountValue": 1200,
  "sellerFeedbackPercentValue": 99.3,
  "positionOnPage": 3,
  "url": "https://www.ebay.com/itm/..."
}
```

A test run like the one above costs about **$0.06** - full pricing below.

***

### Why this scraper

- **60 listings per request.** Uses eBay's 60-per-page layout, so a page of data costs one request.
- **Rich, computed fields out of the box.** Beyond raw text, you get numeric `totalPrice` (item + shipping), parsed `discountPercent`, `soldCountValue`, seller feedback as numbers, currency codes, price ranges, and rank tracking (`pageNumber`, `positionOnPage`) - all derived from the same HTML, no extra requests.
- **Native eBay filters.** Sort, condition, listing type, price range, free-shipping-only, and seller location are applied as URL parameters - zero extra requests.
- **8 marketplaces.** `ebay.com`, `.co.uk`, `.de`, `.fr`, `.it`, `.es`, `.com.au`, `.ca`. Currency is detected per listing.
- **Sold / completed listings.** Set `listingStatus: "sold"` to analyze real sale prices - no URL crafting needed.
- **Built to survive.** Residential proxy support, session rotation, automatic retries with backoff, block/captcha detection, and de-duplication across queries and pages.

***

### Who it's for

- **Resellers & repricers** monitoring competitor prices and `totalPrice`.
- **Market researchers** measuring demand via `soldCount` and price distributions.
- **Dropshippers** sourcing products and tracking availability.
- **Analysts** studying sold/completed listings for real market value.

***

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

Pricing is **pay-per-event**: a flat **$0.001 per run start** plus **$3.00 per 1,000 results** ($0.003 per listing). All platform costs - compute, residential proxies, storage - are **included**. No subscription, no separate usage bill.

| Scrape | Listings | Cost |
|---|---|---|
| Test run (1 keyword, 10 products) | 10 | ~$0.03 |
| 1 keyword, 2 pages | ~120 | ~$0.36 |
| 5 keywords, 3 pages each | ~900 | ~$2.70 |
| Full search (20 pages, eBay's cap) | ~1,200 | ~$3.60 |

The Actor honors your **maximum cost per run**: set a cap in the run options and it stops cleanly the moment the cap is reached - you are never charged past it.

***

### Input

Configure from the Apify Console UI or via the API. All fields are optional except that you must provide **`searchQueries` or `startUrls`**.

| Field | Type | Default | Description |
|---|---|---|---|
| `searchQueries` | string\[] | - | Keywords; each runs its own search. Required unless `startUrls` is provided. |
| `startUrls` | array | `[]` | Direct eBay search URLs (category pages, sold listings, etc.). Your parameters are preserved; the Actor forces 60/page and paginates. |
| `marketplace` | enum | `ebay.com` | Which eBay domain to search (keyword searches only). |
| `maxProductsPerSearch` | integer | `100` | Cap per query/Start URL. `0` = unlimited. |
| `maxSearchPages` | integer | `5` | Max result pages per search (max 20; eBay caps results around 20 pages). |
| `sort` | enum | `best_match` | `best_match`, `ending_soonest`, `newly_listed`, `price_low`, `price_high`. |
| `condition` | enum\[] | `[]` | `new`, `open_box`, `certified_refurbished`, `seller_refurbished`, `used`, `for_parts`. Empty = all. |
| `listingType` | enum | `all` | `all`, `auction`, `buy_it_now`, `best_offer`. |
| `listingStatus` | enum | `active` | `active`, or `sold` for sold & completed listings (real sale prices). |
| `minPrice` / `maxPrice` | number | - | Price range in the marketplace currency (decimals allowed, e.g. `19.99`). |
| `freeShippingOnly` | boolean | `false` | Only listings with free shipping. |
| `sellerLocation` | enum | `all` | `all`, `domestic_only`, `worldwide`. |
| `deduplicateItems` | boolean | `true` | Skip item IDs already scraped in this run. |
| `proxyConfiguration` | proxy | Residential | Proxy settings. Residential strongly recommended. |
| `maxRequestRetries` | integer | `5` | Retries per blocked/failed request (1–10). |
| `debugLog` | boolean | `false` | Verbose logs + dump first page HTML to the key-value store. |

#### Example input

```json
{
  "searchQueries": ["mechanical keyboard", "ergonomic mouse"],
  "marketplace": "ebay.com",
  "maxProductsPerSearch": 120,
  "maxSearchPages": 3,
  "sort": "price_low",
  "condition": ["new", "open_box"],
  "freeShippingOnly": true,
  "proxyConfiguration": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"] }
}
```

***

### Output

Each dataset item is one listing. **Baseline fields** use the familiar names shared by popular eBay scrapers, so switching takes minutes; **enriched fields** are computed from the same HTML (no extra requests).

#### Baseline fields

| Field | Type | Notes |
|---|---|---|
| `type` | string | Always `"product"`. |
| `itemId` | string | eBay item ID. |
| `title` | string | Listing title. |
| `price` | number|null | First/lowest numeric price. |
| `priceString` | string|null | Raw price text. |
| `listPriceString` | string|null | Raw strikethrough/original price. |
| `shippingCost` | string|null | Raw shipping text. |
| `returnPolicy` | string|null | Raw returns text. |
| `coupon` | string|null | Coupon text, if shown. |
| `discountText` | string|null | Raw discount text (e.g. "40% off"). |
| `condition` | string|null | Condition text. |
| `listingType` | string|null | `"Buy It Now"` or `"Auction"`. |
| `bidCount` | number|null | Auction bids. |
| `timeLeft` | string|null | Auction time-left text. |
| `bestOffer` | boolean | Best Offer accepted. |
| `soldCount` | string|null | Raw sold text (e.g. "93+"). |
| `sellerName` | string|null | Seller username. |
| `sellerFeedbackPercent` | string|null | Raw (e.g. "99.3%"). |
| `sellerFeedbackCount` | string|null | Raw (e.g. "153.4K"). |
| `isSponsored` | boolean | Sponsored placement. |
| `thumbnail` | string|null | Thumbnail image URL. |
| `images` | string\[] | Image URLs available on the card. |
| `url` | string | Canonical listing URL (tracking stripped). |
| `scrapedAt` | string | ISO timestamp. |

#### Enriched fields (extra value)

| Field | Type | Notes |
|---|---|---|
| `currency` | string|null | ISO code inferred from the price / marketplace. |
| `priceMin` / `priceMax` | number|null | For range prices ("$100 to $200"). |
| `shippingCostValue` | number|null | Numeric shipping ("Free" → 0). |
| **`totalPrice`** | number|null | `price + shippingCostValue` - the key field for price monitoring. |
| `listPrice` | number|null | Numeric original price. |
| `discountPercent` | number|null | Parsed from text or computed from list vs. price. |
| `soldCountValue` | number|null | "93+" → 93, "1.2K" → 1200. |
| `soldCountIsMinimum` | boolean | True when the source used "+". |
| `soldDate` | string|null | Sale date on sold/completed listings (e.g. "Sold Jul 11, 2026"). |
| `sellerFeedbackPercentValue` | number|null | 99.3. |
| `sellerFeedbackCountValue` | number|null | "153.4K" → 153400. |
| `itemLocation` | string|null | Item location text. |
| `subtitle` | string|null | Secondary description line. |
| `isFreeShipping` | boolean|null | Derived from shipping text. |
| `isFreeReturns` | boolean|null | Derived from returns text. |
| `conditionCategory` | string|null | Normalized condition bucket across languages: `NEW`, `OPEN_BOX`, `REFURBISHED`, `USED`, `FOR_PARTS`, `OTHER`. |
| `imageLarge` | string|null | Higher-resolution image URL (thumbnail bumped to `s-l1600`). |
| `priceDropText` | string|null | Price-drop promo on the card (e.g. "NEW LOW PRICE"). |
| `availabilityText` | string|null | Low-stock / urgency text ("Last one", "Almost gone", "Letzter Artikel"). |
| `badges` | string\[] | Card badges (Top Rated Plus, eBay Refurbished, …). |
| `watchersText` | string|null | "24 watchers" text, when shown. |
| `listedDate` | string|null | Listed date text, when shown. |
| `searchQuery` | string | Which query/Start URL produced the item. |
| `pageNumber` | number | Result page (rank tracking). |
| `positionOnPage` | number | 1-based rank on that page. |
| `marketplace` | string | e.g. `ebay.com`. |

> Missing data is never fabricated: absent values are `null` (or `[]` / `false`).

#### Example output

```json
{
  "type": "product",
  "itemId": "266123456789",
  "title": "Mechanical Keyboard RGB 87-Key Hot-Swappable",
  "price": 39.99,
  "priceString": "$39.99",
  "shippingCost": "Free delivery",
  "condition": "Brand New",
  "listingType": "Buy It Now",
  "soldCount": "1.2K sold",
  "soldCountValue": 1200,
  "sellerName": "keebs_store",
  "sellerFeedbackPercent": "99.3%",
  "sellerFeedbackPercentValue": 99.3,
  "sellerFeedbackCountValue": 153400,
  "currency": "USD",
  "shippingCostValue": 0,
  "totalPrice": 39.99,
  "isFreeShipping": true,
  "badges": ["Top Rated Plus"],
  "url": "https://www.ebay.com/itm/266123456789",
  "searchQuery": "mechanical keyboard",
  "pageNumber": 1,
  "positionOnPage": 3,
  "marketplace": "ebay.com",
  "scrapedAt": "2026-07-10T12:00:00.000Z"
}
```

***

### Run via API

**Node.js** (`apify-client`):

```js
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: 'YOUR_TOKEN' });
const run = await client.actor('valev-lab/ebay-search-scraper').call({
    searchQueries: ['laptop'],
    maxProductsPerSearch: 100,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

**Python** (`apify-client`):

```python
from apify_client import ApifyClient
client = ApifyClient("YOUR_TOKEN")
run = client.actor("valev-lab/ebay-search-scraper").call(run_input={
    "searchQueries": ["laptop"],
    "maxProductsPerSearch": 100,
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)
```

**cURL**:

```bash
curl -X POST "https://api.apify.com/v2/acts/valev-lab~ebay-search-scraper/runs?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"searchQueries":["laptop"],"maxProductsPerSearch":100}'
```

***

### Scheduling & integrations

- **Schedule** recurring runs from the Console (**Schedules**) for daily price snapshots.
- **Integrations:** push results to Google Sheets, Slack, Zapier, Make, or your own webhook/API. The dataset exports to JSON, CSV, Excel, and XML.

***

### Recipes

**Sold / completed listings** - analyze real sale prices with the `listingStatus` filter:

```json
{ "searchQueries": ["iphone 13"], "listingStatus": "sold" }
```

(Equivalent Start URL also works: `https://www.ebay.com/sch/i.html?_nkw=iphone+13&LH_Sold=1&LH_Complete=1`.)

**Multi-marketplace** - run the same keyword across domains by using Start URLs from each domain, or run separate jobs per `marketplace`. Currency is detected per listing.

***

### Tips for best results

- **Start small.** Set `maxProductsPerSearch` to 10-20 while testing a new query or filter combo, then scale up once the results look right.
- **Use specific keywords.** `"apple airpods pro 2nd generation"` returns tighter, more relevant results than `"airpods"`.
- **Stack native filters.** Combine `condition`, `listingType`, `minPrice`/`maxPrice`, `freeShippingOnly`, and `sellerLocation` - they're all applied as URL parameters, so stacking them costs nothing extra.
- **Keep residential proxies on.** Datacenter IPs get blocked quickly; the default residential proxy group is what keeps runs reliable.
- **Batch queries in one run.** List multiple keywords in `searchQueries` instead of starting a separate run per keyword - cheaper and faster than many small jobs.
- **Cap pages to control cost.** `maxSearchPages` × 60 listings is the ceiling per query; lower it if you only need the first page or two of results.

***

### FAQ

**Some prices are `null`.** Auction-only cards, "see price in cart" items, or unusual formats can lack a parseable price. Raw text is still in `priceString`.

**Why ~60 items per page?** eBay serves up to 60 listings per results page; the Actor uses that maximum. eBay also caps search results at roughly 20 pages, so a single search yields up to ~1,200 listings.

**Duplicates?** With `deduplicateItems` (default on), an item ID scraped earlier in the run is skipped, so overlapping queries/pages don't repeat listings.

**Getting blocked / few results?** eBay commonly blocks datacenter IPs. Use **residential** proxies (default) and keep concurrency modest (the Actor already rate-limits itself).

**Does it open product pages?** No. Every field comes from the search-results HTML - one request per page.

***

### Legal

This Actor collects only **publicly available** search-results data. You are responsible for using the output in compliance with eBay's Terms of Service and applicable laws (including data-protection rules). Do not use it to collect personal data unlawfully.

# Actor input Schema

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

Keywords to search on eBay. Each entry runs its own search. Required unless you provide Start URLs below.

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

Direct eBay search-results URLs to scrape (e.g. category pages, or sold listings with LH\_Sold=1\&LH\_Complete=1). The Actor keeps all your parameters, forces 60 items per page, and paginates. Use this OR Search queries (you can use both).

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

Which eBay marketplace (domain) to search. Only used when building URLs from Search queries; Start URLs keep their own domain. Currency is read per listing.

## `maxProductsPerSearch` (type: `integer`):

Maximum number of listings to collect per search query / Start URL. Use 0 for no cap.

## `maxSearchPages` (type: `integer`):

Maximum result pages to open per search (60 listings per page). eBay itself caps search results at around 20 pages.

## `sort` (type: `string`):

How eBay should sort the results.

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

Restrict to one or more item conditions. Leave empty for all conditions.

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

Restrict to a buying format.

## `listingStatus` (type: `string`):

Active listings (default), or sold & completed listings — real sale prices for market analysis.

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

Lowest price to include, in the marketplace currency. Leave empty for no minimum.

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

Highest price to include, in the marketplace currency. Leave empty for no maximum.

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

Only include listings that offer free shipping (eBay LH\_FS filter).

## `sellerLocation` (type: `string`):

Restrict by where the item is located.

## `deduplicateItems` (type: `boolean`):

Skip listings whose eBay item ID was already scraped earlier in this run (across queries and pages).

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

Proxy settings. Residential proxies are strongly recommended — eBay blocks datacenter IPs. The proxy country is auto-set to match the marketplace (e.g. ebay.de → Germany) so prices and shipping are market-native; pick a Country here to override.

## `maxRequestRetries` (type: `integer`):

How many times a failed/blocked page request is retried (each retry uses a fresh session).

## `debugLog` (type: `boolean`):

Enable verbose logging, and dump the first fetched page's raw HTML to the key-value store (key DEBUG\_PAGE\_HTML) so you can capture a real fixture.

## Actor input object example

```json
{
  "searchQueries": [
    "laptop"
  ],
  "startUrls": [],
  "marketplace": "ebay.com",
  "maxProductsPerSearch": 100,
  "maxSearchPages": 5,
  "sort": "best_match",
  "condition": [],
  "listingType": "all",
  "listingStatus": "active",
  "freeShippingOnly": false,
  "sellerLocation": "all",
  "deduplicateItems": true,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  },
  "maxRequestRetries": 5,
  "debugLog": false
}
```

# Actor output Schema

## `listings` (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 = {
    "searchQueries": [
        "laptop"
    ]
};

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

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

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

```

## Python example

```python
from apify_client import ApifyClient

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

# Prepare the Actor input
run_input = { "searchQueries": ["laptop"] }

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

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

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

```

## CLI example

```bash
echo '{
  "searchQueries": [
    "laptop"
  ]
}' |
apify call valev-lab/ebay-search-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=valev-lab/ebay-search-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

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