# Vivino Scraper – Wine Prices, Ratings & Reviews (`s-r/vivino-scraper`) Actor

Collect Vivino wines and vintages with prices, ratings, grapes, merchant offers and optional reviews. Search names, combine explore filters or supply wine URLs. Separate wine and review rows support catalog enrichment and price research.

- **URL**: https://apify.com/s-r/vivino-scraper.md
- **Developed by:** [SR](https://apify.com/s-r) (community)
- **Categories:** E-commerce, Business
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 vivino wines

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

## Vivino Scraper

Vivino Scraper collects wine and vintage data, market prices, ratings, merchant offers and optional customer reviews. Use it to enrich a wine catalog, research prices for an importer, or supply a wine discovery application with structured public data. Search by wine name, combine explore filters, or provide specific Vivino wine and vintage URLs. The results distinguish each vintage from its written reviews, so you can analyse the two separately and join them by vintage id.

### Vivino Scraper input options

Choose a wine name with `search`, select explore filters, or supply `urls`. A search for `tignanello` uses Vivino's wine-name search. Explore filters include wine type, grape, origin region, origin country, minimum rating and price range. Sorting supports average rating, rating count, price and discount percentage, in ascending or descending order. The default minimum rating is 1, which enables a broad explore search when no narrower filter is selected.

The `country` option selects the market for prices and available merchants. It defaults to `US`. The `currency` option uses an ISO currency code and defaults to the currency of the selected country. The wine's origin is independent of this choice: an Italian wine can have a US price market. Use `origin_countries` for the countries where the wine was produced, and `country` for the country where the offer is available. US offers can also vary by state; the actual observed state is included when available.

The six wine types are `red`, `white`, `sparkling`, `rose`, `dessert` and `fortified`. Grape filters accept grape names or grape ids as strings. Region filters accept Vivino region ids. Unknown grape names, unsupported country or currency codes, and a minimum price above the maximum price produce an input error. Source names are requested in English for consistent country, region and wine metadata.

For specific wines, `urls` accepts HTTPS Vivino URLs ending in `/w/<wine_id>`, optionally with a year, or `/wines/<vintage_id>`. You can also provide a vintage id as a string. Bare numbers identify vintages, so do not put a wine id in that format. Specific URLs take precedence over explore filters. A dated wine URL selects the requested vintage when Vivino lists it; an unavailable year does not silently become a different vintage.

`max_wines` sets the maximum number of unique vintages and defaults to 50. `include_offers` adds merchant offers. `include_details` adds exact vintage grapes, winemaker and further vintage statistics. `include_reviews` adds separate written-review rows. `max_reviews` defaults to 20 and limits unique reviews per wine. Increase either volume option for a larger collection; continuation keeps the same limits across the whole request.

### Output: wine rows and review rows

The dataset contains two row types. `record_type` and `_type` identify a wine as `catalog_entry` and a written review as `review`. Every row includes the source, platform, retrieval timestamp, a stable row id and the relevant vintage id in `entry_id`. Wine rows include title, wine id, wine name, winery, vintage year, region, origin country, image and a Vivino page URL. They also contain the vintage's average rating and rating count, plus the wine-wide rating statistics when the source provides them.

Wine prices are numeric amounts in `currency`. Price fields can be null when no matching offer is available for that market. Discount information and bottle volume are included where available. With offers enabled, each merchant has its name, id, country and a list of prices. Each price can include a bottle-quantity message, inventory, link and bottle volume. Availability and prices can change between runs, so retain the timestamp and market fields with your analysis.

Grapes require care. Without details, the grape names describe Vivino's wine style and `grapes_source` is `style`. With details, they come from the vintage and `grapes_source` is `vintage`. Exact grapes are useful for catalog enrichment; style grapes are useful for discovery but should not be treated as a confirmed blend. Further detail fields include review count, rating distribution, wine facts, awards and recommended drinking years where supplied.

Review rows include a string `review_id`, rating, text, language, publication time, reviewer alias, likes and comments. Public reviewer statistics include rating count, review count and follower count. A dated vintage requests reviews for that year. Every delivered review has the same `entry_id` as its associated delivered wine, including non-vintage wines. Reviews of other vintages are excluded from delivery, the review limit and billing. Reviews follow the associated wine in delivery order.

### Example output

A search with `country: US`, `wine_types: ["red"]` and `max_wines: 3` returns wine rows like this one (abridged from a real result):

```json
{
  "record_type": "catalog_entry",
  "entry_id": 172082835,
  "title": "Fortunate Son The Dreamer 2022",
  "winery": "Fortunate Son",
  "region": "Napa Valley",
  "country_code": "US",
  "vintage_year": 2022,
  "grapes": ["Cabernet Sauvignon"],
  "grapes_source": "style",
  "rating_average": 4.7,
  "ratings_count": 79,
  "price": 199,
  "currency": "USD",
  "market_country": "US",
  "url": "https://www.vivino.com/w/10472042?year=2022"
}
```

With `include_reviews` enabled, review rows follow their wine (reviewer name omitted here):

```json
{
  "record_type": "review",
  "review_id": "187251426",
  "entry_id": 6401709,
  "rating": 4.5,
  "text": "Notes of vanilla, cherry and oak. A slight whiff of coffee on the nose. We had with steak and then cheese, and it was a perfect partner.",
  "language": "en",
  "published_at": "2020-12-19T18:58:07.000Z",
  "likes": 4,
  "author_ratings_count": 419
}
```

### Vivino Scraper use cases

- **Wine retail and importers:** compare shelf prices with Vivino ratings per market, and spot well-rated wines that are priced below their peers.
- **Price research:** sort by price or discount percentage and track how merchant offers move between runs.
- **Drinks apps and catalogs:** enrich your own wine list with vintage ids, regions, grapes and rating statistics.
- **Sentiment and taste analysis:** collect written reviews with language, date and flavour words to study how a wine is described.

### Vivino Scraper compared with other Vivino actors on the Store

Other Vivino actors on the Store mostly take a list of wine names and return one row per match, or run a filtered search with a single dataset item type. This actor combines both: name search, explore filters and direct vintage URLs in one input. It keeps wine rows and review rows separate, linked by `entry_id`, so you pay for reviews only when you ask for them. It also lets you choose the market country and currency independently of the wine's origin, and empty results are free.

### Pricing and free usage

Wine rows and review rows are billed as separate events. Only delivered wine rows count toward the wine event, and only delivered review rows count toward the review event. Enabling offers or details does not create another wine row. Empty search results and unavailable inputs deliver no billable rows. Check this actor's Pricing tab for the current event prices before starting a run; price terms are managed by Apify.

Free usage is limited to ten delivered rows in total, including both wines and reviews. A wine with nine reviews fills that allowance. The requested wine and review volumes are capped before collection, and rows beyond the delivery allowance are not billed. Paid runs use the requested wine and review limits, subject to source availability and the run's configured timeout.

### Continuation and source availability

Large requests continue in bounded pages. Apify automatically follows the continuation cursor while time and delivery allowance remain. If a cursor is exposed in a result, keep every other input option unchanged when resuming. Already delivered wine rows are not repeated when the next page continues collecting reviews. Stable row identities also prevent duplicate rows from being counted twice during collection.

A source interruption can leave useful partial results. Read the warnings and partial flag before assuming that every requested detail or review was available. A wine with no written reviews remains a valid wine result. Optional offers are null when not requested or unavailable, and an empty list when the request succeeded but no merchants were listed. Missing source fields remain null rather than being guessed.

Invalid individual wine or review records are skipped with a warning while collection continues. Invalid merchant offers leave the wine result available with a warning. Review collection scans at most 1,000 source pages per wine; reaching that limit before completion also produces a warning.

### FAQ

#### Can I collect a particular vintage?

Yes. Provide a vintage URL or vintage id, or use a wine URL with its year. For example, a wine URL with `?year=2021` selects that year when Vivino lists it. Each wine result has its own vintage id and year.

#### Why do prices differ between countries?

Prices and merchants depend on the chosen market and available delivery area. The wine's origin country describes production, while `market_country` describes offers. Currency controls the amount's denomination. Keep those fields together when comparing results.

#### Does the review limit apply to the entire dataset?

`max_reviews` applies separately to each wine. `max_wines` counts unique wine vintages and excludes review rows. The free allowance applies to all delivered rows together, so it can stop a run before either requested volume is reached.

#### What happens when no wines match?

The run records a clear no-results error and does not deliver billable wine or review rows. Try a broader price range, a lower minimum rating or another market. Some wine types have limited availability in particular countries.

#### Can I use these results for a catalog join?

Use `entry_id` for a vintage and `wine_id` for the broader wine. Reviews carry their own vintage id. This source does not supply a dependable GTIN or EAN in the supported responses, so barcode joins require another source.

# Actor input Schema

## `country` (type: `string`):

Price and merchant market. Use an ISO country code; this also selects the country for requests.

## `currency` (type: `string`):

ISO currency code. Defaults to the selected country currency.

## `search` (type: `string`):

Wine name search, for example tignanello.

## `wine_types` (type: `array`):

Explore wine types.

## `grapes` (type: `array`):

Grape names or numeric grape ids as strings, validated against the captured Vivino grape list.

## `region_ids` (type: `array`):

Vivino region ids; names are not accepted.

## `origin_countries` (type: `array`):

Wine origin country codes. Separate from the price market country.

## `price_min` (type: `number`):

Minimum price in the selected currency.

## `price_max` (type: `number`):

Maximum price in the selected currency.

## `min_rating` (type: `number`):

Minimum vintage average rating. Also enables a broad explore search.

## `sort_by` (type: `string`):

Explore sort field.

## `sort_order` (type: `string`):

Explore sort direction.

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

Specific HTTPS Vivino wine URLs, vintage URLs or vintage ids as strings. Wine URLs can select ?year=2021. Takes precedence over explore filters.

## `max_wines` (type: `integer`):

Maximum unique vintages across all cursor calls, excluding review rows.

## `include_offers` (type: `boolean`):

Collect merchant names and their available prices for each vintage.

## `include_details` (type: `boolean`):

Collect exact vintage grapes, winemaker, review count and rating distribution.

## `include_reviews` (type: `boolean`):

Add separate review rows after each wine. Dated vintages request reviews for that year; non-vintage wines can include other years.

## `max_reviews` (type: `integer`):

Maximum unique review rows per wine across cursor calls.

## `cursor` (type: `string`):

Opaque continuation cursor. Keep all other input options unchanged.

## Actor input object example

```json
{
  "country": "US",
  "wine_types": [
    "red"
  ],
  "min_rating": 1,
  "sort_by": "ratings_average",
  "sort_order": "desc",
  "max_wines": 50,
  "include_offers": false,
  "include_details": false,
  "include_reviews": false,
  "max_reviews": 20
}
```

# Actor output Schema

## `results` (type: `string`):

Results in the default dataset.

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

Run delivery summary.

## `errors` (type: `string`):

Source errors.

# 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 = {
    "wine_types": [
        "red"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("s-r/vivino-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 = { "wine_types": ["red"] }

# Run the Actor and wait for it to finish
run = client.actor("s-r/vivino-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 '{
  "wine_types": [
    "red"
  ]
}' |
apify call s-r/vivino-scraper --silent --output-dataset

```

## MCP server setup

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