# eBay Sold Items by Image & Keyword (`scrapelabmax/ebay-sold-image-scraper`) Actor

Search eBay sold listings by PHOTO or by keyword. Upload an image and CLIP visual similarity finds matching sold items. Returns USD prices, sold dates, condition, and seller info, plus a median/avg price summary. Ideal for pricing research, reselling, and collectibles valuation.

- **URL**: https://apify.com/scrapelabmax/ebay-sold-image-scraper.md
- **Developed by:** [Scrapelab Max](https://apify.com/scrapelabmax) (community)
- **Categories:** E-commerce, AI, Automation
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.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 Sold Items by Image & Keyword

**Snap a photo, get sold comps.** Give this actor a picture of an item (or a
keyword, or both) and it returns *actually-sold* eBay listings — not active
asking prices — ranked by how visually similar each sold item's photo is to
yours, using a CLIP vision model baked into the actor image. Resellers use it
to answer "what did this actually sell for?" in seconds, without opening a
browser or manually filtering eBay's "Sold" search.

### What it does

- Searches eBay's sold/completed listings (`LH_Sold=1&LH_Complete=1`) by
  keyword, by a reference image via eBay's reverse-image search, or both.
- When an image is provided, every candidate's thumbnail is embedded with a
  CLIP vision model (`Xenova/clip-vit-base-patch32`) and scored against your
  image by cosine similarity; only items at or above `similarityThreshold`
  are returned, best match first.
- Keyword-only runs stream matching sold listings as they're found, no
  scoring step.
- Every run also pushes one free `type: "summary"` record with price
  statistics (median, average, min/max, auction vs. buy-it-now counts, sold
  date range) for whatever it returned.
- `maxItems` is a hard cap on results — and on cost.

### Input examples

**Keyword only** — every matching sold listing, no image scoring:

```json
{
    "keyword": "rtx 3080",
    "maxItems": 200
}
```

**Image only** — sold comps for a photo, ranked by CLIP similarity:

```json
{
    "imageUrl": "https://i.ebayimg.com/images/g/abcAAOSwexampleId/s-l300.jpg",
    "maxItems": 100
}
```

(`imageBase64` also works in place of `imageUrl` — base64-encode the image,
up to 3 MB — for uploading a photo directly instead of linking one.)

**Image + keyword** — narrows discovery with the keyword, still ranks by
image similarity; this is the recommended combination when you know roughly
what the item is:

```json
{
    "imageUrl": "https://i.ebayimg.com/images/g/abcAAOSwexampleId/s-l300.jpg",
    "keyword": "nintendo switch oled console",
    "similarityThreshold": 0.65,
    "maxItems": 150
}
```

`similarityThreshold` (0–1, default **0.65**) is the minimum CLIP cosine
score a sold item's photo must reach to be kept. Other inputs —
`minPrice`, `maxPrice`, `condition` (`any` / `new` / `used` / `refurbished`),
`categoryId`, `daysBack` (1–90, default 90), `marketplace` (`ebay.com` only
in v1), `proxyConfiguration` — are documented inline in the input form.

### Output sample

One `soldItem` record per matched listing:

```json
{
    "type": "soldItem",
    "itemId": "126789012345",
    "title": "Nintendo Switch OLED White Console - Excellent Condition",
    "url": "https://www.ebay.com/itm/126789012345",
    "soldPrice": 249.99,
    "currency": "USD",
    "totalPrice": 259.99,
    "soldDate": "2026-08-03",
    "condition": "Pre-Owned",
    "listingType": "buyItNow",
    "bidsCount": null,
    "sellerUsername": null,
    "sellerFeedback": null,
    "shippingPrice": 10.0,
    "imageUrl": "https://i.ebayimg.com/images/g/abcAAOSwexampleId/s-l300.jpg",
    "similarityScore": 0.87,
    "matchedBy": ["image", "keyword"]
}
```

Plus exactly one free `summary` record per run:

```json
{
    "type": "summary",
    "query": { "keyword": "nintendo switch oled console", "imageProvided": true },
    "resultCount": 42,
    "medianPrice": 245.0,
    "avgPrice": 251.32,
    "minPrice": 180.0,
    "maxPrice": 310.0,
    "auctionCount": 6,
    "buyItNowCount": 36,
    "earliestSoldDate": "2026-05-12",
    "latestSoldDate": "2026-08-09",
    "droppedBelowThreshold": 58,
    "droppedOverBudget": 0,
    "droppedByFilters": 3
}
```

Use the **Sold items** dataset view for a table of just the sold listings
(with inline thumbnails); the summary record isn't part of that view's
columns and shows up as a blank row there — see Limitations.

`sellerUsername` and `sellerFeedback` are always `null`: eBay's sold search
results cards don't expose seller identity, only the item detail page does,
and this actor doesn't fetch detail pages.

### Pricing

Pay-per-event:

| Event | Price | When it's charged |
|---|---|---|
| Sold listing | **$1.50 / 1,000 items** | Keyword-only match pushed to the dataset |
| Similarity-scored sold listing | **$4.50 / 1,000 items** | Image-matched item pushed to the dataset (CLIP-scored) |
| Run summary | Free | Always, once per run |

You are only charged for items actually pushed to the dataset — `maxItems`
is a hard ceiling on both result count and spend. See **Limitations** below
for scored-run cost mechanics that aren't captured by these per-item prices.

### Limitations — verified

- **~90-day sold window.** eBay's sold/completed listings search only goes
  back roughly 90 days; there's no way to query further into the past.
- **`daysBack` filters after parsing, not before crawling.** Listings older
  than `daysBack` are dropped once parsed, but the search pages that contain
  them are still crawled first — a tight `daysBack` does not reduce crawl
  volume or proxy traffic, only which parsed items make it into the dataset.
- **Best Offer amounts are masked by eBay.** For `bestOffer` listings, the
  price on the sold-search card is the listed asking price, not the
  negotiated accepted price — eBay does not expose the actual sale amount
  for Best-Offer sales on this page.
- **`ebay.com` only in v1.** Other eBay marketplaces aren't supported yet.
- **The image-search endpoint is unofficial** (reverse-engineered from
  eBay's front-end bundle, not a published API) **and can change without
  notice.** If it breaks: an image+keyword run automatically falls back to
  keyword discovery with CLIP scoring on the results (you still get scored
  matches, just found via keyword instead of eBay's visual search); an
  image-only run has no keyword to fall back to and fails with
  `EBAY_IMAGE_SEARCH_UNAVAILABLE`.
- **Scored runs (image provided) download up to ~1,000 candidate
  thumbnails before any billable result is pushed.** The candidate budget is
  `min(1000, max(3 × maxItems, 150))` — every one of those thumbnails is
  downloaded and run through CLIP before results are ranked and the top
  matches are pushed and charged. Those thumbnail downloads go directly to
  eBay's image CDN (`i.ebayimg.com`), **not** over the residential proxy, so
  they add run time and CDN-side rate-limit exposure rather than proxy cost —
  but they are still work the run pays for even though only the final,
  above-threshold items are billed as dataset events.
- **CLIP cold start is ~2–5 seconds** even though the model is baked into
  the Docker image (no network download at runtime) — it still has to load
  into memory on the first image-scoring call of a run.
- **2048 MB memory is recommended** (and set as this actor's default) to
  keep CLIP inference and the crawler comfortably inside their memory
  budget; lower memory settings may work for keyword-only runs but are more
  likely to be tight on image-scored runs.

# Actor input Schema

## `imageUrl` (type: `string`):

Reference image to match sold listings against — upload a file or paste an image URL. JPEG, PNG, HEIC (iPhone), WebP, GIF, TIFF, and AVIF up to 10 MB. Provide this, imageBase64, or keyword (at least one is required).

## `imageBase64` (type: `string`):

Base64-encoded reference image, up to 3 MB. Intended for API integrations that send the image inline; in this form, use the upload field above instead.

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

Search keyword(s), e.g. "rtx 3080". Provide this, imageUrl, or imageBase64 (at least one is required).

## `similarityThreshold` (type: `number`):

Minimum CLIP image-similarity score (0-1) for a listing to be considered a match when an image is provided.

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

Exclude sold listings priced below this amount.

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

Exclude sold listings priced above this amount.

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

Filter sold listings by item condition.

## `categoryId` (type: `string`):

Restrict the search to a specific eBay category ID.

## `daysBack` (type: `integer`):

How many days back (from today) to include sold listings for.

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

eBay marketplace to search.

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

Maximum number of sold items to return. Required — acts as a hard cost cap.

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

Apify Proxy configuration used for requests to eBay. Defaults to US residential exits: eBay converts sold prices to the viewer's local currency by geo-IP, and a non-US exit makes prices unusable (they are dropped rather than mis-reported).

## Actor input object example

```json
{
  "similarityThreshold": 0.65,
  "condition": "any",
  "daysBack": 90,
  "marketplace": "ebay.com",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}
```

# Actor output Schema

## `soldItems` (type: `string`):

No description

## `allRecords` (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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapelabmax/ebay-sold-image-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 = {}

# Run the Actor and wait for it to finish
run = client.actor("scrapelabmax/ebay-sold-image-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 '{}' |
apify call scrapelabmax/ebay-sold-image-scraper --silent --output-dataset

```

## MCP server setup

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