Vivino Wine Scraper $1.2💰 Ratings, Prices, Reviews & Offers
Pricing
from $1.20 / 1,000 wine results
Vivino Wine Scraper $1.2💰 Ratings, Prices, Reviews & Offers
From $1.2/1K. Scrape Vivino.com for wine ratings, prices, taste profiles, food pairings, grapes, and reviews. Search by wine names or URLs, or discover wines by type, price, rating, country, and grape. Returns 50+ fields, including multi-merchant offers and value scores.
Pricing
from $1.20 / 1,000 wine results
Rating
0.0
(0)
Developer
Abot API
Maintained by CommunityActor stats
1
Bookmarked
6
Total users
0
Monthly active users
19 days ago
Last modified
Categories
Security
Limited permissionsShare
Vivino Scraper: Wine Ratings, Prices and Tasting Notes
Vivino Scraper turns Vivino into structured wine data you can pull on demand. Look up wines by name or paste Vivino links, or skip the list entirely and discover wines by type, price, rating, origin country and grape with full sorting. Records carry ratings, multi merchant prices, Vivino's value and competitiveness scores where Vivino lists offers, plus a taste profile by default and optional reviews, then export to JSON, CSV or Excel, or read the results straight through the API.
Why This Scraper?
- Two ways to find wines. Paste an exact list of names and/or Vivino URLs (one record each), or set filters and a sort order and let the actor walk the catalog for you.
- 50+ fields per wine, including a full taste profile, multi merchant price offers, and Vivino's value and competitiveness scores.
- Real discovery filters and sort. Wine type, minimum rating, price range, origin country and grape, sorted by rating, price, popularity or biggest discount first.
- Enrichment on demand. Taste profile (acidity, tannins, sweetness, intensity, flavor notes) is fetched by default and can be turned off; user reviews are off by default and can be turned on. Any wine that gets either adds one combined enrichment charge, not two.
- Market aware. Choose the pricing country and currency, and echo a ship-to country on every record.
- Resume and recurring updates. One interrupted run resumes automatically, and Incremental mode returns only new and updated wines by default on every scheduled run.
- Resilient connections. A refused request is retried on a different connection automatically, and each retry uses a fresh connection of the same kind you selected; a different connection type is used only when the selected one is refused outright or returns the wrong market's results, no extra setup needed.
Use Cases
- Wine shops and marketplaces: track competitor pricing, discounts and merchant offers for the wines you carry.
- Wine apps and recommendation engines: build a searchable catalog with ratings, taste profiles and food pairings.
- Price and deal monitoring: schedule Incremental mode against a watch list and get only the wines whose price, rating or availability changed.
- Sommeliers and content teams: pull tasting notes, food pairings and reviews to write wine guides and shelf talkers.
- Market research and data science: build historical rating and pricing datasets across countries and currencies.
Data You Get
Sample shape: values are illustrative placeholders, not from a live record.
| Field | Example |
|---|---|
wineId | 1234567 |
vintageId | 89012345 |
name | "Sample Winery Reserve Red 2019" |
winery | "Sample Winery" |
vintage | 2019 |
wine_type | "Red" |
region | "Sample Region" |
country | "France" |
country_code | "fr" |
appellation | "Sample Appellation" |
grape_varieties | ["Cabernet Sauvignon", "Merlot"] |
average_rating | 4.3 |
ratings_count | 1200 |
price | 49.9 |
currency | "EUR" |
merchant_url | Vivino checkout link for the matched merchant offer |
vfm_score / vfm_category | 4.1 / "great" (Vivino's value-for-money score and label) |
is_natural | false |
image_url | "https://images.vivino.com/thumbs/000000000_pb_x960.png" |
food_pairings | ["Beef", "Lamb", "Game"] |
description | Style description text |
taste_profile | { body, acidity, tannins, sweetness, fizziness, intensity, flavor_notes, flavor_groups } (when enabled) |
reviews | [ { rating, note, language, user_name, created_at } ] (when enabled) |
prices | [ { amount, currency, merchant_url, discount_percent } ], every merchant offer for this vintage |
vivino_url | "https://www.vivino.com/sample-winery-reserve-red/w/1234567" |
Every record also carries the wine's overall, all-vintage rating separately from this vintage's rating (wine_average_rating, wine_ratings_count), the full pricing detail behind the headline price (discounted_from, discount_percent, discount_amount, is_on_sale, bottle_volume_ml), Vivino's competitiveness score and tier (cmp_score, cmp_tier), a larger label image (label_image_url), winery identity and status (winery_id, winery_seo_name, winery_status), region identity (region_id, region_name_en), style copy (style_name, body_description, acidity_description, varietal_name, regional_name, interesting_facts, blurb), Vivino's own wine rank (vivino_rank), and provenance fields describing how the row was produced rather than the wine itself (matchScore, isAlternative, requested_vintage, searchQuery, shipTo, inputSource, scrapedAt). Each row also has a ready-to-use headline rating (display_rating, display_ratings_count, display_rating_scope = vintage or wine, vintage_rating_status), resolution_status, wine_description, enrichment_errors (taste, review or alcohol fetches that did not complete) and rate_limited_during_search (true when Vivino refused one or more requests while that row's search or listing page was being resolved, even though the row was recovered by retrying on a fresh connection); failure_reason, error and errorMessage are always empty because wines that cannot be resolved are never pushed or billed. alcohol (ABV in percent) is read from Vivino's vintage record as part of the detail enrichment, so it is filled when Include taste profile or Include reviews is on and stays empty (with alcohol listed in enrichment_errors when the fetch failed) otherwise; it adds no separate charge. lwin and lwin_canonical are not published by Vivino and are always empty.
How to Use
- Pick a Mode:
lookup(exact wines by name or URL) ordiscovery(find wines by filter and sort). - In lookup mode, list wine names and/or Vivino URLs. In discovery mode, set your filters and sort order instead.
- Turn on taste profile and/or reviews if you want them, set the market country and currency, and set Max wines to control run size and cost.
- Click Start, then download the dataset as JSON, CSV or Excel, or read it through the API.
Look up wines by name:
{"mode": "lookup","wines": ["Opus One 2019", "Dom Pérignon", "Cloudy Bay Sauvignon Blanc"],"countryCode": "US","currencyCode": "USD"}
URL shapes in lookup mode. Besides wine pages, the wines list accepts these Vivino URL shapes (each is in the input example list):
| Shape | Example | What you get |
|---|---|---|
| Wine page | https://www.vivino.com/cloudy-bay-sauvignon-blanc/w/18978 | that wine |
| Wine page with vintage | .../w/18978?year=2015 | that vintage |
| Market and language prefix | https://www.vivino.com/FR/en/cloudy-bay-sauvignon-blanc/w/18978 | that wine |
| Short form | https://www.vivino.com/w/18978 | that wine |
| Search page | https://www.vivino.com/search/wines?q=opus+one | matching wines |
| Explore page with filters | https://www.vivino.com/explore?country_code=FR&min_rating=4&wine_type_ids[]=1 | matching wines |
| Grape, region or winery page | .../grapes/sauvignon-blanc, .../wine-regions/marlborough, .../wineries/cloudy-bay | the wines of that exact grape, region or winery (matched by Vivino's own id, not a text search) |
| Explore JSON API URL | https://www.vivino.com/api/explore/explore?... | matching wines |
| Reviews JSON API URL | https://www.vivino.com/api/wines/18978/reviews | the wine behind it |
Listing shapes return up to 100 wines per URL, capped by Max items; when a listing has more, the run log says so and incremental mode does not report the unread wines as expired. Any other URL shape is skipped with a warning in the run log, never silently. If you leave the wines field untouched, the actor now scrapes the example list above (names plus one URL of each shape); each returned wine is a normal billed result, so set your own list or Max items to control cost.
Look up wines by URL, with taste profile and reviews:
{"mode": "lookup","wines": ["https://www.vivino.com/cloudy-bay-sauvignon-blanc/w/18978","https://www.vivino.com/chateau-margaux-premier-grand-cru-classe/w/1127795?year=2015"],"includeTasteProfile": true,"includeReviews": true,"maxReviewsPerWine": 20}
Discover top rated French reds between 50 and 150, sorted by rating:
{"mode": "discovery","wineTypes": ["Red"],"originCountries": ["fr"],"minRating": 4,"priceMin": 50,"priceMax": 150,"sortBy": "rating_desc","countryCode": "US","currencyCode": "USD","maxItems": 50}
Recurring monitoring of a watch list (schedule this daily):
{"mode": "lookup","wines": ["https://www.vivino.com/cloudy-bay-sauvignon-blanc/w/18978?year=2022"],"incrementalMode": true,"stateKey": "my-watchlist"}
Run it from your code
Python:
from apify_client import ApifyClientclient = ApifyClient("<YOUR_APIFY_TOKEN>")run = client.actor("abotapi/vivino-wine-data-scraper").call(run_input={"mode": "lookup", "wines": ["Opus One 2019"]})for wine in client.dataset(run["defaultDatasetId"]).iterate_items():print(wine["name"], wine["average_rating"], wine["price"])
JavaScript:
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });const run = await client.actor('abotapi/vivino-wine-data-scraper').call({ mode: 'lookup', wines: ['Opus One 2019'] });const { items } = await client.dataset(run.defaultDatasetId).listItems();
Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the Integrations tab.
Resume and recurring updates
There are two different things here, pick the one that matches what you're doing:
| Need | Use |
|---|---|
| A run stopped and should continue | resumeFromRunId (or the automatic same-run checkpoint, no input needed) |
| Run the same wine list or discovery filters on a schedule and receive only changes | incrementalMode |
| Keep separate monitoring campaigns for similar searches | distinct stateKey values |
| Run a normal full snapshot | leave both off |
Resume (resumeFromRunId) continues one specific interrupted or previous run: paste a run ID or dataset ID and this run skips wines already collected there, returning only the remaining new ones. An automatic same-run checkpoint also protects against platform migrations, so a run that is restarted by the platform continues from where it left off with no extra input.
Incremental mode (incrementalMode) is for a schedule, for example daily: the actor remembers the previous run of the same wine list or discovery filters by itself, so you never paste a run ID. The first run returns everything as NEW. Later runs return only NEW and UPDATED wines by default; unchanged wines are suppressed and not billed. REAPPEARED only appears after an earlier run with emitExpired on and a complete scan tombstoned the wine as gone. Turn on emitUnchanged or emitExpired only when you also want those rows returned, and billed for. State is isolated per wine list, discovery filter set and enrichment setup automatically; set stateKey to name or deliberately share a monitoring campaign.
A wine is UPDATED when any returned field other than scrapedAt changes (price, discount, merchant, vintage, ratings, taste profile, reviews); changedFields names them. Use ignoreFieldsForChanges to exclude further noisy fields; the stored baseline is re-compared under your current list, so changing it causes no one-time wave of UPDATED rows.
Every record returned in incremental mode also carries:
| Field | Description |
|---|---|
changeType | NEW, UPDATED, UNCHANGED, REAPPEARED, or EXPIRED |
changedFields | Top-level fields that changed since last seen; non-empty only for UPDATED |
firstSeenAt | When this wine was first observed by this monitoring campaign |
lastSeenAt | When this wine was last observed |
REAPPEARED and EXPIRED are not everyday outcomes. EXPIRED is only produced once a run has completely scanned its tracked wines or filters, meaning nothing was capped by maxItems, no resumeFromRunId was used, and every list entry or catalog page was actually reached. A capped run, a resumed run, or a run where even one wine could not be resolved leaves EXPIRED detection switched off for that run, so a wine that was merely unreachable this time is never wrongly reported as gone. REAPPEARED only follows an earlier run that genuinely marked a wine EXPIRED.
Read this before you schedule a discovery run: Vivino's answers vary per request
Vivino tailors what it returns to where the request appears to come from, and that location is not guaranteed to be identical from one run to the next. Two things follow, both measured on this actor across several back to back identical runs of a small discovery walk minutes apart:
- The discovery result set itself can move. Repeated identical runs can return different sets of wines.
- A wine that does appear on two runs can come back as a different vintage from a different merchant, reported
UPDATEDwithchangedFieldsnaming price and identity fields together, then flip back on a later run.
So a scheduled discovery run can report, and bill, a real share of NEW and UPDATED rows every time. That is variance in what Vivino serves, not a fault in change detection: the actor is correctly reporting that the record it just received is not the record it stored.
For stable, low noise scheduled monitoring:
- Prefer lookup mode over discovery mode. Give an explicit wine list, and ideally paste Vivino URLs carrying a
?year=, which pins the vintage and removes most of the churn above. - Set
countryCodeandcurrencyCodeexplicitly and keep them identical on every run of a campaign. - Choose the Residential connection option and pin its country to that same
countryCode, so requests stay inside one market instead of moving between markets. - Read
changedFieldsbefore acting on a row. A row whosechangedFieldsnamesvintage,vintageIdornameis a different vintage of the same wine, not a change to the wine you were watching. - Leave
emitUnchangedoff, the default, so wines that genuinely did not move are neither returned nor billed.
Send results into your apps (MCP connectors)
Optionally pipe each record into the apps you already use, through Model Context Protocol (MCP) connectors. This is an extra delivery step after the scrape: the Apify dataset is never changed.
What gets written to the connector: a condensed, human-readable summary of each wine, not the full JSON. Each item becomes one entry with a title and its key fields flattened to plain text. The complete record always stays in the Apify dataset.
- Authorize a connector once under Apify, Settings, API & Integrations (Notion, Linear, Airtable, or Apify).
- Select it in the "Pipe results into your apps" input field. If the picker is empty, you haven't authorized a connector yet.
- For Notion, also set
notionParentPageUrlto the page where items should be created.
The connection is mediated by Apify's MCP proxy, so this actor never sees your third-party credentials. Leave the field empty to skip.
Input Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
mode | string | lookup | lookup: scrape the exact wines you list. discovery: find wines by filter and sort. |
wines | array | none (prefill: ["Dom Pérignon", "Opus One 2019", "https://www.vivino.com/cloudy-bay-sauvignon-blanc/w/18978"]) | Lookup mode: mixed list of wine names and Vivino URLs, one record each. |
searchMode | string | auto | Vintage handling for name lookups: auto, name_and_vintage, name_only. |
matchingMode | string | basic | basic takes the top match. advanced also surfaces one alternative candidate, a normal row that counts toward Max wines, gets the same enrichment, and is billed the same as any other row. |
wineTypes | array | [] (all types) | Discovery filter: Red, White, Sparkling, Rosé, Dessert, Fortified. |
minRating | integer | none | Discovery filter: minimum Vivino average rating (1 to 5). |
priceMin | integer | none | Discovery filter: lowest price in the selected currency. |
priceMax | integer | none | Discovery filter: highest price in the selected currency. |
originCountries | array | [] (all) | Discovery filter by wine origin, ISO alpha-2 codes, e.g. fr, it, us. |
grapes | array | [] (all) | Discovery filter by Vivino grape IDs. |
sortBy | string | relevance | relevance, rating_desc, rating_asc, price_asc, price_desc, popularity, discount_desc. |
countryCode | string | FR | Market country for pricing and availability, ISO alpha-2. |
currencyCode | string | EUR | Currency for prices, ISO 4217. |
shipTo | string | none | Optional ship-to country, echoed on each record. |
includeTasteProfile | boolean | true | Fetch acidity, tannins, sweetness, fizziness, intensity and flavor notes (body is currently always empty). |
includeReviews | boolean | false | Fetch user reviews per wine. Increases run time and cost. |
maxReviewsPerWine | integer | 20 | Maximum reviews per wine when reviews are enabled (1 to 100). |
maxItems | integer | 20 | Caps the total wine records returned. Set 0 for unlimited. |
maxPages | integer | none (unlimited, walks every result page) | Discovery mode only: optional bound on pages walked. The run still stops at Max wines. |
proxy | object | Apify Proxy (default group) | Connection settings. Choose Residential for higher reliability on large or frequent runs. |
mcpConnectors | array | none | Optional: send a summary of each record to apps you authorized under Integrations. |
notionParentPageUrl | string | none | Notion connector only: page under which records are created. |
maxNotifyListings | integer | 50 | Cap on items written to each connector per run. Does not affect the dataset. |
resumeFromRunId | string | none | Continue one specific previous run or dataset: wines already collected there are skipped, so this run only appends new ones. |
incrementalMode | boolean | false | Daily or recurring monitoring of the same wine list or discovery filters. Returns only new and updated wines on later runs by default; reappeared wines only after an earlier emitExpired run has marked one gone. |
stateKey | string | none | Optional name for a monitoring campaign, so its incremental state stays stable or is deliberately shared. Auto-derived when left empty. |
emitUnchanged | boolean | false | Incremental mode only. Also return wines unchanged since the last run, marked UNCHANGED. Adds and bills extra rows you already have. |
emitExpired | boolean | false | Incremental mode only. Also return wines from a previous run no longer found, marked EXPIRED, once a run has fully scanned the tracked wines or filters. Adds and bills extra synthetic rows. |
ignoreFieldsForChanges | array | none | Incremental mode only. Field names (dotted path for nested fields) excluded from change detection, on top of scrapedAt. Ignored fields stay in the row but never cause UPDATED or appear in changedFields. |
Output Example
Sample shape: values are illustrative placeholders, not from a live record.
{"wineId": 1234567,"vintageId": 89012345,"name": "Sample Winery Reserve Red 2019","winery": "Sample Winery","vintage": 2019,"wine_type": "Red","region": "Sample Region","country": "France","country_code": "fr","grape_varieties": ["Cabernet Sauvignon", "Merlot"],"average_rating": 4.3,"ratings_count": 1200,"price": 49.9,"currency": "EUR","vfm_category": "great","is_natural": false,"food_pairings": ["Beef", "Lamb"],"taste_profile": {"body": null,"acidity": 3.1,"tannins": 3.8,"sweetness": 1.6,"fizziness": null,"intensity": 3.7,"flavor_notes": ["black fruit", "oak", "leather"]},"reviews": [{ "rating": 4.5, "note": "Sample review text.", "language": "en", "user_name": "reviewer", "created_at": "2026-01-01T00:00:00.000Z" }],"vivino_url": "https://www.vivino.com/sample-winery-reserve-red/w/1234567","scrapedAt": "2026-01-01T00:00:00.000Z","inputSource": "search"}
Plan Requirement
Apify datacenter proxies usually work for this site, so the default connection setting is enough for most runs. Vivino's CDN refuses some requests at random (an HTTP 403 "Request blocked" page), so the actor retries a refused request on a different connection automatically; this is intermittent, not a permanent block. For large or frequent discovery runs, choosing the Residential option under Proxy configuration gives the most consistent pricing and match results. Apify free plans do not include residential proxies; if the selected connection is unavailable the run logs "The selected connection is unavailable to this run. Apify free plans do not include residential proxies." and continues on a backup connection, and the lines it serves are marked "Served over the backup connection (the default connection was unavailable)." Upgrade at https://apify.com/pricing for residential access, or supply your own proxy URLs. If you turn proxies off in Proxy configuration, requests go out directly from the Apify platform with no proxy and no backup connection; Vivino may refuse some of them, and a request that is refused is simply retried.
FAQ
How much does it cost?
You pay per record returned, plus one combined enrichment charge for any wine that gets taste profile (on by default) or reviews (off by default). The Pricing tab shows the current rates. Use Max wines to cap the cost of any run.
Is it legal to scrape Vivino?
This actor collects only publicly available wine data. You are responsible for how you use it: follow Vivino's terms and the laws that apply to you, and get legal advice if you plan commercial redistribution. Ratings and prices are generally facts, but labels, photos and review text may be subject to third-party rights.
Can I get only new or changed wines on a schedule?
Yes. Schedule the actor from the Schedules tab and turn on Incremental mode. Each run then returns only new and updated wines by default, plus reappeared wines once an earlier run with Emit expired on has marked one gone, and unchanged ones are not billed. Lookup mode with pinned-vintage URLs gives the most stable results; see "Read this before you schedule a discovery run" above if you're scheduling discovery mode.
Why does a scheduled discovery run keep reporting changes even when nothing looks different?
Vivino answers from the location a request appears to come from, so a discovery walk run minutes apart can return a different set of wines, or quote the same wine as a different vintage from a different merchant. That reads as UPDATED because the record genuinely differs from what was stored, not because change detection is wrong. Pin your market with countryCode/currencyCode and a Residential connection, or switch to lookup mode with ?year= URLs, to reduce it.
Why did my run fail instead of returning an empty dataset?
A run fails when it cannot start correctly (for example an invalid resumeFromRunId, or combining incrementalMode with resumeFromRunId once that campaign already has saved state) or when no results page could be loaded at all. In that last case the run ends FAILED with a connection message instead of an empty dataset: "Could not load any results page (the requests failed), so nothing was collected. This is a connection problem, not an empty search. Please try again later." If your account has no proxy access the message says "The proxy connection was refused for this account" and points to https://apify.com/pricing. A single wine that could not be matched, or one page that could not be fetched, does not fail the run: it is skipped, logged, never billed, and reported in the run's status message and log, while every other wine is still returned.
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:
| 🍷 Wine-Searcher Scraper Look up wines on wine-searcher.com by name, URL, or LWIN code. Returns 30+ fields... | 🍷 Dan Murphy’s Scraper Scrape Dan Murphy’s products by category, keyword, or specials. Extract 45+ fields... |
| 🍷 Untappd Beer, Brewery and Check-in Scraper Scrape Untappd beers, breweries, venues and check-ins by keyword, brewery, Top Rated... | 🥦 ResQ Club Scraper Scrape ResQ Club surplus food offers in Finland, Sweden and Estonia: name, price, current... |
| 🥦 Coles AU Scraper Scrape Coles.com.au grocery products and customer reviews. Search by keyword, browse... | 🥦 FoodHero Scraper Scrape FoodHero surplus grocery deals across Canada by area, store or offer ID. Extract... |
💬 Support
- 🐞 Found a bug or a missing field? Open a ticket on the Issues tab. 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.