# eBay Scraper — Search, Listings, Prices & Sellers (`kestrel/ebay-search-scraper`) Actor

eBay search results and item pages as structured rows: price, list price, shipping, condition, buying format, bids, units sold, watchers, seller feedback and location. Filter by price server-side across 11 marketplaces. Pay per listing row.

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

## Pricing

$3.00 / 1,000 listing rows

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, Listings, Prices & Sellers

An eBay scraper that turns search results and item pages into clean rows: price, struck-through list price, shipping cost, condition, buying format, bids and time left, **units sold**, **watchers**, seller feedback score and percentage, item location and image. No API key, no login, no browser. It reads the same public pages a shopper sees, on 18 marketplaces, and bills you per listing row it actually delivers. Use it for ebay price research, for bulk ebay product data, and as an ebay api alternative when the official Browse API's onboarding is more than your project needs.

The differentiator is the **demand column**. Most ebay listing scraper output tells you what sellers are asking. This one also tells you what shoppers are doing about it: how many units a listing has already sold, how many people are watching it, how many bids an auction has drawn and how long it has left, whether the seller takes offers, and how far the current price sits below the seller's own list price. Those five numbers are what separate a price you can copy from a price nobody pays.

**Last verified working: 2026-08-29.**

### eBay listings: what does this scraper collect?

Every row carries the same columns, whether it came from a search card or an item page, so exports are never ragged.

| Field | What it is |
| --- | --- |
| `item_id` | eBay's numeric listing id — the number in any `/itm/` URL |
| `title` | Listing title as the seller wrote it |
| `url` | Canonical item page URL |
| `query` / `search_url` | The keyword, and the exact URL the page was read from |
| `page` / `position` | Where the listing sat in eBay's own order, counted across pages |
| `domain` / `marketplace` | `com`, `co.uk`, `de` … and the country name |
| `price` / `price_display` / `currency` | The number, the string exactly as printed, and the currency read from the price itself. `price` is null on the occasional MAP-restricted listing where eBay prints "See price" instead of a number — `price_display` then says so, and price bounds drop those rows free |
| `list_price` / `list_price_display` / `discount_pct` | The struck-through original and how far below it the listing sits |
| `shipping_cost` / `shipping_display` / `free_shipping` | Delivery charge as a number, the full line, and the free-delivery flag |
| `condition` | Brand New, Pre-Owned, Open Box, Certified - Refurbished, For parts … |
| `buying_format` | `buy_it_now` or `auction` |
| `bids` / `time_left` | Auction rows: bid count and eBay's own countdown text |
| `best_offer` | Whether the seller accepts offers |
| `units_sold` | How many units this listing has sold |
| `watchers` | How many shoppers are watching it |
| `returns` | The returns line, e.g. "Free returns" or "30 days returns" |
| `seller` / `seller_feedback_pct` / `seller_feedback_count` | Username or store, positive-feedback percentage, and the score expanded from eBay's `40.6K` shorthand to `40600` |
| `location` | Where the item ships from |
| `image` | Listing image URL |
| `rating` / `ratings_count` | Product star rating, when the listing is matched to a catalogue product |
| `results_total` | How many listings eBay reports for the whole search |
| `quantity_available`, `breadcrumbs`, `category`, `item_specifics`, `seller_url` | Item-page rows only: availability text, the category path, the leaf category, the seller's specifics table, and the store URL |
| `fetched_at` | UTC timestamp |

A second row type, `status`, is emitted once per keyword or URL and is **always free**: `kind`, `target`, `status` (`ok` / `no_results` / `error`), `items`, `filtered`, `duplicates`, `pages`, `results_total`, `error`, `fetched_at`. Read it to know that a job ran and what it produced, without inferring anything from row counts.

### eBay search results by keyword: how do I get them?

To scrape eBay search results by keyword, give it keywords and a depth. Each page carries 60 listings.

```json
{
  "queries": ["mechanical keyboard", "keycap set"],
  "domain": "com",
  "pages": 2,
  "maxResultsPerQuery": 60
}
```

That run reads two pages per keyword, keeps the first 60 listings of each in eBay's own order, and charges for 120 rows at most — fewer if a keyword returns fewer.

### How do I use my own eBay search URL?

Build the search in a browser with whatever filters you like, copy the address bar into `startUrls`, and the actor reads it exactly as pasted. The marketplace comes from the URL, so one run can mix countries.

```json
{
  "startUrls": [
    "https://www.ebay.co.uk/sch/i.html?_nkw=lego+star+wars&_sacat=19006",
    "https://www.ebay.de/sch/i.html?_nkw=nintendo+switch+oled"
  ],
  "pages": 3
}
```

Individual item URLs work in the same field and return the richer item-page row — category breadcrumb, the seller's specifics table, availability and returns:

```json
{
  "startUrls": ["https://www.ebay.com/itm/800459096845"]
}
```

### How does the price filter save me money?

`minPrice` and `maxPrice` become eBay's own `_udlo` and `_udhi` parameters, so eBay does the filtering **before it sends the page**. You pay for the pages that come back, not for thousands of rows you throw away afterwards. The bound is then checked a second time here, because eBay applies its own bound to price *plus shipping* in some views and a row can still arrive outside your range — anything that fails the second check is dropped and never billed. Those drops show up in the `filtered` count on the status row.

```json
{
  "queries": ["vintage pocket watch"],
  "minPrice": 50,
  "maxPrice": 400,
  "buyingFormat": "auction",
  "sortBy": "ending_soonest",
  "pages": 2
}
```

`buyingFormat`, `conditions`, `category` and `sortBy` are server-side in exactly the same way. `conditions` is a multi-select because eBay grades refurbished in four separate tiers — Certified, Excellent, Very good, Good — and a filter that sends only "refurbished" silently drops most of that market.

### eBay price research: how do I compare a market?

Point the actor at a product's keywords, take the whole first page or two, and the distribution answers the question. This is bulk ebay data extraction with the analysis columns already attached. `price` plus `shipping_cost` is the real landed cost, and it is the number ebay buy it now shoppers actually compare. `units_sold` tells you which of those prices has ever transacted. The ebay units sold figure is the strongest signal on the page. The ebay watchers count tells you where interest sits without a sale yet, and a listing with many ebay watchers and no sales is usually priced above the market. `results_total` tells you how crowded the listing is. `condition` lets you split the used market from the new one, `best_offer` shows which sellers will negotiate — an ebay best offer flag on most of a page means the printed prices are ceilings, not clearing prices — and ebay seller feedback lets you discount the outliers from accounts nobody buys from.

A practical routine for resellers and sourcing teams:

1. Run the keyword with `sortBy: "price_low"` and `pages: 2` to find the floor.
2. Run it again with `buyingFormat: "auction"` and `sortBy: "ending_soonest"` to see what is clearing right now. That is ebay auction data in its most useful form: `bids` and `time_left` on those rows are live competition, and a lot of bids with little time left is a price about to be proved.
3. Sort your export by `units_sold` descending. The top rows are the listings that have proven the price.
4. Compare `price` against `list_price` — a large `discount_pct` across many sellers is a category in decline, not a bargain.
5. Schedule the same run daily and diff on `item_id` to watch new listings, price moves and `units_sold` growth.

This is the ebay market research loop that ebay reseller tools charge a monthly subscription for, except it lands in your own dataset and you pay by the row.

### eBay seller data: how do I find who dominates a category?

The `sellers` view groups the same rows by who is selling. `seller_feedback_count` is expanded from eBay's abbreviated card display, so `40.6K` becomes `40600` and you can sort on it numerically. Combined with `units_sold`, `location` and `condition`, this ebay seller feedback view is enough to find the handful of accounts that dominate a category, to check whether they are stores or private sellers, and to see how their pricing differs from the long tail. Auction cards are the one exception: eBay does not print the seller line on an auction card, so those rows carry the seller from the item page only.

### eBay marketplaces: which ones are supported?

`com`, `de`, `fr`, `it`, `es`, `at`, `nl`, `pl`, `com.au`, `com.sg`, `com.my` — eleven, each measured on 2026-08-29 returning a full 60-listing page on the first request. Set `domain` for keyword searches, or paste marketplace URLs into `startUrls` and mix as many as you like in one run.

Five hosts are deliberately **not** offered: eBay UK, Ireland, Canada, Switzerland and Hong Kong answered eBay's anti-bot challenge on 18 of 18 attempts, across datacentre and residential proxies, country-pinned and not, warm and cold — while Germany and Australia answered 5 of 5 on the same proxies in the same minute. eBay's posture there is per-host and outside our control, and listing them as supported would sell you a run that cannot finish. If you want to try one anyway, paste its URL into `startUrls`: the row will still be labelled and priced in the right currency, and a refusal comes back as a plain `status: "error"` rather than as an empty market. We re-check this on every release. Condition labels, buying-format wording and the shipping line are all parsed in each site's own language, so a German run returns `Gebraucht` and `Sofort-Kaufen` rather than nulls. `currency` is read from the rendered price rather than assumed from the marketplace, because eBay localises the price block to the caller's egress and a run proxied through another country would otherwise be labelled with the wrong currency.

### How much does it cost?

Pay per event: **$0.003 per listing row**, and nothing else. There is no per-run start fee and no compute charge on top.

- A search that returns nothing is free.
- Listings where eBay withholds the price ("See price") are dropped free whenever you have set a price bound.
- Rows dropped by your price bounds are free.
- A listing found by two of your keywords is delivered and billed **once** per run.
- `status` rows are free.
- If your spending limit is reached mid-run, the run stops delivering rather than delivering rows it could not charge for — you never receive unpaid data and never pay for data you did not receive.

One page of 60 listings is $0.18. A daily two-page watch on five keywords is about $1.80 a month.

### Every input, explained

| Input | Default | What it does |
| --- | --- | --- |
| `queries` | `[]` | Keywords to search, one job each |
| `startUrls` | `[]` | eBay search URLs read verbatim, and/or individual item URLs |
| `domain` | `com` | Marketplace for the keyword searches (11 verified hosts) |
| `buyingFormat` | `all` | `all`, `buy_it_now` or `auction` |
| `conditions` | `[]` | Multi-select of the nine eBay condition grades |
| `minPrice` | `0` | Lower bound, sent to eBay and re-checked before billing |
| `maxPrice` | `0` | Upper bound; `0` means none |
| `category` | `""` | eBay numeric category id (the `_sacat` number in any category URL) |
| `sortBy` | `best_match` | `best_match`, `ending_soonest`, `newly_listed`, `price_low`, `price_high` |
| `pages` | `1` | Result pages per job, 60 listings each, up to 34 |
| `maxResultsPerQuery` | `0` | Cap per job; `0` means everything the pages returned |
| `sessions` | `4` | Parallel proxy sessions (egress IPs) |
| `perIp` | `0.5` | Requests per second per IP |
| `proxyConfiguration` | Apify Proxy | Required; the run escalates to residential on its own |

### How do I call it from Python, JavaScript or curl?

Python:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("kestrel/ebay-search-scraper").call(run_input={
    "queries": ["mechanical keyboard"],
    "pages": 2,
    "minPrice": 30,
    "maxPrice": 200,
})
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
    if row["type"] == "item":
        print(row["price"], row["currency"], row["units_sold"], row["title"])
```

JavaScript (Node):

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('kestrel/ebay-search-scraper').call({
    queries: ['keycap set'],
    pages: 1,
    sortBy: 'price_low',
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.filter((r) => r.type === 'item').length, 'listings');
```

curl:

```bash
curl -X POST "https://api.apify.com/v2/acts/kestrel~ebay-search-scraper/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H 'content-type: application/json' \
  -d '{"queries":["vintage pocket watch"],"buyingFormat":"auction","pages":1}'
```

### Does it work with n8n, Make and AI agents?

Yes. The actor is a standard Apify Actor, so it appears in the **n8n** Apify node and in **Make**'s Apify app as "Run an Actor", and the dataset comes back as ordinary JSON you can map straight into a sheet, a database or an alert. For agents, the Apify **MCP** server exposes it as a callable tool, so an AI agent can ask for eBay listings by keyword and get typed rows back rather than a page of HTML. The `status` rows make it safe for an agent to run unattended: a failure is a row with `status: "error"` and a readable message, never a silently empty result.

Zapier, Airtable and Google Sheets all work through the same dataset endpoint, and the "Run Actor and get dataset" pattern needs no code.

### eBay API alternative: why not use the official API?

The eBay Browse API is excellent if you are already an eBay developer: you register an application, complete the onboarding, manage OAuth tokens and live inside its rate limits and its field set. For a one-off market study, a price watch, a sourcing spreadsheet or an agent tool, that is a lot of ceremony. This actor is the ebay api alternative for those cases — paste keywords, get rows, pay for what you got. It also returns things the catalogue-shaped API does not surface the same way: the struck-through list price a seller is advertising against, the watcher count, the units already sold on that specific listing, and the exact position the listing held in eBay's own results.

If you need order management, listing creation, or guaranteed contractual access, use the official API. If you need to read the public market, use this.

### Does this scraper collect eBay sold items?

**No, and here is why.** eBay's completed and sold search — the `LH_Sold=1&LH_Complete=1` view — is behind a **sign-in gate**. A logged-out request to it is redirected to `signin.ebay.<tld>` with `sgfl=srch`, on every marketplace. We verified this on 2026-08-29 across seven marketplaces, datacentre and residential proxies in seven countries, eight different URL routes including eBay's own "Sold items" rail link and the RSS feed, and three browser fingerprints: **not one of roughly 115 attempts returned sold results.** A headless browser does not change the answer, because the block is an HTTP redirect and not a JavaScript challenge — only a signed-in eBay account gets through, which we will not do on your behalf or ours.

So this actor scrapes active listings only, and says so plainly rather than shipping a "sold" toggle that returns nothing. If you came here for ebay sold items to build price comps, the honest substitute is the `units_sold` column: it is the number of units a live listing has actually sold at its current price, published by eBay on the public results page, and for pricing decisions it answers most of the same question — *has anyone paid this?* — using data that is genuinely available.

### FAQ

#### How many listings are on each eBay page?

Sixty. The actor requests `_ipg=60`, which is eBay's ceiling. `pages: 3` therefore reads up to 180 listings per job. eBay stops paginating any single search at roughly 2000 items however many it reports in `results_total`, which is why `pages` is capped at 34.

#### Why did my search return no rows?

Either eBay genuinely found nothing, or the run was refused. The two are never confused: a genuine miss produces a status row with `status: "no_results"` and costs nothing, and a refusal produces `status: "error"` with the reason. This matters more than it sounds — eBay's zero-result page still renders about twenty "results matching fewer words" cards from *other* searches, and a scraper that delivers those bills you for listings you never asked for. This one drops them.

#### Do I need a proxy?

Yes, and Apify Proxy is the default. eBay is behind Akamai Bot Manager and refuses unrotated callers with a `/splashui/challenge` interstitial. The run opens several proxy sessions, warms each one against the marketplace homepage, paces every session separately, retries on a different IP whenever a page is refused, and escalates from datacentre to residential proxies by itself once eBay has pushed back twice. Measured at one request per second on 2026-08-29: datacentre answered 5 of 10 searches and residential 8 of 10, so with retries a run completes reliably on either.

#### Are sponsored listings flagged?

There is deliberately no `sponsored` column. eBay's current results page prints the (deliberately obfuscated) "Sponsored" disclosure on **every** card — 60 of 60 on four different searches across two marketplaces and both buying formats, all from the same organic results module. A column that is always `true` is worse than no column, so we left it out and documented the finding instead.

#### Can I track prices over time?

Yes — schedule the run and keep the datasets. Each row carries `fetched_at`, `item_id` and `price`, which is everything a time series needs. A daily run of the same keywords, appended to one table and grouped by `item_id`, gives you an ebay price tracker with the listing's `units_sold` and `watchers` moving alongside the price, which is far more informative than the price alone.

#### What about eBay Kleinanzeigen or eBay Motors?

Kleinanzeigen is a separate site with its own markup and is not covered. Motors listings are ordinary eBay listings and come back like any other, though vehicle-specific attributes live in `item_specifics` on item-page rows rather than in dedicated columns.

#### Is scraping eBay legal?

This actor reads only publicly available pages — the same search results and item pages any visitor sees without logging in — and collects no personal data beyond the seller usernames and store names eBay itself publishes next to every listing. Public-data scraping of this kind is broadly lawful in the US and the EU, and courts have repeatedly declined to treat access to a public web page as unauthorised access. That is not the whole story, though: eBay's own Terms of Service restrict automated access, so scraping can be a contractual matter between you and eBay even where it is not a legal one. You are responsible for your own use — respect robots directives and rate limits, do not republish listing content wholesale, keep collection proportionate to your purpose, and take your own legal advice for anything commercial. We do not scrape anything behind a login, and we will not: that is exactly why the sold-listings view above is absent rather than faked.

#### How do I find an eBay category id?

Open any eBay category in a browser and read the `_sacat` number out of the URL, or click into a category from a search and copy it from the address bar. Pass it as `category` and every keyword in the run is searched inside that category only.

### Changelog and support

See `CHANGELOG.md` for version history. If a field stops populating, open an issue on the actor's Issues tab with the input you used — the parsers are covered by an offline test suite built from captured pages, and a fixture from a real failing run is the fastest possible fix.

### Related scrapers

eBay is one marketplace. These read the others, with the same row-per-item shape and the same pay-per-delivered-row billing:

- [Amazon Product Scraper](https://apify.com/kestrel/amazon-product-scraper) — one ASIN in, the whole product page out: buybox and list price, stock flag, gallery, bullet points and the details table, across 20 marketplaces — the Amazon column of a price-spread table.
- [Amazon Search Scraper](https://apify.com/kestrel/amazon-search-scraper) — Amazon keyword results with page position and a sponsored flag, when you want to know what ranks for a phrase there the way this actor tells you for eBay.
- [Amazon Best Sellers Scraper](https://apify.com/kestrel/amazon-best-sellers-scraper) — the Top 100 of any Amazon category with every rank filled, plus New Releases, for demand ranking rather than price.
- [Amazon Reviews Scraper](https://apify.com/kestrel/amazon-reviews-scraper) — customer reviews and ratings by ASIN with filters that run before billing, for the feedback behind a listing you priced here.
- [AliExpress Reviews Scraper](https://apify.com/kestrel/aliexpress-reviews-scraper) — buyer feedback with photos, the variant chosen and the buyer's country, for checking a supplier's product before you list it on eBay.

All of them bill per delivered row, never charge for rows a filter or a spending limit removed, and write an Apify dataset you can export to CSV, Excel or JSON.

# Changelog

This Actor's version history is a separate document: https://apify.com/kestrel/ebay-search-scraper/changelog.md

# Actor input Schema

## `queries` (type: `array`):

Keywords to search, exactly as a shopper would type them: "mechanical keyboard", "vintage pocket watch". Each is searched across the pages set below, on the marketplace you choose.

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

eBay search URLs (https://www.ebay.com/sch/i.html?\_nkw=...) to read exactly as pasted — every filter already in the URL is kept — and/or individual item URLs (https://www.ebay.com/itm/123456789012), which return the richer item-page row. The marketplace is taken from each URL, so you can mix countries in one run.

## `domain` (type: `string`):

eBay marketplace for the keyword searches: com, de, fr, it, es, at, nl, pl, com.au, com.sg, com.my. These are the hosts measured answering reliably on 2026-08-29; eBay UK, Ireland, Canada, Switzerland and Hong Kong currently return an anti-bot challenge on every proxy pool, so they are not offered here — paste their URLs into the field above if you want to try anyway. URLs bring their own marketplace.

## `buyingFormat` (type: `string`):

Restrict to Buy It Now listings or to auctions. Auction cards carry the bid count and the time left; fixed-price cards carry units sold and watchers.

## `conditions` (type: `array`):

Keep only these conditions. eBay grades refurbished in four separate tiers, so pick the ones you actually accept. Leave empty for every condition.

## `minPrice` (type: `integer`):

Lower price bound in the marketplace's own currency. Sent to eBay as \_udlo so the results arrive already filtered, then checked again before billing — you are never charged for a listing outside your range.

## `maxPrice` (type: `integer`):

Upper price bound in the marketplace's own currency. Sent to eBay as \_udhi and checked again before billing. Leave at 0 for no upper bound.

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

Optional eBay category id to search inside, e.g. "175672" for computer components. It is the \_sacat number in any eBay category URL. Leave empty to search all of eBay.

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

How eBay orders the results. Price sorts are on price plus shipping, which is what a shopper actually compares.

## `pages` (type: `integer`):

How many result pages to walk. Each page carries 60 listings, and eBay stops paginating a search at about 2000 items however many it reports.

## `maxResultsPerQuery` (type: `integer`):

0 = everything the pages returned. N = the first N in eBay's order. The main cost control: the run stops fetching once each job has N.

## `sessions` (type: `integer`):

How many proxy sessions (egress IPs) run in parallel. More is faster; each is paced separately and warmed on eBay's homepage before its first request.

## `perIp` (type: `number`):

Pace for each session. eBay tolerates about 0.5/s per IP; faster earns interstitials, not speed.

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

Apify Proxy is required: eBay answers unproxied and unrotated callers with an Akamai interstitial. The run starts on datacentre proxies and escalates to RESIDENTIAL by itself when eBay pushes back.

## Actor input object example

```json
{
  "queries": [
    "mechanical keyboard"
  ],
  "startUrls": [],
  "domain": "com",
  "buyingFormat": "all",
  "conditions": [],
  "minPrice": 0,
  "maxPrice": 0,
  "category": "",
  "sortBy": "best_match",
  "pages": 1,
  "maxResultsPerQuery": 0,
  "sessions": 4,
  "perIp": 0.5,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

One row per listing with its price, shipping, condition, buying format, demand signals and seller, plus status rows. Charged rows are billed as delivered; status rows are always free.

## `summary` (type: `string`):

One JSON record with the counts this run delivered and charged, its error, filtered and duplicate tallies, and its HTTP stats.

# 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 = {
    "queries": [
        "mechanical keyboard"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("kestrel/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 = { "queries": ["mechanical keyboard"] }

# Run the Actor and wait for it to finish
run = client.actor("kestrel/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 '{
  "queries": [
    "mechanical keyboard"
  ]
}' |
apify call kestrel/ebay-search-scraper --silent --output-dataset

```

## MCP server setup

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