# BJ's Wholesale Scraper: Club Prices, Coupons & Stock (`moggedhq/bjs-wholesale-scraper`) Actor

Get BJ's Wholesale Club products with live member prices by club, Instant Savings dates, coupon conditions, item numbers, sizes and stock. Search, categories or product links by ZIP or club ID. About $0.004 per product: 1,000 products cost $4. No login.

- **URL**: https://apify.com/moggedhq/bjs-wholesale-scraper.md
- **Developed by:** [mogged](https://apify.com/moggedhq) (community)
- **Categories:** E-commerce, Automation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.20 / 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.
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

## BJ's Wholesale Scraper: Club Prices, Coupons & Stock

For retail price analysts, brands and comparison services monitoring BJ's products at a selected club. Get club-specific member prices, Instant Savings with their published dates, public coupon conditions, item numbers, package labels and availability. About **$0.004 per delivered product**, or **$0.08 for 20 products**, plus the minimum **$0.00005 Actor start charge**. No retailer login or retailer API key is required.

### Understand the prices

`price` is the current advertised catalog price for the exact club in `storeId`. An active Instant Savings promotion replaces the base club price, while `regularPrice` retains the published base or original price. `baseClubPrice` preserves the club's source base value. BJ's membership pricing is repeated in `memberPrice`, with the program named in `memberProgram`.

Coupons remain separate in `offers`. Each matched offer keeps its discount, dates, minimum quantity, clipping requirement, conditions and disclaimer. A coupon is not automatically subtracted from `price`: the shopper may need to clip it or buy qualifying quantities. Buy More Save More conditions are retained when they appear in the supplied offer text. `isDeal` includes an active Instant Savings reduction or a matched public coupon.

### What you get

The common US retail fields come first. Shipping prices stay in `onlinePrice`; catalog-wide ranges never replace missing club prices.

| Field | Meaning |
| --- | --- |
| `verdict` | Readable product, price, promotion, stock and club summary. |
| `retailer`, `country`, `currency` | BJ's Wholesale Club, US and USD. |
| `storeId`, `storeName`, `location` | Four-digit club number, verified club name and requested ZIP. Exact/default club selections have a null location. |
| `productId`, `sku` | Retailer item identifier and supplied part number, preserved as strings. |
| `gtin`, `title`, `brand`, `size` | Supplied barcode, product title, explicit brand and package-size label. |
| `price`, `regularPrice`, `onSale` | Advertised club price, base/original price and sale comparison. |
| `promoText`, `promoType` | Instant Savings text or matched coupon text and its type. |
| `promoStart`, `promoEnd` | Published promotion dates. Timestamps without offsets use `promoTimezone`. |
| `memberPrice`, `memberProgram` | Advertised BJ's member price and membership label. |
| `unitPrice`, `unitOfMeasure` | Supplied price per unit and unit label. |
| `inStock`, `stockLevel` | Explicit selected-club availability and a quantity when supplied. |
| `rating`, `reviewCount` | Exact aggregate rating when supplied and the published review count. |
| `category`, `url`, `imageUrl` | Catalog category path, canonical product URL and image. |
| `scrapedAt` | UTC timestamp for the observation. |

BJ's fields add more context:

| Field | Meaning |
| --- | --- |
| `clubId`, `itemNumber` | BJ's club number and item number. |
| `parentProductId`, `variantId` | Long parent and variant catalog identifiers. |
| `inClubOnly`, `onlineOnly` | Supplied catalog channel flags, separate from stock at the chosen club. |
| `couponText`, `offers` | Matched coupon text plus offer IDs, validity, purchase conditions and clipping requirements. |
| `digitalCouponAvailable`, `digitalCouponText` | Common retail aliases for coupon availability and text. |
| `sameDayDelivery`, `pickupEligible` | Selected-club eligibility flags supplied for the product. |
| `onlinePrice`, `onlineInStock` | Separate shipping-channel price and availability. |
| `instantSavings`, `baseClubPrice` | Active source sale record and source base club price. |
| `priceRange`, `ratingBucket` | Catalog-wide price bounds and the source rating filter bucket. |
| `categories`, `sellByWeight`, `ebtEligible` | All category paths, source weight-selling flag and club-specific EBT eligibility. |
| `storeAddress`, `storePostalCode`, `storeState` | Public address metadata for the selected club. |
| `storeDistance`, `selectionMethod` | ZIP-centroid distance in miles and how the club was selected. |
| `storeMetadataObservedAt`, `storeMetadataFromCache` | When address metadata was observed and whether it was reused. |
| `details`, `sourceUrl` | Optional catalog description/attributes and the public catalog request supporting the row. |

The overview compares prices, promotions, stock and clubs. The deals view groups promotion columns; `DEALS` JSON contains only `isDeal: true` rows. Large exports use linked parts. Dataset records are `PRODUCT`, free `NOTE` or the final free `SUMMARY`.

`SUMMARY` reports selected clubs, products, deals, duplicates, source outcomes, free failures, charges and spending-limit status. Inspect it before accepting a snapshot.

### Start in three steps

1. Enter terms such as milk, coffee or dog food, or paste product/category links.
2. Choose ZIP codes or stable club IDs and set the product limits.
3. Run, inspect SUMMARY and export JSON, CSV or Excel.

The form prefill searches milk near ZIP 44094 and caps delivery at 20 products. Its 4 November 2026 cloud test returned **20 unique product observations**, with **20 product charges and no source failures**, in **25.8 seconds**. That test resolved the ZIP to Willoughby club `0096`. Two volume checks each returned 1,200 unique observations with complete summaries and price/stock fields on every row, covering multiple search terms, categories and clubs.

### Inputs and examples

Empty locations and club IDs use the website default club. Duplicate ZIP selections of one club are combined. Pagination has a 50-page limit per source.

| Input | Example and behavior |
| --- | --- |
| `searchTerms` | `["milk", "coffee"]`; each term is searched at every selected club. |
| `startUrls` | BJ's category, product, search, deals or coupon URLs. Current category paths and legacy numeric category IDs are supported. |
| `productIds` | `["906", "297623"]`, retailer item numbers, or long catalog IDs such as `3000000000000221387`. |
| `locations` | `["44094", "01532"]`; nearest active retail club within 150 miles of the ZIP centroid. ZIP+4 is accepted. |
| `storeIds` | `["0096", "0211"]`; exact club selection, taking precedence over ZIP codes. Leading zeros are accepted and preserved. |
| `maxItemsPerSearch` | New product rows per term/category source and club; default 20, maximum 1,000. |
| `maxItems` | Global product/club row limit; default 20, maximum 10,000. |
| `dealsOnly` | Keep active Instant Savings and matched public coupon offers. |
| `includeDetails` | Include additional catalog description, attributes, badges and availability date. No extra product charge. |
| `proxyConfiguration` | Default US datacenter, with fresh US residential retries after datacenter failures. Custom routing is supported. |

Compare 20 coffee products at two clubs, up to 40 observations costing about $0.16 at base pricing:

```json
{
  "searchTerms": ["coffee"],
  "storeIds": ["0096", "0211"],
  "maxItemsPerSearch": 20,
  "maxItems": 40
}
```

For a known basket, use `productIds: ["906", "297623"]`, `storeIds: ["0096", "0211"]` and `maxItems: 4`. A product URL can be supplied instead of its ID. For dog food promotions, set `searchTerms: ["dog food"]` and `dealsOnly: true`. A BJ's coupons/deals URL opens the catalog's promotion selection.

### Real output example

Selected fields from the 4 November 2026 ZIP 44094 prefill:

```json
{
  "recordType": "PRODUCT",
  "retailer": "BJ's Wholesale Club",
  "country": "US",
  "currency": "USD",
  "storeId": "0096",
  "storeName": "Willoughby",
  "location": "44094",
  "productId": "906",
  "sku": "906",
  "title": "Wellsley Farms Whole Milk, 1 gal.",
  "price": 3.27,
  "regularPrice": 3.27,
  "memberPrice": 3.27,
  "onSale": false,
  "inStock": true,
  "unitPrice": null,
  "gtin": null
}
```

### Pricing and spending limits

One product means one distinct item at one club. The same item at two clubs is two observations. Duplicates, failed requests, empty source responses, notes, summaries and exports have no product charge. There is no additional default dataset-row fee. Optional catalog details have no extra fee.

| Products | Base | Bronze | Silver | Gold |
| --- | --- | --- | --- | --- |
| 20 | $0.08 | $0.076 | $0.072 | $0.064 |
| 100 | $0.40 | $0.38 | $0.36 | $0.32 |
| 1,000 | $4.00 | $3.80 | $3.60 | $3.20 |

Add the minimum $0.00005 start charge. Base pricing is $0.004 per product, Bronze $0.0038, Silver $0.0036 and Gold $0.0032. Apify's maximum total charge controls larger jobs. The Actor checks remaining capacity before saving another product, stops cleanly at the cap and records that decision in SUMMARY. A delivered out-of-stock observation is still a product observation, with its stock state and available price fields preserved.

### Monitor prices every day

Save a task containing stable club IDs and the products, terms or categories you monitor. In Apify Console, create a schedule, select that task, choose a daily time and timezone, then send completed-run datasets to your database or workflow.

Join observations using `storeId + productId`, with `scrapedAt` as the observation timestamp. Keep membership prices, base prices, Instant Savings and coupons in separate columns. Preserve identifiers as text in spreadsheets. A new scheduled run collects a fresh catalog observation and charges for its delivered products again.

### API and automation

Make, Zapier, n8n, webhooks and Sheets workflows can consume the dataset and SUMMARY.

Use your own Apify token:

```bash
curl -X POST \
  'https://api.apify.com/v2/acts/moggedhq~bjs-wholesale-scraper/runs' \
  -H 'Authorization: Bearer YOUR_APIFY_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"searchTerms":["coffee"],"storeIds":["0096"],"maxItems":20}'
```

```python
from apify_client import ApifyClient
client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("moggedhq/bjs-wholesale-scraper").call(
    run_input={"searchTerms": ["coffee"], "storeIds": ["0096"], "maxItems": 20}
)
rows = list(client.dataset(run["defaultDatasetId"]).iterate_items())
products = [row for row in rows if row.get("recordType") == "PRODUCT"]
```

### Use with AI agents (MCP)

Connect to `https://mcp.apify.com`. Ask for BJ's Wholesale Club member prices, Instant Savings dates, coupon conditions and stock at a ZIP code or club ID. Supply a product cap and ask the agent to inspect SUMMARY, preserve null values and keep coupon requirements separate from item prices. Exact item numbers are useful for repeatable basket monitoring.

### Limits

The signed-out catalog does not supply UPCs, unit prices, product brands, model numbers, exact average ratings, detailed ingredients or physical stock quantities for most products. Those fields remain null. `ratingBucket` is the rounded search-filter bucket, not an exact average rating. Additional description and attributes are included only when provided. Coupon item identifiers are not relabeled as UPC barcodes.

Prices and availability are the retailer's published catalog observations and can change after collection or lag other shopping surfaces. Stock is a catalog availability flag, not an audited shelf count. Weighted products retain `sellByWeight`; actual checkout totals can depend on final weight, tax and fulfillment charges. Catalog-wide minimum and maximum prices are informational and never substitute for a club price.

The public guest coupon feed covers non-personalized offers matched to item numbers. Geographic offers without verified club eligibility, account-only offers, clipped-account state and personalized savings are excluded. Coupons may require membership, clipping and qualifying purchases. Offers shown as Instant Savings or Buy More Save More retain their supplied text and conditions; the Actor does not calculate a personalized checkout basket.

ZIP selection uses 2025 Census ZIP-area centroids and straight-line distance to a listed active retail club, with a 150-mile limit. A full street address or the website's travel-distance selection can choose a different club. Use exact club IDs for a fixed comparison panel. Unknown and out-of-coverage ZIPs produce free explanatory output.

Public club address metadata may be reused for seven days. Prices, stock and coupon observations are requested for each run. Historical backfill is unavailable. Site refusals and outages can prevent complete output; affected requests are recorded without a product charge. Parseable invalid inputs produce free notes and a summary. Apify may reject schema-invalid input before starting a run.

Delivery and product charges are serial and recover from the saved dataset ledger. Resume reconciles acknowledged charges and platform counts before further delivery. Ambiguous billing evidence stops further charging. The Actor does not log in, use account cookies, solve CAPTCHAs or use a paid unblocking service.

### FAQ

**Do coupons reduce price?** No. Coupon text, discounts, dates and purchase requirements remain in `offers`. Active Instant Savings are reflected in `price`, with the source base price retained separately.

**Why is a product priced but out of stock?** The retailer can publish a club price while its availability flag is false. Preserve both fields rather than treating the price as proof of stock.

**Can the same item appear more than once?** Once per selected club in each run. An item at two clubs is two observations. Overlapping terms, URLs and IDs at one club do not add duplicate product charges.

# Actor input Schema

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

Find BJ's products such as milk, coffee or dog food at each club. Leave empty when using product IDs or URLs.

## `startUrls` (type: `array`):

Optional HTTPS BJ's category, product, coupon, deals or search URLs. Run club selections control the price.

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

BJ's numeric item numbers such as 906 or 297623, or long catalog product IDs such as 3000000000000221387.

## `locations` (type: `array`):

US ZIP codes. Chooses the nearest active retail club within 150 miles of the 2025 Census ZIP centroid by straight-line distance. Empty uses the website default club.

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

Exact BJ's club numbers, such as 0096 (Willoughby) or 0211 (Northborough). These take precedence over ZIP codes.

## `maxItemsPerSearch` (type: `integer`):

Maximum new product rows from each term, category or deals source at one store. Duplicates are free.

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

Global limit across all products and stores. About $0.004 per product: 20 products cost about $0.08 plus the small start charge.

## `dealsOnly` (type: `boolean`):

Keep active Instant Savings and non-geographic guest coupons matched to item numbers. Coupons are not subtracted from the item price.

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

Include the additional description, attributes, badges and catalog availability date supplied in the same public feed. Missing ingredients, barcodes and specifications stay null. No extra charge.

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

Default: US datacenter, with fresh US residential retries only after datacenter fails. Uses a browser-compatible HTTP transport. Custom proxy settings are supported.

## Actor input object example

```json
{
  "searchTerms": [
    "milk"
  ],
  "locations": [
    "44094"
  ],
  "maxItemsPerSearch": 20,
  "maxItems": 20,
  "dealsOnly": false,
  "includeDetails": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `dataset` (type: `string`):

No description

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

No description

## `deals` (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 = {
    "searchTerms": [
        "milk"
    ],
    "locations": [
        "44094"
    ],
    "maxItemsPerSearch": 20,
    "maxItems": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("moggedhq/bjs-wholesale-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"],
    "locations": ["44094"],
    "maxItemsPerSearch": 20,
    "maxItems": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("moggedhq/bjs-wholesale-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"
  ],
  "locations": [
    "44094"
  ],
  "maxItemsPerSearch": 20,
  "maxItems": 20
}' |
apify call moggedhq/bjs-wholesale-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,moggedhq/bjs-wholesale-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/DCmy9xfhjowaltYXv/builds/epuNM14WEGmnL1dmr/openapi.json
