# Ebay Email Scraper (`w3crawler/ebay-email-scraper`) Actor

Extract publicly visible seller emails from eBay search results for lead generation and e-commerce research. Only addresses displayed on public pages are returned.

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

## Pricing

from $2.99 / 1,000 emails

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?

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 Email Scraper

eBay Email Scraper is a public eBay research Actor. It searches public eBay result pages or inspects explicit item pages and returns seller email addresses only when the address is visibly published in the fetched public HTML. It does not log in, use private APIs, bypass access controls, or infer hidden contact data.

Actor page: [eBay Email Scraper on Apify](https://console.apify.com/actors/ORn6S9ye40i2eLDpT)

#### Value and use cases

Use it for public seller-contact research, marketplace catalog research, and e-commerce lead qualification where the seller intentionally publishes a business email. The output records the public page and extraction method so downstream users can review provenance. Public email addresses can be personal data; use them only for a legitimate purpose and follow eBay terms, privacy law, and anti-spam rules.

#### What the Actor does not promise

The Actor does not discover every seller email, reveal private contact data, or guarantee that a listing contains an email. It only reports non-eBay email addresses found in bounded public HTML. A blocked page, an item without a public email, or an email excluded by `customDomains` produces a diagnostic or no normal row rather than a guessed address.

### Input

The input is a JSON object. Unknown top-level fields are rejected. Omitted fields use the defaults below.

| Field                  | Type and limits                                                                                           | Default       | Behavior                                                                                   |
| ---------------------- | --------------------------------------------------------------------------------------------------------- | ------------- | ------------------------------------------------------------------------------------------ |
| `startUrls`            | Array of at most 50 objects, each with an eBay HTTP(S) `/itm/` `url`                                      | `[]`          | Explicit item pages inspected first, in input order. Duplicate URLs are removed.           |
| `keywords`             | Array of at most 10 non-empty strings, each at most 200 characters                                        | `[]`          | Search terms run after `startUrls`, in input order. Duplicate terms are removed.           |
| `market`               | `us`, `uk`, `de`, `fr`, `it`, `es`, `ca`, or `au`                                                         | `us`          | Selects the eBay host for keyword searches.                                                |
| `location`             | Optional non-empty string, at most 160 characters                                                         | omitted       | Passed to eBay search as the public location parameter.                                    |
| `customDomains`        | Array of at most 50 domains, such as `outlook.com` or `@outlook.com`                                      | `[]`          | Retains only emails whose normalized domain matches one of these domains.                  |
| `maxResults`           | Safe integer from 1 to 500                                                                                | `100`         | Global cap on normal email rows across explicit URLs and searches.                         |
| `maxPages`             | Safe integer from 1 to 10                                                                                 | `3`           | Maximum result pages fetched per keyword.                                                  |
| `maxListingsPerSearch` | Safe integer from 1 to 50                                                                                 | `10`          | Maximum listing cards inspected per keyword; it does not override the global `maxResults`. |
| `timeoutMs`            | Safe integer from 5,000 to 120,000                                                                        | `30000`       | Per-request timeout.                                                                       |
| `maxBytes`             | Safe integer from 100,000 to 10,000,000                                                                   | `5000000`     | Maximum decompressed response size retained per request.                                   |
| `maxRetries`           | Safe integer from 0 to 3                                                                                  | `1`           | Bounded retries for transient failures.                                                    |
| `userAgent`            | Optional non-empty string, at most 300 characters                                                         | omitted       | Transparent HTTP user-agent override.                                                      |
| `proxyConfiguration`   | Standard Apify Proxy object; `useApifyProxy` is boolean and `proxyUrls` accepts at most 20 non-blank URLs | direct access | Enables Apify Proxy or custom proxy URLs.                                                  |

#### Runnable input examples

Minimal keyword search:

```json
{
  "keywords": ["juicer"]
}
```

Inspect one public item page:

```json
{
  "startUrls": [
    {
      "url": "https://www.ebay.de/itm/365959456652"
    }
  ],
  "market": "de",
  "maxResults": 3,
  "maxPages": 1,
  "maxListingsPerSearch": 3
}
```

Combine explicit pages and keyword searches with a domain filter:

```json
{
  "startUrls": [
    {
      "url": "https://www.ebay.com/itm/365959456652"
    }
  ],
  "keywords": ["vintage camera", "film camera"],
  "market": "us",
  "location": "10001",
  "customDomains": ["outlook.com"],
  "maxResults": 10,
  "maxPages": 1,
  "maxListingsPerSearch": 2
}
```

Bounded developer options with Apify Proxy:

```json
{
  "keywords": ["public seller email"],
  "maxResults": 5,
  "maxPages": 1,
  "maxListingsPerSearch": 2,
  "timeoutMs": 20000,
  "maxBytes": 2000000,
  "maxRetries": 0,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyCountry": "US"
  }
}
```

### Output

Normal dataset rows contain a public email observation. The exact fields are:

| Field                              | Meaning                                                                                                                       |
| ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `url`                              | Public eBay item URL where the observation was made; required on every row.                                                   |
| `scrapedAt`                        | ISO timestamp for the observation; required on every row.                                                                     |
| `searchUrl`                        | Public search URL that produced the listing, when the row came from a keyword search.                                         |
| `keyword`, `location`, `page`      | Search context, when applicable. `page` is the one-based eBay result page.                                                    |
| `title`, `description`             | Public listing text captured from the result card or item page.                                                               |
| `sellerName`, `sellerUrl`          | Visible seller identity and public seller URL, when present.                                                                  |
| `email`, `emailDomain`             | Lower-cased non-eBay email and its normalized domain.                                                                         |
| `context`                          | `search_result` for a card-level email or `item_page` for an item-page extraction.                                            |
| `extractionMethod`                 | `public_search_card_text`, `public_html_mailto`, `public_html_visible_text`, or `public_html_mailto_and_visible_text`.        |
| `marketplace`                      | Resolved eBay marketplace code. An explicit item page is resolved from its final host; search rows use the selected `market`. |
| `isPubliclyPublished`, `isGuessed` | Provenance flags. Normal rows are always `true` and `false`.                                                                  |
| `error`, `errorCode`               | Present only on a diagnostic row; normal rows do not contain these fields.                                                    |

Example normal search-card row:

```json
{
  "url": "https://www.ebay.com/itm/123456789012",
  "searchUrl": "https://www.ebay.com/sch/i.html?_nkw=juicer&_pgn=1",
  "keyword": "juicer",
  "page": 1,
  "title": "Commercial juicer",
  "sellerName": "Example Seller",
  "email": "sales@example.org",
  "emailDomain": "example.org",
  "context": "search_result",
  "extractionMethod": "public_search_card_text",
  "marketplace": "us",
  "isPubliclyPublished": true,
  "isGuessed": false,
  "scrapedAt": "2026-09-08T10:00:00.000Z"
}
```

Example normal item-page row:

```json
{
  "url": "https://www.ebay.de/itm/365959456652",
  "title": "Vintage watch",
  "sellerName": "Vintage Store",
  "sellerUrl": "https://www.ebay.de/str/vintagestore24",
  "email": "vintagestore24@outlook.com",
  "emailDomain": "outlook.com",
  "context": "item_page",
  "extractionMethod": "public_html_mailto_and_visible_text",
  "marketplace": "de",
  "isPubliclyPublished": true,
  "isGuessed": false,
  "scrapedAt": "2026-09-08T10:00:01.000Z"
}
```

#### Diagnostics

Diagnostics are explicit four-field dataset rows with `url`, `error`, `errorCode`, and `scrapedAt`. They do not contain guessed contact data. Common codes are `MISSING_TARGETS`, `SOURCE_BLOCKED`, and `NO_PUBLIC_EMAILS`; transport failures can also use the response code supplied by the public request layer, such as `RESPONSE_TOO_LARGE`.

Example empty-input diagnostic:

```json
{
  "url": "https://www.ebay.com/",
  "error": "Provide at least one public eBay item URL or search keyword.",
  "errorCode": "MISSING_TARGETS",
  "scrapedAt": "2026-09-08T10:00:00.000Z"
}
```

Example blocked-source diagnostic:

```json
{
  "url": "https://www.ebay.com/sch/i.html?_nkw=juicer&_pgn=1",
  "error": "The public source returned an access-control page.",
  "errorCode": "SOURCE_BLOCKED",
  "scrapedAt": "2026-09-08T10:00:02.000Z"
}
```

### Pagination, filtering, deduplication, and source behavior

#### Request precedence and pagination

The Actor normalizes and deduplicates the input, inspects `startUrls` first, and then searches each `keyword` on the selected `market` host. Explicit URLs are not replaced by search results. Every explicit URL is eligible for one bounded public item-page inspection, while a keyword search requests pages 1 through `maxPages` and processes at most `maxListingsPerSearch` distinct cards for that keyword. Page order is source order; there is no user-configurable sort.

`maxResults` is a global output cap. It applies across explicit item pages, search-card emails, and item-page emails. A per-keyword listing bound and a page bound can reduce results before the global cap is reached. There is no `startUrls`-style pagination for explicit item pages and no input for arbitrary search URLs. Once the global cap is reached, later keywords are not requested.

#### Extraction, filtering, and deduplication

The parser reads public search-card and item-page HTML only. It removes script, style, noscript, SVG, and template content before visible-text extraction, accepts `mailto:` links and visible email-like text, excludes eBay/example/invalid/test domains, and keeps at most 20 candidate emails per page. `customDomains` is applied after normalization; when it is non-empty, only matching domains are retained. Search-card emails are emitted without a detail request when they pass the filter; cards with no retained card-level email, including cards whose visible email fails the domain filter, are inspected at their public item URL.

Normal rows are deduplicated by the normalized email plus listing URL. Explicit URLs are deduplicated after URL normalization, and the same email on two different listings remains two observations. No hidden seller contact, login-only page, private API response, or inferred address is used.

#### Proxy, blocked, empty, and partial runs

Direct public requests are the default. `proxyConfiguration.useApifyProxy: true` uses Apify Proxy; non-empty `proxyUrls` are also passed to Apify's proxy configuration. Proxy use is reported in `OUTPUT_SUMMARY.proxyUsed` but proxying does not bypass a login or access-control decision. The Actor warms a normal public cookie session and uses bounded retries; it does not use stealth or CAPTCHA evasion.

HTTP 403/429 responses and pages containing common challenge markers such as `bm-verify` or `/_sec/verify` become `SOURCE_BLOCKED` diagnostics. A listing without a retained public email becomes `NO_PUBLIC_EMAILS`. If normal rows were collected before a later search failure, those rows remain in the dataset and the summary status is `COMPLETED_WITH_DIAGNOSTICS`. With no normal rows, `hasData` is false. Responses exceeding `maxBytes` are not retained.

### Run summary and downloads

The key-value store contains `OUTPUT_SUMMARY` with `status`, `hasData`, `normalRecords`, `diagnosticRecords`, `inspectedListings`, `searchCardsProcessed`, `requestedItemUrls`, `requestedKeywords`, `marketplace`, `proxyUsed`, `source`, and `finishedAt`. `status` is `SUCCEEDED` when no diagnostics were emitted and `COMPLETED_WITH_DIAGNOSTICS` otherwise. The legacy `OUTPUT` record remains available with `datasetItems`, `diagnosticItems`, and `uniqueItems` counts.

Example `OUTPUT_SUMMARY`:

```json
{
  "status": "SUCCEEDED",
  "hasData": true,
  "normalRecords": 2,
  "diagnosticRecords": 0,
  "inspectedListings": 2,
  "searchCardsProcessed": 0,
  "requestedItemUrls": 2,
  "requestedKeywords": 0,
  "marketplace": "us",
  "proxyUsed": false,
  "source": "ebay-public",
  "finishedAt": "2026-09-08T10:00:03.000Z"
}
```

You can download the dataset in various formats such as JSON, HTML, CSV, or Excel.

#### Cost and limits

There is no fixed per-result fee. Apify compute and storage usage depends on response size, pages, item inspections, retries, and proxy choice. Keep `maxPages`, `maxListingsPerSearch`, `maxResults`, `timeoutMs`, and `maxBytes` bounded. Public-source availability and the presence of seller emails are not guaranteed.

### Running the Actor

#### Apify Console

1. Open the [Actor page](https://console.apify.com/actors/ORn6S9ye40i2eLDpT).
2. Enter a valid JSON input in the Input tab or use the form fields.
3. Start the Actor and review normal and diagnostic rows in the Dataset tab.
4. Review `OUTPUT_SUMMARY` in the Key-value store and download the dataset in the format you need.

#### API and CLI

Start a run with the Apify API using the Actor ID `ORn6S9ye40i2eLDpT` and a JSON input body, or use the CLI from this directory:

```bash
npm install
apify call ORn6S9ye40i2eLDpT -p '{"keywords":["juicer"],"maxResults":5}'
```

#### Local development

```bash
npm install
npm run lint
npm test
apify validate-schema
apify run --purge --input-file test/inputs/default.json
npm run validate
```

The repository test inputs are bounded public probes, not proof that every eBay marketplace or seller publishes an email. A diagnostic-only result is truthful evidence of the current source response, not a normal-record guarantee. Do not commit generated `storage/` output.

### Troubleshooting

#### The dataset has only diagnostics

Check `errorCode` and `OUTPUT_SUMMARY`. `NO_PUBLIC_EMAILS` means no qualifying public address was found in the bounded pages. `SOURCE_BLOCKED` means eBay returned an access-control or challenge response. Try a smaller public item set, a different marketplace, or a compliant proxy configuration; do not add credentials or attempt to evade the source controls.

#### Search returns fewer rows than expected

The source may have fewer qualifying public emails than listings. Also check `customDomains`, `maxListingsPerSearch`, `maxPages`, and the global `maxResults` cap. Search-card results are deduplicated by email plus listing URL, and a listing without a qualifying card email receives one bounded item-page inspection.

#### A request is too large or too slow

Increase `maxBytes` or `timeoutMs` only within their documented bounds, reduce the page/listing limits, and keep `maxRetries` bounded. The Actor intentionally stops when a response exceeds `maxBytes`.

### Privacy, legal, and affiliation

This Actor processes publicly displayed seller contact information. You are responsible for having a lawful basis, respecting eBay's terms and robots or access policies, complying with privacy and anti-spam laws, and honoring opt-outs. Do not use the Actor to collect private, login-only, or access-controlled contact data. This Actor is an independent community tool and is not affiliated with, endorsed by, or sponsored by eBay Inc.

For defects or contract questions, use the [Issues tab on Apify](https://console.apify.com/actors/ORn6S9ye40i2eLDpT/issues) and include the input shape, run ID, summary, and diagnostic code without sharing private data.

# Actor input Schema

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

Optional explicit public eBay /itm/ pages to inspect for visibly published seller contact details.

## `keywords` (type: `array`):

eBay listing terms to search for.

## `location` (type: `string`):

Optional location text passed to the eBay search.

## `market` (type: `string`):

Marketplace used for keyword searches.

## `customDomains` (type: `array`):

Optional seller email domains to retain; values may be written as example.com or @example.com.

## `maxResults` (type: `integer`):

Maximum public seller-email records emitted across explicit URLs and keyword searches.

## `maxPages` (type: `integer`):

Maximum eBay result pages per keyword.

## `maxListingsPerSearch` (type: `integer`):

Bounded number of item pages inspected for visibly published contact details per keyword.

## `timeoutMs` (type: `integer`):

Maximum time allowed for each public request.

## `maxBytes` (type: `integer`):

Maximum decompressed response bytes retained per public request.

## `maxRetries` (type: `integer`):

Bounded retries for transient public request failures.

## `userAgent` (type: `string`):

Optional transparent HTTP user-agent string.

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

Optional standard Apify Proxy configuration; direct access is the default.

## Actor input object example

```json
{
  "startUrls": [],
  "keywords": [],
  "market": "us",
  "customDomains": [],
  "maxResults": 100,
  "maxPages": 3,
  "maxListingsPerSearch": 10,
  "timeoutMs": 30000,
  "maxBytes": 5000000,
  "maxRetries": 1,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `dataset` (type: `string`):

No description

## `runSummary` (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("w3crawler/ebay-email-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("w3crawler/ebay-email-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 w3crawler/ebay-email-scraper --silent --output-dataset

```

## MCP server setup

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