# Saratoga Wine Exchange Scraper (`crawlerbros/saratoga-wine-exchange-scraper`) Actor

Scrape SaratogaWine.com - a major US wine, spirits and beer retailer with 50,000+ products. Search by keyword, browse any category, or fetch exact products by ID. Returns price, category, stock, rating, images and more.

- **URL**: https://apify.com/crawlerbros/saratoga-wine-exchange-scraper.md
- **Developed by:** [Crawler Bros](https://apify.com/crawlerbros) (community)
- **Categories:** E-commerce, Automation, Integrations
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 results

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

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Saratoga Wine Exchange Scraper

Scrape live product data from [Saratoga Wine Exchange](https://www.saratogawine.com)
— a major US wine, spirits and beer retailer with 50,000+ products. No login,
no cookies, and no paid proxy required.

### What this actor does

- **Search the catalog** — free-text match against product name and description
- **Browse a category** — pull products from any of Saratoga Wine's 25 live catalog categories (collectible wines, collectible spirits, bulk deals, and more) via a dropdown
- **Lookup exact products** — fetch specific products by their numeric ID
- **Filters:** price, brand, category, rating, vintage year, stock, sale status, and Saratoga Wine's own product attributes (color, Rapid Ship, case-only, in-store, oversized bottle)
- **Empty fields are omitted** — only fields Saratoga Wine actually returns for a given product are included

### Output per product

- `productId`, `sku`, `title`
- `vintageYear` — parsed from the product title; omitted for non-vintage (NV) products
- `bottleSize` — parsed from the product title, e.g. `750ml`, `1.5L`
- `description`, `productType` — Saratoga Wine's own WooCommerce product type (`simple`, `bundle`, `variable`, `grouped`)
- `categories[]`, `category`, `categoryUrls[]`
- `brand`
- `price` — current selling price
- `priceMin` / `priceMax` — replace `price` for build-your-own bundle products, whose real price depends on which items the customer picks
- `regularPrice` — pre-discount list price, present only when the product is on sale
- `currency`
- `onSale`, `inStock`
- `stockCount` — exact units left in stock, parsed from Saratoga Wine's own "N in stock" label; omitted when the site only shows a generic "In stock" label or the product is out of stock
- `images[]`, `primaryImageUrl`
- `averageRating`, `reviewCount` — present only when the product has published customer reviews
- `caseSize` — the bottle/unit count the product must be purchased in multiples of, when Saratoga Wine enforces one
- `sourceUrl`, `recordType: "product"`, `scrapedAt`

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `mode` | string | `search` | `search` / `byCategory` / `byProductIds` |
| `searchQuery` | string | `cabernet sauvignon` | Keyword matched against product name and description (mode=search) |
| `categorySlug` | select | – | One of 25 live Saratoga Wine catalog categories (mode=byCategory) |
| `productIds` | array | – | Exact numeric product IDs (mode=byProductIds) |
| `sortBy` | string | `popularity` | `popularity` / `date` / `price` / `price-desc` / `rating` / `title` |
| `wineColor` | select | – | `red` / `white` / `rose` (mode=search/byCategory) |
| `rapidShipOnly` | boolean | `false` | Only "Rapid Ship" tagged products (mode=search/byCategory) |
| `caseOnly` | boolean | `false` | Only products sold exclusively by the case (mode=search/byCategory) |
| `inStoreTodayOnly` | boolean | `false` | Only products flagged as physically available in-store today (mode=search/byCategory) |
| `inStoreOnly` | boolean | `false` | Only products sold exclusively in-store, not shippable online (mode=search/byCategory) |
| `shipsFreeWith12Only` | boolean | `false` | Only products free-shipping-eligible as part of a 12-bottle order (mode=search/byCategory) |
| `oversizedBottleOnly` | boolean | `false` | Only oversized bottle formats, e.g. magnums (mode=search/byCategory) |
| `minPrice` / `maxPrice` | int | – | Price range (USD) |
| `category` | string | – | Category name contains this text |
| `brand` | string | – | Brand name contains this text |
| `minRating` | number | – | Minimum average rating 0-5 (most products have no reviews yet) |
| `minVintageYear` / `maxVintageYear` | int | – | Vintage-year range, parsed from the product title; drops non-vintage products when set |
| `inStockOnly` | boolean | `false` | Only emit products currently in stock |
| `onSaleOnly` | boolean | `false` | Only emit products currently discounted below their regular price |
| `maxItems` | int | `50` | Hard cap on emitted records (1-2000) |

Note: `wineColor`, `rapidShipOnly`, `caseOnly`, `inStoreTodayOnly`, `inStoreOnly`, `shipsFreeWith12Only`, and `oversizedBottleOnly` all narrow the result set
server-side (Saratoga Wine's own product attributes) but are not themselves
echoed as output fields — Saratoga Wine's API doesn't return per-product
attribute values, only lets you filter by them.

#### Example: search with price and color filters

```json
{
  "mode": "search",
  "searchQuery": "cabernet sauvignon",
  "wineColor": "red",
  "minPrice": 20,
  "maxPrice": 80,
  "maxItems": 50
}
```

#### Example: browse a category

```json
{
  "mode": "byCategory",
  "categorySlug": "collectible-wines",
  "sortBy": "price",
  "inStockOnly": true,
  "maxItems": 100
}
```

#### Example: lookup exact products by ID

```json
{
  "mode": "byProductIds",
  "productIds": ["123456", "789012"]
}
```

#### Example: on-sale rapid-ship reds under $30

```json
{
  "mode": "search",
  "searchQuery": "red wine",
  "wineColor": "red",
  "rapidShipOnly": true,
  "onSaleOnly": true,
  "maxPrice": 30
}
```

### Use cases

- **Wine & spirits retailers** — track competitor pricing and stock across categories
- **Price-comparison tools** — build a searchable export filtered by price range or rating
- **Collectible wine tracking** — monitor allocated/collectible releases via `categorySlug=collectible-wines`
- **Deal alerting** — watch for newly discounted or Rapid Ship products
- **Inventory research** — check stock counts and case-size requirements before bulk purchasing

### Limitations

When `mode=search` is combined with `wineColor` / `rapidShipOnly` /
`caseOnly` **and** `sortBy=price` (low-to-high or high-to-low), Saratoga
Wine's own Store API widens the matched pool beyond your `searchQuery`
before sorting, so price-ascending results can surface a long tail of
unrelated / discontinued low-price listings ahead of real matches. The
actor already re-verifies every result against your `searchQuery` and any
`minPrice`/`maxPrice`/`inStockOnly` filters client-side (so you'll never
see a wrong product), and scans extra pages automatically when a color/
attribute filter is active, but a narrow combination of `searchQuery` +
attribute filter + price bounds + `sortBy=price` can still legitimately
return fewer results (or 0) than the same filters with the default
`sortBy=popularity`. If you hit 0 results with an attribute filter +
`sortBy=price`, retry with `sortBy=popularity` (the default) or drop the
price sort.

Separately: if you combine an attribute filter (`wineColor` /
`rapidShipOnly` / `caseOnly` / `inStoreTodayOnly` / `shipsFreeWith12Only` / `oversizedBottleOnly`)
**with both** `minPrice` and/or `maxPrice` **and** `sortBy=price` (either
direction), the actor automatically substitutes `sortBy=popularity`
server-side instead (or `sortBy=date` when `oversizedBottleOnly` is the
active filter — see below) — Saratoga Wine's Store API gets stuck
re-returning the same narrow price bracket for 20+ pages straight under
that exact combination and would otherwise silently return 0 records even
when matches exist. Output completeness is prioritized over exact price
ordering for this specific combo; the results are still fully filtered
by your price bounds, just not sorted ascending/descending by price.
Drop either the attribute filter or the price bounds if strict price
ordering matters more than completeness for your use case.

Separately: `oversizedBottleOnly` combined with the default
`sortBy=popularity` hits a different Store API quirk — `orderby=popularity`
silently drops the `pa_oversized-bottle` attribute filter entirely and
returns the unfiltered catalog instead (confirmed live: `orderby=date`,
`=rating`, and `=title` all honor the filter correctly). The actor
automatically substitutes `sortBy=date` whenever `oversizedBottleOnly` is
set and `sortBy` is left at its `popularity` default, so you always get
correctly-filtered results without needing to change `sortBy` yourself.

### FAQ

**Does this need login or cookies?**
No. Saratoga Wine publishes its catalog through a public, unauthenticated
WooCommerce Store API.

**Does this use a paid/residential proxy?**
No proxy is required at all.

**Can I get every product on the site?**
Yes — use `mode=search` with a broad query, or iterate categories with
`mode=byCategory`, and set a high `maxItems`.

**Is this affiliated with Saratoga Wine Exchange?**
No, this is a third-party actor that reads Saratoga Wine's public product
API; it is not built or endorsed by Saratoga Wine Exchange.

**Why is `averageRating` missing from most products?**
Saratoga Wine's catalog currently has very few products with published
customer reviews, so the field (and `reviewCount`) is only present when a
product actually has ratings — never a `0` placeholder.

**Why do bundle products show `priceMin`/`priceMax` instead of `price`?**
Build-your-own bundles (`productType: "bundle"`) don't have one fixed price
since it depends on which items the customer picks; Saratoga Wine's API
reports a flat `0` for these, so the actor substitutes the real min/max
range instead of a misleading `0`.

**How current is the data?**
Every run queries Saratoga Wine's live Store API at request time — prices
and stock reflect what's on the site right now, not a cached snapshot.

**What happens if I combine `sortBy=price` with an attribute filter like `wineColor`?**
See the Limitations section above — some filter + sort combinations hit
known quirks in Saratoga Wine's own Store API; the actor works around most
of them automatically, but a few edge cases can still return fewer results
than expected.

# Actor input Schema

## `mode` (type: `string`):

What to fetch.

## `searchQuery` (type: `string`):

Keyword matched against product name and description.

## `categorySlug` (type: `string`):

Saratoga Wine catalog category to browse.

## `productIds` (type: `array`):

Exact numeric Saratoga Wine product IDs.

## `wineColor` (type: `string`):

Only return products tagged with this wine color (applies to `search` and `byCategory` modes; not supported for `byProductIds`). Uses SaratogaWine.com's own color attribute, so results are exact.

## `rapidShipOnly` (type: `boolean`):

Only return products tagged "Rapid Ship" by Saratoga Wine (applies to `search` and `byCategory` modes; not supported for `byProductIds`). Uses SaratogaWine.com's own Rapid Ship attribute, so results are exact.

## `caseOnly` (type: `boolean`):

Only return products Saratoga Wine sells exclusively by the case (applies to `search` and `byCategory` modes; not supported for `byProductIds`). Uses SaratogaWine.com's own Case Only attribute, so results are exact.

## `inStoreTodayOnly` (type: `boolean`):

Only emit products Saratoga Wine flags as physically available in-store today (its own pa\_in-store-today attribute).

## `inStoreOnly` (type: `boolean`):

Only return products Saratoga Wine flags as sold exclusively in its physical store, not shippable online (its own pa\_in-store-only attribute; applies to `search` and `byCategory` modes, not `byProductIds`).

## `shipsFreeWith12Only` (type: `boolean`):

Only return products Saratoga Wine flags as free-shipping-eligible when bought as part of a 12-bottle order (its own pa\_12-ship-free attribute; applies to `search` and `byCategory` modes, not `byProductIds`).

## `oversizedBottleOnly` (type: `boolean`):

Only return products Saratoga Wine flags as an oversized bottle format (its own pa\_oversized-bottle attribute, e.g. magnums and other large-format bottlings; applies to `search` and `byCategory` modes, not `byProductIds`).

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

Sort order used when browsing or searching.

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

Drop products priced below this value.

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

Drop products priced above this value.

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

Category name contains this text (applies on top of any mode).

## `brand` (type: `string`):

Brand name contains this text.

## `minRating` (type: `number`):

Drop products rated below this value (0-5). Fractional values (e.g. 4.5) are accepted since product ratings are fractional. Note: most Saratoga Wine products currently have no published customer reviews, so setting this above 0 will typically return 0 results.

## `minVintageYear` (type: `integer`):

Drop products whose vintage year (parsed from the product title, e.g. "... 2022 750ml") is below this value. Non-vintage (NV) products have no vintage year and are dropped when this filter is set.

## `maxVintageYear` (type: `integer`):

Drop products whose vintage year (parsed from the product title) is above this value. Non-vintage (NV) products have no vintage year and are dropped when this filter is set.

## `inStockOnly` (type: `boolean`):

Only emit products currently in stock.

## `onSaleOnly` (type: `boolean`):

Only emit products currently discounted below their regular price.

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

Hard cap on emitted records.

## Actor input object example

```json
{
  "mode": "search",
  "searchQuery": "cabernet sauvignon",
  "productIds": [],
  "wineColor": "",
  "rapidShipOnly": false,
  "caseOnly": false,
  "inStoreTodayOnly": false,
  "inStoreOnly": false,
  "shipsFreeWith12Only": false,
  "oversizedBottleOnly": false,
  "sortBy": "popularity",
  "inStockOnly": false,
  "onSaleOnly": false,
  "maxItems": 50
}
```

# Actor output Schema

## `products` (type: `string`):

Dataset containing all scraped Saratoga Wine Exchange products.

# 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 = {
    "mode": "search",
    "searchQuery": "cabernet sauvignon",
    "productIds": [],
    "wineColor": "",
    "rapidShipOnly": false,
    "caseOnly": false,
    "inStoreTodayOnly": false,
    "inStoreOnly": false,
    "shipsFreeWith12Only": false,
    "oversizedBottleOnly": false,
    "sortBy": "popularity",
    "inStockOnly": false,
    "onSaleOnly": false,
    "maxItems": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("crawlerbros/saratoga-wine-exchange-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 = {
    "mode": "search",
    "searchQuery": "cabernet sauvignon",
    "productIds": [],
    "wineColor": "",
    "rapidShipOnly": False,
    "caseOnly": False,
    "inStoreTodayOnly": False,
    "inStoreOnly": False,
    "shipsFreeWith12Only": False,
    "oversizedBottleOnly": False,
    "sortBy": "popularity",
    "inStockOnly": False,
    "onSaleOnly": False,
    "maxItems": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("crawlerbros/saratoga-wine-exchange-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "mode": "search",
  "searchQuery": "cabernet sauvignon",
  "productIds": [],
  "wineColor": "",
  "rapidShipOnly": false,
  "caseOnly": false,
  "inStoreTodayOnly": false,
  "inStoreOnly": false,
  "shipsFreeWith12Only": false,
  "oversizedBottleOnly": false,
  "sortBy": "popularity",
  "inStockOnly": false,
  "onSaleOnly": false,
  "maxItems": 50
}' |
apify call crawlerbros/saratoga-wine-exchange-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=crawlerbros/saratoga-wine-exchange-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/qgU2Q4x9gLiag0A4H/builds/ZccN7jBrREs6aLUDq/openapi.json
