# eBay Sold Listings Scraper (`tnodes/ebay-sold-scraper`) Actor

Scrape real sold prices, dates, conditions, and shipping from eBay's completed listings filter. Residential-proxy backed (Apify pool) with Chrome 124 TLS fingerprint impersonation — bypasses eBay's anti-bot.

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

## Pricing

from $2.00 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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 Sold Listings Scraper

Real sold prices, dates, conditions, and shipping from eBay's completed-listings
filter. Output schema mirrors the de-facto Apify convention
(`itemId`, `soldPrice`, `endedAt`, `condition`, `conditionId`, ...) so it
drop-in replaces `caffein.dev/ebay-sold-listings` and similar actors.

### How it works

eBay aggressively blocks scrapers via:

1. **IP fingerprinting** — datacenter IPs get 404/CAPTCHA
2. **TLS fingerprinting** — plain `requests`/`httpx` flagged by TLS handshake
3. **Cookie/session checks** — naked requests to `/sch/i.html?LH_Sold=1` get 403

This actor handles all three:

1. Apify's **residential proxy** pool (sticky session per actor run)
2. **`curl_cffi`** impersonates Chrome 124's TLS fingerprint exactly
3. Browser-realistic flow: visit homepage (warm cookie jar) → search with `Referer` header

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `keyword` | string | (required) | Search term — product name, MPN, model number |
| `condition` | enum | `any` | `any` / `new` / `used` |
| `daysToScrape` | int | 60 | Only listings sold within last N days (1–90; eBay caps at 90) |
| `count` | int | 50 | Max results to return (1–300) |

### Output fields

Each dataset record:

| Field | Type | Notes |
|---|---|---|
| `itemId` | string | eBay item identifier |
| `url` | string | Listing URL |
| `title` | string | Listing title (`Opens in a new tab` accessibility suffix stripped) |
| `soldPrice` | number | Realized sale price |
| `soldCurrency` | string | ISO code (`USD`, `GBP`, `EUR`, ...) |
| `endedAt` | string | ISO 8601 sale date |
| `condition` | string | Localized label (`Pre-Owned`, `Brand New`, ...) |
| `conditionId` | int | eBay numeric enum: 1000=New, 1500=Open Box, 3000=Used, 7000=For Parts |
| `shippingPrice` | number | null if unknown |
| `shippingCurrency` | string | ISO code |
| `shippingType` | enum | `free` / `paid` / `pickup` / `unknown` |
| `totalPrice` | number | `soldPrice + shippingPrice` |
| `scrapedAt` | string | ISO 8601 of when this run extracted the record |

### Limits + notes

- eBay's sold-listings filter only shows the **last 90 days**, regardless of `daysToScrape`.
- Bundle/lot listings (`"Lot of 5 ..."`) and for-parts listings are **NOT** filtered out here — downstream consumers should regex-filter if they want comparable single-unit prices.
- Seller info (`sellerUsername`, `sellerFeedbackScore`) is NOT in the new
  2026 `li.s-card` layout and isn't returned. Older actors that include
  these fields are scraping individual listing pages, which is 50x more
  expensive.

### Local development

```bash
apify run -p  # runs locally with the input from .actor/INPUT.json
```

### License

MIT.

# Actor input Schema

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

Search term (product name, MPN, or model number). eBay's fuzzy search applies — `Samsung PM883 1.92TB` matches PM883-series listings.

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

Restrict to one condition bucket. `any` returns both. eBay's New = factory-sealed + open-box + refurbished; Used = pre-owned + acceptable + for-parts.

## `daysToScrape` (type: `integer`):

Only return listings sold within the last N days. eBay caps sold-listing visibility at 90 days regardless.

## `count` (type: `integer`):

Maximum number of listings to return (after filtering).

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

eBay aggressively blocks datacenter IPs — keep the default (Apify Residential) for best results. You can also use your own proxies if you have a deal with another provider.

## Actor input object example

```json
{
  "keyword": "Samsung PM883 1.92TB",
  "condition": "any",
  "daysToScrape": 60,
  "count": 50,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}
```

# 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 = {
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "US"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("tnodes/ebay-sold-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 = { "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "US",
    } }

# Run the Actor and wait for it to finish
run = client.actor("tnodes/ebay-sold-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 '{
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}' |
apify call tnodes/ebay-sold-scraper --silent --output-dataset

```

## MCP server setup

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