# Whole Foods Scraper: Store Prices, Sales & Prime Deals by ZIP (`zaiq/whole-foods-scraper`) Actor

Scrape Whole Foods Market products and prices for any store by ZIP code or store ID: regular, sale and Prime member prices with sale dates, unit price, UPC, brand, size, diets, ingredients and stock. Search terms, category pages or product URLs. Pay only for products delivered.

- **URL**: https://apify.com/zaiq/whole-foods-scraper.md
- **Developed by:** [Zaiq](https://apify.com/zaiq) (community)
- **Categories:** E-commerce, Automation
- **Stats:** 1 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 products

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

## Whole Foods Market Scraper: Store Prices, Sales and Prime Member Prices by ZIP Code

Get Whole Foods Market products with the prices of the store you choose: the regular price, the sale price everyone
pays, the Prime member price, sale start and end dates, a unit price, the UPC, brand, size, category, diets,
ingredients and whether the store has the product. Choose stores by ZIP code or store number; prices differ from store
to store, and every row says which store its price belongs to.

### What it does

- Finds the store for each ZIP code you give (the same store wholefoodsmarket.com picks for that ZIP code), or uses
  the store numbers you give.
- Runs your search terms, category pages and product URLs at every store, following pages until your limit.
- Returns one row per product per store: regular price, sale price, Prime member price, sale dates, a unit price per
  fluid ounce, ounce or count, UPC, brand, size, category path, image, product URL and availability at that store.
- With product details on (the default), also returns ingredients, diets (organic, vegan, gluten-free and so on) and
  certifications.
- Deals only: returns just the products on sale or with a Prime member price.
- The same product found twice at one store (for example by two search terms) is returned and charged once.

The data comes from the catalogue wholefoodsmarket.com shows for each store. No login, no account and no Amazon
sign-in are used.

### Who uses it

- **Price monitoring and retail analysts** tracking Whole Foods prices and promotions across regions.
- **Brands and distributors** checking their shelf prices, sale depth and Prime member pricing store by store.
- **Price comparison and shopping apps** that need store-level prices matched by UPC.
- **Researchers** studying grocery prices and how they vary between cities.

### Example

The same 365 by Whole Foods Market organic milks at five stores, from a run on 4 October 2026 (ZIP codes 78704,
98004, 60614, 10001 and 94110):

| Product (UPC) | Austin TX | Bellevue WA | Chicago IL | New York NY | San Francisco CA |
|---|---|---|---|---|---|
| Organic Lactose Free Whole Milk, 64 fl oz (099482514754) | $5.49 | $5.49 | $5.39 | $5.99 | $6.49 |
| Organic Whole Milk, 128 oz (099482516635) | $7.49 | $7.59 | $7.99 | not sold there | not sold there |

"Not sold there" came back as a free row for the product URL at those two stores.

In an earlier run the same day, 9 of the 13 products found at three or more of five stores had different prices.

Other test runs on 4 October 2026:

- 350 products for the search term "cheese" at the San Francisco (Noe Valley) store in 53 seconds with product details,
  or 9 seconds without; 343 of the 350 had a UPC.
- Deals only, New York (Manhattan West): 50 products, each with its sale price, Prime member price and sale end date,
  for example a 12 oz coffee at $16.99 regular, $13.20 on sale and $11.88 for Prime members until 6 October.
- A ZIP code with no Whole Foods store nearby (99950, Alaska) came back as a free row saying so.

### Input

```json
{
  "searchTerms": ["organic whole milk", "sourdough bread"],
  "categoryUrls": ["https://www.wholefoodsmarket.com/products/dairy-eggs"],
  "productUrls": ["https://www.wholefoodsmarket.com/product/365-by-whole-foods-market-organic-whole-milk-128-oz-b09fblv8qy"],
  "zipCodes": ["78704", "98004", "10001"],
  "maxItemsPerQuery": 100,
  "maxItems": 1000,
  "onSaleOnly": false,
  "includeDetails": true
}
```

| Field | What it does |
|---|---|
| `searchTerms` | Words to search for, as a shopper would type them. Each term is searched at every store. |
| `categoryUrls` | Category pages such as `https://www.wholefoodsmarket.com/products/dairy-eggs` or `.../products/milk-cream`, or just the last part (`milk-cream`). |
| `productUrls` | Product pages, or Amazon ASINs such as `B09FBLV8QY`. Each is checked at every store. |
| `zipCodes` | Five-digit ZIP codes. Each gives the store wholefoodsmarket.com picks for it. |
| `storeIds` | Whole Foods store numbers, for example `10145` (Lamar, Austin TX). Shown on every row as `storeId`. |
| `maxItemsPerQuery` | Products per search term or category at each store (default 100). |
| `maxItems` | Products in total (default 1,000; 0 means no limit, your spending limit still applies). |
| `onSaleOnly` | Only products on sale or with a Prime member price. |
| `includeDetails` | Open each product's page for its UPC, ingredients, diets, certifications, category path and availability (default on). Off is faster. |
| `includeFailures` | Add a free row for each ZIP code without a store, empty search or unreadable product (default on). |
| `maxConcurrency`, `proxyConfiguration` | Jobs at once (default 4) and the proxy (US residential by default). |

### Output

One row per product per store. A row from the run above, with long text shortened:

```json
{
  "retailer": "Whole Foods Market",
  "storeId": "10145",
  "storeName": "Whole Foods Market Lamar",
  "storeAddress": "525 N Lamar Blvd.",
  "storeCity": "Austin",
  "storeState": "TX",
  "storeZip": "78703",
  "requestedZip": "78704",
  "productId": "B09FBL77P3",
  "upc": "099482516673",
  "name": "Organic Whole Milk, 64 FZ",
  "brand": "365 by Whole Foods Market",
  "size": "64 FZ",
  "sizeQuantity": 64,
  "sizeUnit": "fl oz",
  "price": 4.49,
  "regularPrice": 4.49,
  "salePrice": null,
  "onSale": false,
  "loyaltyPrice": null,
  "loyaltyPriceLabel": null,
  "currency": "USD",
  "unitPrice": 0.0702,
  "unitPriceUnit": "fl oz",
  "unitPriceComputed": true,
  "promotionText": null,
  "promotionStart": null,
  "promotionEnd": null,
  "inStock": true,
  "category": "Milk & Cream",
  "categoryPath": ["Dairy & Eggs", "Milk & Cream"],
  "imageUrl": "https://m.media-amazon.com/images/S/assets.wholefoodsmarket.com/PIE/product/41AmX6HGOvL.jpg",
  "productUrl": "https://www.wholefoodsmarket.com/product/365-by-whole-foods-market-organic-whole-milk-64-fz-b09fbl77p3",
  "source": "365 organic whole milk",
  "sourceType": "search",
  "position": 3,
  "scrapedAt": "2026-10-04T15:59:32Z",
  "charged": true,
  "dietTags": ["Organic"],
  "certifications": ["Certified Organic"],
  "ingredients": "ORGANIC MILK VITAMIN D3"
}
```

- `price` is what a shopper pays without Prime: the sale price when there is one, otherwise the regular price.
  `loyaltyPrice` is the Prime member price (`loyaltyPriceLabel`: "Prime member price"), with `promotionStart` and
  `promotionEnd`.
- `unitPrice` is worked out from the price and the size (`unitPriceComputed: true`), per fluid ounce, ounce or count.
  It is left empty when the size could mean one item or the whole pack (for example "4PK, 12 fl oz"). Products sold
  by the pound (`soldByWeight: true`) have their price per pound.
- `upc` is the 12-digit UPC-A. Whole Foods publishes it without the check digit; the check digit is added.
- `requestedZip` is the ZIP code you gave that led to the store; it is empty when you chose the store by number.
- Fields the retailer does not publish (aisle, stock level, rating, SNAP) are present and empty, so rows from the
  other grocery scrapers in this series line up column for column.
- Failure rows have `error` set and `charged: false`.
- The run's key-value store has `RUN-SUMMARY`: stores used, products written, failures, stop reason and traffic.

### Where the prices come from

Each price is the price wholefoodsmarket.com shows for that store at the time in `scrapedAt`. Prices differ between
stores and change with weekly sales; always use the store columns (`storeId`, `storeName`, `storeZip`) with the price.
The site's prices are its in-store catalogue prices; delivery or pickup ordered through Amazon can be priced
differently. The Actor does not read Amazon.com.

### Use it from code, automations and AI agents

- **API**: run it and get the results in one call: `POST https://api.apify.com/v2/acts/zaiq~whole-foods-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN` with the input as JSON. The API tab on this page has ready-made Python, JavaScript and cURL examples.
- **Python** (`pip install apify-client`):

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_TOKEN")
run = client.actor("zaiq/whole-foods-scraper").call(run_input={'searchTerms': ['milk'], 'zipCodes': ['78704'], 'maxItemsPerQuery': 20})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)
```

- **No code**: connect it to Google Sheets, Zapier, Make or n8n from the Integrations tab, schedule runs in Apify Console, and get a webhook when a run finishes.
- **AI agents (MCP)**: add it as a tool to Claude, Cursor or any MCP client through Apify's MCP server: `https://mcp.apify.com?tools=zaiq/whole-foods-scraper`.

### Pricing

You pay only for product rows: $0.005 per product ($5 per 1,000), Apify platform usage included. Failure rows,
duplicates and retries are free. Your spending limit for the run is respected: the run stops cleanly before going over
it.

### Limits

- One store per ZIP code: the one wholefoodsmarket.com assigns. To compare several stores in one city, give their
  store numbers.
- A search or category returns at most 10,000 products (the site's own limit); split big categories into smaller ones.
- Product details add one small request per product; for large daily price checks, switch them off.
- No ratings, reviews, aisle locations or stock counts: wholefoodsmarket.com does not publish them.
- Only stores in the United States.

### FAQ

**Do I need a Whole Foods or Amazon account?** No. The Actor reads the public store catalogue and never signs in.

**How do I find a store number?** Run once with a ZIP code: every row has `storeId`, `storeName` and the address.

**Why is a product missing at one store?** Stores carry different ranges. A product URL that a store does not carry
comes back as a free row saying so.

**Why is the unit price empty for some products?** When the size is ambiguous (a pack count next to a size), the
Actor does not guess; the price and size are still there.

**Can I get only deals?** Yes: switch on Deals only. Every row then has a sale price or a Prime member price.

# Actor input Schema

## `searchTerms` (type: `array`):

Words to search for on wholefoodsmarket.com, as a shopper would type them (for example milk, organic eggs, sourdough bread). Each term is searched at every store.

## `zipCodes` (type: `array`):

Prices differ by store. Each ZIP code gives the Whole Foods store the website itself picks for it (one store per ZIP).

## `storeIds` (type: `array`):

Optional, instead of or as well as ZIP codes. Whole Foods store numbers such as 10145 (Lamar, Austin TX) or 10153 (Bellevue WA). The store number is shown on each row as storeId.

## `categoryUrls` (type: `array`):

Optional. Category pages from wholefoodsmarket.com/products, such as https://www.wholefoodsmarket.com/products/dairy-eggs or https://www.wholefoodsmarket.com/products/milk-cream. A bare category name such as milk-cream works too.

## `productUrls` (type: `array`):

Optional. Product pages such as https://www.wholefoodsmarket.com/product/365-by-whole-foods-market-organic-whole-milk-128-fl-oz-b074h6m6xs, or Amazon ASINs such as B074H6M6XS. Each is checked at every store.

## `maxItemsPerQuery` (type: `integer`):

Stop each search term or category after this many products at each store. Pages are followed until then.

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

The run stops after this many product rows. 0 means no limit (your spending limit still applies).

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

Only products on sale or with a Prime member price.

## `includeDetails` (type: `boolean`):

Also open each product's page: adds the UPC, ingredients, diets and certifications, the full category path and whether the store has it. About one extra small request per product; switch off for the fastest price monitoring.

## `includeFailures` (type: `boolean`):

Add a free row (with an error field) for each ZIP code without a store, search without results or product that could not be read. Switch off to keep only product rows.

## `maxConcurrency` (type: `integer`):

How many searches, categories or product lists run at the same time.

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

US residential proxies are used by default (the sites serve US visitors).

## Actor input object example

```json
{
  "searchTerms": [
    "milk"
  ],
  "zipCodes": [
    "78704"
  ],
  "maxItemsPerQuery": 20,
  "maxItems": 20,
  "onSaleOnly": false,
  "includeDetails": true,
  "includeFailures": true,
  "maxConcurrency": 4,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}
```

# Actor output Schema

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

Dataset with one row per product per store (failure rows are free and have an error field).

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

Stores used, products written, failures, stop reason and approximate traffic.

# 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 = {
    "searchTerms": [
        "milk"
    ],
    "zipCodes": [
        "78704"
    ],
    "maxItemsPerQuery": 20,
    "maxItems": 20,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "US"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("zaiq/whole-foods-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 = {
    "searchTerms": ["milk"],
    "zipCodes": ["78704"],
    "maxItemsPerQuery": 20,
    "maxItems": 20,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "US",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("zaiq/whole-foods-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 '{
  "searchTerms": [
    "milk"
  ],
  "zipCodes": [
    "78704"
  ],
  "maxItemsPerQuery": 20,
  "maxItems": 20,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}' |
apify call zaiq/whole-foods-scraper --silent --output-dataset

```

## MCP server setup

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