# Wine-Searcher Scraper - Wine Prices, Scores, Offers & LWIN (`abotapi/wine-searcher-scraper`) Actor

Look up wines on wine-searcher.com by name, URL, or LWIN code. Returns 30+ fields, including critic scores, prices, grape, region, appellation, producer, label image, user ratings, food pairing, live offer counts, cheapest merchant offer, and optional critic review breakdown.

- **URL**: https://apify.com/abotapi/wine-searcher-scraper.md
- **Developed by:** [Abot API](https://apify.com/abotapi) (community)
- **Categories:** E-commerce, Automation, Developer tools
- **Features:** Limited permissions
- **Stats:** 56 total users, 10 monthly users, 39.7% runs succeeded, 2 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.70 / 1,000 wine results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## Wine-Searcher Scraper: Scores, Prices & Popularity

Wine-Searcher Scraper turns Wine-Searcher into a clean data feed. Give it a list of wine names, Wine-Searcher links, or LWIN codes (or mix all three with auto-detect) and it returns critic scores, average and market prices, grape, region, appellation, producer, label image, and merchant offer counts as structured JSON, CSV, or Excel, or straight into your app through the API.

### Why This Scraper?

- **Three input styles, one list.** Wine names, Wine-Searcher URLs, or LWIN codes (LWIN7 / 11 / 16 / 18). Auto-detect routes each item automatically, so a mixed list just works.
- **A rich per-wine record.** 30+ fields, including critic score, average price, grape, region, appellation, producer, description, and label image.
- **Optional merchant offers.** Turn on offer extraction for the cheapest live offer, merchant name and link, the full worldwide offers list, and the number of offers and merchants.
- **Optional critic review breakdown.** Turn on review collection for a per-critic reviewer name, score, and tasting note for each wine.
- **Price-spread stats already computed.** With merchant offers turned on, highest and median price are normalised to one standard 750ml bottle, so a case offer or a magnum never skews the comparison for you.
- **Currency follows your market.** The proxy country you choose sets the price-currency context, so results match the market you care about.
- **Fails loudly, not silently.** When Wine-Searcher cannot be reached at all, the run stops with a clear message instead of quietly returning an empty dataset.

### Use Cases

- **Wine merchants and retailers:** benchmark your prices against average and cheapest market prices for the same vintage.
- **Sommeliers and collectors:** look up critic scores, tasting notes, and provenance details before buying or listing a bottle.
- **Cellar and portfolio valuation:** resolve a list of LWIN codes to current average and market prices for a valuation report.
- **Market analysts:** track price spread across regions and currencies for a set of wines over time.
- **Auction houses and wine funds:** verify critic scores and merchant offer counts before quoting a lot.

### Data You Get

> Sample shape: values are illustrative placeholders, not from a live record.

| Field | Example |
| --- | --- |
| `wineName` | `"2015 Sample Wine, Pomerol, France"` |
| `vintage` | `2015` |
| `appellation` | `"Pomerol, France"` |
| `region` / `grapeVariety` | `"Pomerol"` / `"Merlot"` |
| `style` / `beverageType` | `"Red - Savoury and Classic"` / `"Wine"` |
| `producer` | `"Sample Producer"` |
| `description` | `"Short producer or wine description text."` |
| `score` / `scoreBestRating` / `criticReviewsCount` | `97` / `100` / `20` |
| `criticReviews` | per-critic name, score, and tasting note (when review collection is on) |
| `avgPrice` / `avgPriceCurrency` | `5076` / `"EUR"` |
| `cheapestPriceAmount` / `cheapestPriceCurrency` | `3400.00` / `"EUR"` |
| `cheapestPriceMerchant` | `"Sample Merchant"` |
| `status` | `"ok"` (only found wines are pushed and billed; failed or not-found lookups are reported in the log, never pushed) |
| `inputValue` / `inputType` | the name, URL or LWIN you gave, and how it was read |
| `criticScores` | scores only as `{critic name: score}`, always included; tasting notes need review collection |
| `cheapestPriceEur` / `cheapestPriceConverted` / `targetCurrency` | cheapest offer in euros and in your chosen display currency (ECB daily rates; display only, empty when offers are off or the currency has no rate) |
| `offersPages` | `1` when offers were fetched (every offer is read from one page) |
| `offers` | full merchant offers list (when offer extraction is on) |
| `offersCount` / `merchantCount` | `13` / `11` |
| `highestPriceAmount` / `medianPriceAmount` | `4500.00` / `3800.00`, per standard 750ml bottle |
| `labelImageUrl` | Wine-Searcher label image link |
| `wineSearcherUrl` / `searchLocation` | Wine-Searcher link for the wine / `"France"` |

`producer` and `wineryName` carry the same value; `appellation` and `origin` carry the same value. Both pairs are kept for compatibility with either field name. **Output tabs:** Overview (one row per wine), Offers (one row per merchant offer), Critic reviews (one row per review), and Changes (incremental status, changed fields, first and last seen). Offers and reviews also stay nested on each wine row. When a run ends, a `Run summary` record (key `OUTPUT` in the key-value store) lists how many wines were requested, found, missed and pushed, per input type.

The full `offers` array (every merchant, price, and link) and the complete `criticReviews` breakdown are always kept in the Apify dataset.

Not exposed by Wine-Searcher in the page data this actor reads, so they are not returned: wine popularity and popularity rank, a winery page URL. Failed lookups produce no row, so there is no `error` field on rows.

### How to Use

1. Pick an **input type**: auto-detect (recommended for mixed lists), wine names, Wine-Searcher URLs, or LWIN codes.
2. Paste your list into the matching field.
3. Turn on **Fetch merchant offers** and/or **Fetch critic reviews** if you want that extra depth, and set **Max wines** to control run size and cost.
4. Click **Start**, then download the dataset as JSON, CSV, or Excel, or read it through the API.

**Look up by wine name (auto-detected):**

```json
{
  "inputType": "auto",
  "wineNames": ["Petrus 2015", "Dom Perignon 2013"],
  "maxItems": 20
}
```

**Look up by Wine-Searcher URL, with offers and reviews:**

```json
{
  "inputType": "urls",
  "urls": ["https://www.wine-searcher.com/find/sassicaia/2019"],
  "fetchOffers": true,
  "fetchReviews": true
}
```

**Pin a different currency market (United Kingdom / GBP):**

```json
{
  "inputType": "auto",
  "wineNames": ["Sassicaia 2019"],
  "proxy": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"], "apifyProxyCountry": "GB" }
}
```

#### Run it from your code

Python:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("abotapi/wine-searcher-scraper").call(run_input={"inputType": "wineNames", "wineNames": ["Petrus 2015"]})
for wine in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(wine["wineName"], wine["score"], wine["avgPrice"])
```

JavaScript:

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

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('abotapi/wine-searcher-scraper').call({ inputType: 'wineNames', wineNames: ['Petrus 2015'] });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

Or connect it to Make, Zapier, n8n, Google Sheets, or webhooks from the **Integrations** tab.

#### Resume & recurring updates

**Resume from a previous run** (`resumeFromRunId`) is a one-off delta: give it a previous run or dataset ID and this run skips wines already collected there, looking up only the rest of your list. Useful for finishing a large list across several runs without re-paying for wines you already have.

**Incremental mode** (`incrementalMode`) is for a wine list you run on a schedule (daily/weekly) to track changes over time - critic score moved, price shifted, a merchant added or dropped an offer. Every wine is classified:

| `changeType` | Meaning |
|---|---|
| `NEW` | Never seen under this State key before. |
| `UPDATED` | Something changed since last time - `changedFields` names which top-level fields. |
| `UNCHANGED` | Identical to last time. Suppressed (not pushed, not billed) unless `emitUnchanged` is on. |
| `REAPPEARED` | Was `EXPIRED`, now found again. |
| `EXPIRED` | Tracked before, not found this run. Only reported when `emitExpired` is on AND this run completed a full scan of your list (no Resume, no cap, no connection ceiling cut it short, no lookup blocked). |

State is kept in a separate key-value store, keyed by `stateKey` (or an auto-derived hash of your exact wine list when left empty) - it never re-visits wine-searcher.com, so checking state costs no extra proxy spend. **What is compared:** every output field, including critic score, prices and currency, each offer's merchant, price and unit, each critic review's author, score and note, region, appellation, description and the label image path. **What is never compared** (still written to every row): `scrapedAt`, the rate-driven `cheapestPriceEur`, `cheapestPriceConverted` and `targetCurrency`, each offer's `priceValidUntil`, the order of `offers` and `criticReviews`, and the host and query part of `labelImageUrl` (rotating image servers). Add your own exclusions with **Ignore fields when detecting changes (incremental mode only)** (`ignoreFieldsForChanges`): a field name such as `description`, or a dotted path into a list such as `offers.merchantUrl`. Ignored fields never turn a wine into `UPDATED` and never appear in `changedFields`. Adding fields to your ignore list does not mark already-tracked wines as changed.

⚠️ The auto-derived key is built from your wine list itself, so editing the list (adding or removing a wine) also changes the auto key and starts a fresh baseline - convenient if you always re-run the identical list, but it means `emitExpired` can never report a wine you removed as EXPIRED under the auto key. If you intend to grow/shrink your tracked list over time and still want removed wines reported, set your own fixed `stateKey` so every run shares one baseline regardless of list edits.

**Track changes on a recurring list:**

```json
{
  "inputType": "auto",
  "wineNames": ["Petrus 2015", "Opus One 2019"],
  "incrementalMode": true,
  "emitExpired": true
}
```

Long runs (large lists) also survive a platform migration or a manual Resurrect automatically: progress is checkpointed periodically, so a resumed attempt of the same run does not re-look-up (or re-charge for) wines it already extracted.

### Send results into your apps (MCP connectors)

Optionally pipe every result into the tools you already use, via Model Context Protocol (MCP) connectors. This is an extra delivery step after the scrape; the Apify dataset is never changed.

1. Authorize a connector once under **Apify, Settings, API & Integrations** (Notion, Linear, Airtable, or Apify).
2. Select it in the **"Pipe results into your apps"** input field. For Notion, also set `notionParentPageUrl` to the page under which item pages should be created.
3. Run the actor. Results are written to the connector after the dataset is saved.

Each wine becomes one entry with a title and its key fields flattened to plain text; Notion gets one page per wine, other connectors get a best-effort write or digest depending on what that connector supports. The complete record always stays in the Apify dataset. Leaving the field empty skips this entirely.

#### URL mode

With Input type set to Wine-Searcher URLs and the URLs field left untouched, the actor now scrapes one example of each supported link shape (two wines) instead of nothing. Clear the field and paste your own links to scrape those instead. If you supply wine names or LWIN codes and leave the URLs field untouched, the example links are ignored.

| Link shape | Example |
| --- | --- |
| Wine page (name and vintage) | `https://www.wine-searcher.com/find/petrus/2015` |
| LWIN page | `https://www.wine-searcher.com/find/lwin11316442021` |

Any other Wine-Searcher link (search, merchant, region, producer pages) or a non-Wine-Searcher link is skipped with a clear warning in the log naming the link; paste a wine name or LWIN code instead. A wine name or link with no matching page is listed in the log as not found.

### Input Parameters

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `inputType` | string | `auto` | `auto`, `wineNames`, `urls`, or `lwins`. Auto-detect routes each item by its shape. |
| `wineNames` | array | (none) | Full wine names, ideally with vintage year. Up to 1000 items. |
| `urls` | array | one example of each supported shape | Direct Wine-Searcher find links (see the table below). Up to 1000 items. |
| `lwins` | array | (none) | LWIN codes (LWIN7 / 11 / 16 / 18), numeric or string. Up to 1000 items. |
| `fetchOffers` | boolean | `false` | Also extract the cheapest merchant offer, the full offers list, merchant count, and the per-bottle highest and median prices. |
| `fetchReviews` | boolean | `false` | Also collect the per-critic review breakdown (reviewer name and score) for each wine. |
| `targetCurrency` | string | `USD` | Only with Fetch merchant offers: also report the cheapest offer in this currency (`cheapestPriceConverted`, ECB daily rates; display only, never a filter). |
| `maxReviewsPerWine` | integer | `20` | Only with Fetch critic reviews: keep at most this many critic reviews per wine. |
| `resumeFromRunId` | string | (none) | Run or dataset ID of a previous run: wines already collected there are skipped. |
| `incrementalMode` | boolean | `false` | Track a recurring list and classify each wine as NEW, UPDATED, UNCHANGED, REAPPEARED or EXPIRED. |
| `stateKey` | string | (auto) | Incremental mode: name of the saved baseline. Empty derives it from your wine list. |
| `emitUnchanged` | boolean | `false` | Incremental mode: also push (and bill) unchanged wines. |
| `emitExpired` | boolean | `false` | Incremental mode: report wines tracked before but not found this run. |
| `ignoreFieldsForChanges` | array | (none) | Incremental mode only: extra fields (or dotted paths such as `offers.merchantUrl`) left out when deciding whether a wine changed. |
| `concurrency` | integer | `3` | Deprecated and ignored. Lookup parallelism is managed automatically. |
| `maxItems` | integer | `20` | Process at most this many wines from your list. `0` means no limit. |
| `proxy` | object | Residential, France | Residential is the default and prices in your chosen country's currency (France / EUR by default). A datacenter proxy also works but exits in the US only, so prices come back in that market's currency. The actor retries on fresh connections and, if the default connection is unavailable, on a backup connection. |
| `mcpConnectors` | array | (none) | Optional connectors to send a summary of each result to, once authorized under Integrations. |
| `notionParentPageUrl` | string | (none) | Notion connector only: page under which item pages are created. |
| `maxNotifyListings` | integer | `50` | Cap on items written to each connector per run. |

### Output Example

> Sample shape: values are illustrative placeholders, not from a live record.

```json
{
  "inputValue": "Petrus 2015",
  "inputType": "wineNames",
  "wineSearcherUrl": "https://www.wine-searcher.com/find/sample/2015",
  "wineName": "2015 Sample Wine, Pomerol, France",
  "vintage": 2015,
  "appellation": "Pomerol, France",
  "region": "Pomerol",
  "origin": "Pomerol, France",
  "grapeVariety": "Merlot",
  "producer": "Sample Producer",
  "beverageType": "Wine",
  "style": "Red - Savoury and Classic",
  "description": "Short producer or wine description text.",
  "labelImageUrl": "https://www.wine-searcher.com/images/labels/00/00/000000000.jpg",
  "score": 97,
  "scoreBestRating": 100,
  "criticReviewsCount": 20,
  "criticReviews": [
    { "author": "Sample Critic", "score": 95, "bestRating": 100, "tastingNote": "Tasted January 2018." }
  ],
  "avgPrice": 5076,
  "avgPriceCurrency": "EUR",
  "cheapestPriceAmount": 3400.0,
  "cheapestPriceCurrency": "EUR",
  "cheapestPriceMerchant": "Sample Merchant",
  "cheapestPriceMerchantUrl": "https://www.wine-searcher.com/out/0000",
  "bottlesPerUnit": 1,
  "offers": [
    { "price": 3400.0, "priceCurrency": "EUR", "merchant": "Sample Merchant", "merchantUrl": "https://www.wine-searcher.com/out/0000", "unitDescription": "Bottle (750ml)", "bottlesPerUnit": 1, "availability": "InStock", "itemCondition": "NewCondition", "priceValidUntil": "2026-12-31" }
  ],
  "offersCount": 13,
  "highestPriceAmount": 4500.0,
  "highestPriceCurrency": "EUR",
  "medianPriceAmount": 3800.0,
  "merchantCount": 11,
  "wineryName": "Sample Producer",
  "searchLocation": "France",
  "scrapedAt": "2026-01-01T00:00:00.000Z"
}
```

### Plan Requirement

Wine-Searcher shows a bot-protection page ("Access to this page has been denied") to a new connection; the actor handles that check automatically before reading each wine, and clearance is intermittent per connection, so an occasional slow or refused lookup is normal and is retried on a fresh connection.

- **Residential (default).** The default proxy is Apify Residential pinned to France, which sets the price currency to EUR. Residential proxy access needs a paid Apify plan; the Free plan does not include it. Change the proxy country to price a different regional currency.
- **Datacenter.** If you choose a datacenter proxy group the actor uses it, and it works for reading wine pages, but datacenter exits are in the US only, so prices follow that market's currency instead of the French default. A datacenter run never falls back to a residential proxy you did not select.
- **Your own proxy URLs.** Custom proxy URLs are used as given, with no backup connection.

Messages you may see in the log or run status:

- Backup connection in use: "Served over the backup connection (the default connection was unavailable)."
- Residential unavailable on your plan (warning): "The selected connection is unavailable to this run. Apify free plans do not include residential proxies. Continuing on a backup connection; some pages may still be unavailable. For residential access, upgrade at https://apify.com/pricing or supply your own proxy URLs."
- Run failed, connection refused: "The proxy connection was refused for this account, so no page could be read. Your Apify plan does not include proxy access; a paid plan is required: https://apify.com/pricing. If you entered your own proxy URLs, check that their credentials are valid."
- Run failed, residential unavailable and the backup could not load pages: "No results: residential proxies are unavailable on this run (Apify free plans do not include them), and the backup connection could not load the requested Wine-Searcher pages. Upgrade at https://apify.com/pricing or supply your own proxy URLs, then try again."

### FAQ

#### How much does it cost?

You pay per wine returned. Turning on Fetch merchant offers or Fetch critic reviews adds a small extra charge for the additional data pulled per wine, billed per enriched wine and per review captured; leaving both off keeps a run to the base per-record price. The **Pricing** tab shows the current rates. The extra charge is the `detail-enrichment` event: one unit per wine row delivered with offers on, plus one unit per critic review delivered (capped by **Max reviews per wine**, default 20), charged right after each row is saved. Rows that incremental mode suppresses as unchanged are never charged. Use **Max wines** to cap the cost of any run.

#### Is it legal to scrape Wine-Searcher?

This actor collects only publicly available price and rating data. You are responsible for how you use it: follow Wine-Searcher's terms and the laws that apply to you, and get legal advice if you plan commercial redistribution. Prices and offer counts change constantly and reflect a live snapshot at the time of the run.

#### Can I run this on a schedule to track price changes?

Yes. Add the actor to a **Schedule** from the Schedules tab to run it automatically, for example daily. Turn on **Incremental mode** (see Resume & recurring updates above) to get only new and changed wines on each scheduled run.

#### Why don't the highest and median prices match what I see in the offers list?

`highestPriceAmount` and `medianPriceAmount` are normalised to one standard 750ml bottle for a fair comparison across merchants: a case offer is divided by its bottle count, and non-750ml formats such as magnums or half bottles are left out of the comparison. `cheapestPriceAmount` is untouched and always shows the lowest listed offer exactly as a merchant lists it. Wine-Searcher does not publish a specials, was-price, or discount field, so the spread between merchants reflects different merchants, formats, and stock, not a markdown.

#### Why did my run fail instead of returning an empty dataset?

A run fails when it returns no wines at all and at least one lookup could not be reached, for example because every connection attempt was refused. That is a connection problem, not a real answer, so it is reported as a failure rather than a silent empty dataset. If at least one wine is returned, or every lookup was read but simply had no matching page on Wine-Searcher (a misspelled name, a bad URL, or an unknown LWIN code), the run succeeds; unmatched items are logged as not found and are not written to the dataset.

#### Can I use it with AI agents or MCP?

Yes. Call it from any Apify integration or MCP client, and use the connector field to push results into Notion, Linear, or Airtable.

### 🔗 Want more wine and drinks data?

Pair this actor with these related scrapers from the same team:

<table>
<tr><td>🍷 <a href="https://apify.com/abotapi/vivino-wine-data-scraper"><b>Vivino Wine Scraper</b></a><br>From $1.2/1K. Scrape Vivino.com for wine ratings, prices, taste profiles, food pairings...</td><td>🍷 <a href="https://apify.com/abotapi/danmurphys-scraper"><b>Dan Murphy’s Scraper</b></a><br>Scrape Dan Murphy’s products by category, keyword, or specials. Extract 45+ fields...</td></tr>
<tr><td>🍷 <a href="https://apify.com/abotapi/untappd-scraper"><b>Untappd Beer, Brewery and Check-in Scraper</b></a><br>Scrape Untappd beers, breweries, venues and check-ins by keyword, brewery, Top Rated...</td><td>🥦 <a href="https://apify.com/abotapi/lidl-es-scraper"><b>Lidl.es Products, Weekly Offers, Prices &amp; Reviews Scraper</b></a><br>Scrape Lidl.es grocery and non-food products across weekly offers, tools, home, garden...</td></tr>
<tr><td>🥦 <a href="https://apify.com/abotapi/rewe-de-scraper"><b>REWE.de Scraper</b></a><br>Scrape REWE Germany (rewe.de) grocery products: current price, was-price and discount...</td><td>🥦 <a href="https://apify.com/abotapi/resq-club-surplus-food-scraper"><b>ResQ Club Scraper</b></a><br>Scrape ResQ Club surplus food offers in Finland, Sweden and Estonia: name, price, current...</td></tr>
</table>

👉 [Browse all abotapi scrapers](https://apify.com/abotapi)

### 💬 Support

- 🐞 **Found a bug or a missing field?** Open a ticket on the [Issues tab](https://apify.com/abotapi/wine-searcher-scraper/issues/open). We usually reply within hours.
- 🛠️ **Need another site, extra fields or a private build?** Email <contact@abotapi.com>.
- ⭐ **Enjoying it?** A quick review on the actor page helps other users find it.

# Actor input Schema

## `inputType` (type: `string`):

How the items below are formatted. 'Auto-detect' inspects each item and routes it automatically (a wine-searcher.com URL, an all-digit LWIN code, or otherwise a wine name).

## `wineNames` (type: `array`):

Full wine names, ideally with vintage year, e.g. 'Domaine Leflaive Puligny-Montrachet Les Pucelles 2020'. Up to 1000 items.

## `urls` (type: `array`):

Direct wine-searcher.com/find/... links: a wine page (name and vintage) or an LWIN page. Up to 1000 items.

## `lwins` (type: `array`):

Liv-ex Wine Identification Numbers (LWIN7, LWIN11, LWIN16, LWIN18), numeric or string. Up to 1000 items.

## `fetchOffers` (type: `boolean`):

When on, the actor also extracts the cheapest merchant offer (price + merchant), the full offers list, and the cheapest price converted to your display currency. Adds a little time per wine. Leave off for a faster, cheaper run that still returns scores, average price, grape, region and the offer count.

## `targetCurrency` (type: `string`):

Display conversion only, never a filter: the cheapest offer is also reported in this currency as cheapestPriceConverted (and in euros as cheapestPriceEur) using ECB daily reference rates. Needs Fetch merchant offers. The original prices and currencies are always kept unchanged. Merchant prices themselves still follow the proxy country.

## `fetchReviews` (type: `boolean`):

When on, the actor also collects the per-critic review breakdown (reviewer name and score) for each wine. Each review captured is one enriched data item.

## `maxReviewsPerWine` (type: `integer`):

Only used when Fetch critic reviews is on. Keeps at most this many critic reviews per wine; each review delivered is one enriched data item. Values above 200 are treated as 200.

## `concurrency` (type: `integer`):

Deprecated and ignored. Lookup parallelism is managed automatically.

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

The single cap on this run: process at most this many wines from your list. Set to 0 for no limit (process the whole list). Keeps a first run cheap and predictable.

## `proxy` (type: `object`):

Residential (default, France) prices in EUR; a datacenter group works but exits in the US only. The proxy country determines which regional currency the average price is shown in.

## `resumeFromRunId` (type: `string`):

Optional. ID of a previous run of this actor (or a dataset ID). Wines already collected there are skipped, so this run only looks up the rest of your list. Max wines then counts only the new wines. One-off continuation of a SPECIFIC run - for ongoing scheduled tracking of the same wine list use Incremental mode below instead.

## `incrementalMode` (type: `boolean`):

For a wine list you run on a schedule (e.g. daily/weekly): remembers what this exact list returned last time and classifies every wine as NEW / UPDATED / UNCHANGED / REAPPEARED / EXPIRED, so you can act on only what changed. Compared: critic score, prices and currency, offers (merchant, price, unit), critic reviews (author, score, note), region, appellation, description, label image path and every other output field. Never compared: scrapedAt, each offer's priceValidUntil, the order of offers and criticReviews, and the host/query part of the label image URL (change more with Ignore fields below). Off by default so existing scheduled runs are unaffected. Uses State key below to isolate different lists; see Emit unchanged rows / Emit expired rows to control which of those get pushed (both bill extra rows when on).

## `stateKey` (type: `string`):

Names the saved baseline Incremental mode compares against. Leave empty to auto-derive one from your exact wine list (Input type + wineNames/urls/lwins) - convenient when you re-run the SAME list every time, but editing the list also changes this auto key, starting a fresh baseline. If you plan to add or remove wines over time and still want EXPIRED reported for ones you drop, set your own fixed text here so every run shares one baseline regardless of list edits. Ignored when Incremental mode is off.

## `emitUnchanged` (type: `boolean`):

When Incremental mode is on: also push rows classified UNCHANGED (identical to last run), not just NEW/UPDATED/REAPPEARED/EXPIRED. Off by default to save you from paying for rows that didn't change. Turning this on bills the per-result event (and the detail-enrichment surcharge, when fetched) for every unchanged row too.

## `emitExpired` (type: `boolean`):

When Incremental mode is on: push a synthetic EXPIRED row for wines tracked in a previous run that this run could not find. Only fires after a run that completely covered your whole tracked list: if Resume from run/dataset ID was used, if the run stopped early (a connection/session ceiling), or if Max wines cut the list short, expiry reporting is skipped for that run (with a log line saying why) because a partial scan can't tell 'gone' apart from 'not reached yet'. Off by default; turning it on bills the per-result event for each EXPIRED row emitted. EXPIRED rows count against Max wines like any other row; if the cap is reached, the remaining gone wines stay tracked and are reported on a later run rather than being emitted uncapped.

## `ignoreFieldsForChanges` (type: `array`):

Extra fields to leave out when Incremental mode decides whether a wine changed. Use a field name (e.g. `description`) or a dotted path for a field inside a list (e.g. `offers.merchantUrl`). The fields are still written to every row; they just never turn a wine into UPDATED or appear in changedFields. Already excluded by default: scrapedAt, each offer's priceValidUntil, list order of offers and criticReviews, and the host/query part of labelImageUrl. Ignored when Incremental mode is off.

## `mcpConnectors` (type: `array`):

Optionally send results into the apps you already use, via Model Context Protocol (MCP) connectors. Authorize one under Apify → Settings → API & Integrations, then select it here. Notion gets a rich page-per-item export; other connectors get a best-effort write/digest. Leave empty to skip; never changes the dataset output. Supported: Notion (https://mcp.notion.com/mcp), Linear (https://mcp.linear.app/sse), Airtable (https://mcp.airtable.com/mcp), Apify (https://mcp.apify.com).

## `notionParentPageUrl` (type: `string`):

URL (or id) of the Notion page under which item pages are created. Required to enable the Notion export; ignored by other connectors.

## `maxNotifyListings` (type: `integer`):

Cap on items written to each connector per run. Does not affect the dataset.

## Actor input object example

```json
{
  "inputType": "auto",
  "wineNames": [
    "Domaine Leflaive Puligny-Montrachet Les Pucelles 2020",
    "Petrus 2015"
  ],
  "urls": [
    "https://www.wine-searcher.com/find/petrus/2015",
    "https://www.wine-searcher.com/find/lwin11316442021"
  ],
  "lwins": [
    "11316442021",
    "11084042019",
    "1131644"
  ],
  "fetchOffers": false,
  "targetCurrency": "USD",
  "fetchReviews": false,
  "maxReviewsPerWine": 20,
  "concurrency": 3,
  "maxItems": 20,
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "FR"
  },
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "ignoreFieldsForChanges": [],
  "maxNotifyListings": 50
}
```

# Actor output Schema

## `overview` (type: `string`):

No description

## `offers` (type: `string`):

No description

## `reviews` (type: `string`):

No description

## `changes` (type: `string`):

No description

## `summary` (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 = {
    "inputType": "auto",
    "wineNames": [
        "Domaine Leflaive Puligny-Montrachet Les Pucelles 2020",
        "Petrus 2015"
    ],
    "urls": [
        "https://www.wine-searcher.com/find/petrus/2015",
        "https://www.wine-searcher.com/find/lwin11316442021"
    ],
    "lwins": [
        "11316442021",
        "11084042019",
        "1131644"
    ],
    "maxReviewsPerWine": 20,
    "proxy": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "FR"
    },
    "ignoreFieldsForChanges": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("abotapi/wine-searcher-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 = {
    "inputType": "auto",
    "wineNames": [
        "Domaine Leflaive Puligny-Montrachet Les Pucelles 2020",
        "Petrus 2015",
    ],
    "urls": [
        "https://www.wine-searcher.com/find/petrus/2015",
        "https://www.wine-searcher.com/find/lwin11316442021",
    ],
    "lwins": [
        "11316442021",
        "11084042019",
        "1131644",
    ],
    "maxReviewsPerWine": 20,
    "proxy": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "FR",
    },
    "ignoreFieldsForChanges": [],
}

# Run the Actor and wait for it to finish
run = client.actor("abotapi/wine-searcher-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 '{
  "inputType": "auto",
  "wineNames": [
    "Domaine Leflaive Puligny-Montrachet Les Pucelles 2020",
    "Petrus 2015"
  ],
  "urls": [
    "https://www.wine-searcher.com/find/petrus/2015",
    "https://www.wine-searcher.com/find/lwin11316442021"
  ],
  "lwins": [
    "11316442021",
    "11084042019",
    "1131644"
  ],
  "maxReviewsPerWine": 20,
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "FR"
  },
  "ignoreFieldsForChanges": []
}' |
apify call abotapi/wine-searcher-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,abotapi/wine-searcher-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/PKwGz8QAc8H26zPZl/builds/4py9w4DEueQvYsPXH/openapi.json
