# Lowe's Scraper: Products, Prices, Stock & Store Savings (`vant47/lowes-scraper`) Actor

Get Lowe's store prices and stock for tools, appliances and building supplies by ZIP or store ID. Capture was prices, savings with dates, item and model numbers, pickup quantity and optional aisle, bay and UPC. About $0.0045 per product + $0.20 start. No login.

- **URL**: https://apify.com/vant47/lowes-scraper.md
- **Developed by:** [fortbrand](https://apify.com/vant47) (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.60 / 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

## Lowe's Scraper: Products, Prices, Stock & Store Savings

For retail price analysts, brands, contractors, comparison services and resellers who need the advertised Lowe's price and pickup stock at a chosen US store.

About **$0.0045 per product at one store**. Twenty products cost about **$0.09**, and 1,000 product-store rows cost about **$4.50**, plus **$0.20 browser startup at the default 2 GB**. Paid Apify plans receive tier discounts. You need no Lowe's login, exported cookies or external API key.

The Actor uses the same retail inputs and field names as the Safeway and Albertsons family. Each product-store observation has its own scrape timestamp.

### What you get

Every product row starts with a readable `verdict`, followed by the common retail schema. A product found more than once at the same store is delivered and charged once. The same product at two stores is two observations, because each store may have a different price or pickup quantity.

| Information | Output fields |
| --- | --- |
| Store and location | `storeId`, `storeName`, `location`, `storeAddress`, `storeTimezone` |
| Product matching | `productId`, `sku`, `itemNumber`, `modelNumber`, `gtin`, `upc` |
| Advertised price | `price`, `regularPrice`, `currency`, `priceScope` |
| Sale and savings | `onSale`, `isDeal`, `promoText`, `promoType`, `savingsAmount`, `savingsPercent` |
| Promotion dates | `promoStart`, `promoEnd` when the source supplies them |
| Pickup stock | `inStock`, `stockLevel`, `stockQuantity`, `availableForPickup` |
| Conditional bulk offers | `bulkPrice`, `bulkMinimumQuantity`, `bulkPrices`, `offers` with purchase conditions |
| Other fulfillment | `availableForShipping`, `availableForDelivery`, `fulfillment` |
| Store aisle and bay | `aisle`, `bay`, `aisleLocations` from product detail when supplied |
| Comparable metadata | `title`, `brand`, `size`, `unitPrice`, `unitOfMeasure`, `rating`, `reviewCount`, `category` |
| Evidence | `url`, `imageUrl`, `sourceUrl`, `scrapedAt`, `recordType` |
| Optional detail | `details.specifications`, `details.description`, `details.bullets`, `details.barcodes`, `details.measurements` |

For flooring, `price` is the purchasable carton price, while `unitPrice` can be the square-foot price. A live source example was $46.19 per carton and $1.99 per SQFT. Inventory counts remain quantities of the product SKU.

UI-only per-piece calculations can remain null when the public product data does not expose their value. Bulk prices are conditional SKU prices, with the minimum quantity and original offer text retained.

A missing source field stays `null`. The Actor does not guess a barcode, unit price, promotion start date or stock count. In particular, a product's catalog publication date is never used as a promotion start date.

### Store selection is part of correctness

Lowe's may return a product page with your requested store's address while its price and inventory still belong to the visitor's default store. This Actor checks the store that supplied the product data. A store address alone is not accepted as proof of price scope.

When you give `locations`, the public store locator chooses the nearest Lowe's store and that store is selected before reading products. The row retains your requested ZIP in `location`, and names the actual store in `storeId`. When you give `storeIds`, those exact verified stores take precedence over ZIPs. An invalid store is reported with a free note; a default-store price is not substituted.

`stockLevel` and `stockQuantity` mean the on-hand pickup quantity that the public site reports for the selected store. They are not network-wide totals. A parcel warehouse's quantity does not become local shelf stock. `fulfillment` retains the source location for each shipping or delivery method so you can interpret those separately. The public quantity is an online inventory signal and can differ from a physical count.

### Start a run

1. Enter one or more search terms, category or product links, or product IDs.
2. Choose US ZIP codes or exact store IDs and set the result limits.
3. Run the Actor and export the dataset as JSON, CSV or Excel.

The prefill requests 20 hammers near ZIP 28117. For monitoring, use the product IDs at the end of Lowe's URLs. Item numbers, models and UPCs are separate identifiers.

```json
{
  "searchTerms": ["hammer"],
  "locations": ["28117"],
  "maxItemsPerSearch": 20,
  "maxItems": 20,
  "dealsOnly": false,
  "includeDetails": false
}
```

### Monitor a known list

Two products at two stores are four observations: about $0.018 in Product charges plus the $0.20 default start charge.

```json
{
  "productIds": ["1002967576", "5014148635"],
  "storeIds": ["1674", "0595"],
  "maxItems": 4,
  "includeDetails": true
}
```

Product detail is always used for an explicit product ID or product URL. `includeDetails` controls the additional descriptive output and requests product detail for products discovered through search or categories. It has no separate product surcharge. If optional detail is refused, the verified listing observation can still be delivered, accompanied by a free note.

### Search, categories and deals

Search terms can be ordinary words, a brand and product type, or a model number. A search that redirects to one product is handled as a product lookup. Category URLs must be public `/pl/` links ending in their numeric category ID. The input's store selection controls the resulting observations.

```json
{
  "startUrls": [
    "https://www.lowes.com/pl/hand-tools/hammers/4294857564"
  ],
  "storeIds": ["1674"],
  "maxItemsPerSearch": 100,
  "maxItems": 100
}
```

For sales, use `dealsOnly: true` with search terms or category URLs, or supply the public Lowe's deals category. The output includes actual sale prices and Deal of the Day flags. Conditional product and Bulk Savings offers also qualify as deals and retain their purchase conditions. `price` remains the single-SKU advertised price. Free shipping and financing offers alone do not make a product a price deal.

```json
{
  "startUrls": ["https://www.lowes.com/pl/Deals/1611079983848"],
  "locations": ["28117"],
  "dealsOnly": true,
  "maxItemsPerSearch": 50,
  "maxItems": 50
}
```

A single listing is limited to at most 744 result positions, and sponsored products and rolled-up variants can reduce the number of distinct rows. Lowe's limits the depth of an individual listing. One search or category is not a whole-catalog export. Split broader coverage into narrower categories or several relevant searches, or use a maintained list of product IDs. Sponsored products can overlap pages; those duplicates do not incur another product charge.

### Input reference

| Input | Default | Meaning |
| --- | --- | --- |
| `searchTerms` | Hammer when no source is supplied | Array of searches, each run at every selected store |
| `startUrls` | Empty | Optional product, search, category or deals links |
| `productIds` | Empty | Optional numeric Lowe's product IDs from product URLs |
| `locations` | Website default store if empty | Array of five-digit US ZIP codes; nearest store selected |
| `storeIds` | Empty | Exact verified store numbers; takes precedence over ZIPs |
| `maxItemsPerSearch` | 20 | Maximum new rows from each search or category at one store |
| `maxItems` | 20 | Global maximum product-store rows; up to 10,000 |
| `dealsOnly` | False | Keep sale-priced or Deal of the Day products |
| `includeDetails` | False | Add product detail for discovered products |
| `proxyConfiguration` | US residential | Optional explicit proxy configuration |

Invalid input produces diagnostic notes and a summary with no Product charges. The run start charge still applies. Check the summary's `status`, `freeFailures`, `outcomes` and `notes` when incorporating results into an automated workflow. An empty result does not prove that a product is out of stock. A refusal is reported as a refusal, rather than converted into a negative inventory observation.

### Reading the results

The dataset contains `PRODUCT` and free `NOTE` rows. Filter `recordType === "PRODUCT"` for a table containing only paid product observations. A note has a readable explanation in `verdict`, a `code` and contextual identifiers. Note rows never create a Product charge.

The overview shows prices, promotions, unit prices, stock and stores. The deals view adds savings, dates and bulk quantities. `DEALS` contains only deal observations; `SUMMARY` records coverage, free failures, outcomes and spending-limit state.

A live signed-out source check on 3 November 2026 returned Kobalt product 1002967576 at Brooklyn store 1674 for $21.98, with 11 on hand. A DEWALT product at that store was $99, was $179, with an $80 saving and a source promotion end timestamp of 2027-01-20T00:00:00.000Z. These are examples from those observations, not promises about the next run's price or inventory.

`price` is the advertised signed-out selling price. Credit-card savings, financing, installation, taxes, cart discounts and personalized rewards are not silently deducted. `memberPrice` and `memberProgram` remain null when the source does not give a separate supported member product price. `priceScope` retains whether Lowe's labeled a price as store, network or special-order pricing. `inStock` uses positive on-hand quantity when available, otherwise the site's pickup availability signal.

### Monitor prices every day

Save the input as an Apify task and attach an Apify schedule. Each execution creates a fresh dataset. Keep the product and store IDs stable, join observations by `storeId` and `productId`, and retain `scrapedAt` for comparisons.

Interrupted runs recover from saved state and dataset rows. Completed rows are skipped and acknowledged charges are reconciled on reboot. A new scheduled execution is a new observation and charges for its delivered rows.

### Pricing and spending limits

| Delivered observations | List-price product charge |
| --- | --- |
| 20 rows | $0.09 |
| 100 rows | $0.45 |
| 1,000 rows | $4.50 |

The browser start charge is $0.20 at the default 2 GB ($0.10 per GB, minimum one GB). This covers the anonymous visitor session needed to read the retailer. It is separate from the Product charge. Bronze receives 5% off the product price, Silver 10%, and Gold and higher 20%. One product at one store is one Product charge. Duplicate observations within a run, refused requests, retries, empty results and note rows have no Product charge.

Set `maxItems` to control how many observations you request. You can also set the Apify run option `maxTotalChargeUsd` to cap the run's charges. The Actor checks that limit before delivering the next paid observation and stops cleanly when the remaining allowance cannot cover another Product.

### Use through the API

Pass your Apify token securely in the authorization header. The same family inputs work through the Console and API.

```python
import os
import requests

response = requests.post(
    "https://api.apify.com/v2/acts/vant47~lowes-scraper/runs",
    headers={"Authorization": "Bearer " + os.environ["APIFY_TOKEN"]},
    json={
        "productIds": ["1002967576"],
        "storeIds": ["1674"],
        "maxItems": 1
    },
    timeout=60,
)
response.raise_for_status()
run = response.json()["data"]
print(run["id"], run["defaultDatasetId"])
```

Poll the asynchronous run, then read its dataset and `SUMMARY`.

### Use with AI agents (MCP)

Connect an MCP client to `https://mcp.apify.com`, read the Actor input schema, and request product IDs at exact store numbers with a result cap. Retrieve the dataset and summary, distinguish free notes from products, and preserve missing values as null.

### Limits and frequently asked questions

**Do I need a Lowe's account?** No. Only public signed-out pages and anonymous visitor state are used. Account cookies are not accepted and CAPTCHAs are not solved. Refused sessions are retried; persistent failures produce free Product notes.

**Can I choose a ZIP?** Yes. `locations` resolves the nearest store and retains the requested ZIP. Use `storeIds` to keep the same stores between scheduled runs.

**Is an item number a product ID?** No. `productId` is the number ending a product URL; `itemNumber` and `sku` hold Lowe's item number, and `modelNumber` holds the manufacturer's model.

**Why are some fields blank?** Availability differs between source surfaces. Details add what is supplied. UI-only unit calculations, member or cart prices, and unexposed promotion starts remain null. A nearby store's stock or aisle is never substituted.

**Does pickup availability mean stock on hand?** A future pickup quantity can include stock brought from elsewhere. `stockLevel` is the reported on-hand count; `fulfillment` retains the separate availability and date information.

**Can one search export the catalog?** No. The retailer caps listing depth. Use narrower categories, several searches or a known product list. The result limit is an upper bound; duplicates and exhausted source pages can reduce the count.

**What happens on a source refusal?** Already delivered observations remain available. The summary identifies free failures. A refusal is not treated as evidence that a product is out of stock.

**Is this an official integration?** No. This independent tool is unaffiliated with Lowe's. Prices, assortment, promotions and inventory come from the retailer.

### Cloud validation

The production checks completed 30 varied inputs across five ZIP codes. Two volume checks each delivered 1,100 unique product-store rows with exactly 1,100 Product charges and no source failures. Ten distinct products were checked against live product pages. Reboot and one-product spending-limit checks preserved the delivery and charge counts. Anonymous-session retries can extend cold startup.

# Actor input Schema

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

Search Lowe's for tools, appliances, model numbers or building supplies. Each term is searched at every selected store.

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

Optional lowes.com product URLs, /pl/ category URLs ending in a category ID, search URLs or deals links. Store selection from the input controls all prices.

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

Optional Lowe's product IDs, the number at the end of a /pd/ URL, such as 1002967576. These are not item numbers or UPCs.

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

Five-digit US ZIP codes. The nearest Lowe's store is verified and selected before reading its price and pickup stock. Empty uses the website default store.

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

Optional verified store numbers, such as 1674 (Brooklyn) or 0595 (Mooresville). 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 product-store row limit. About $0.0045 per product plus $0.20 browser start at the default 2 GB: 20 products cost $0.29.

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

Keep sale-priced products, Deal of the Day products and conditional product or Bulk Savings offers. Purchase conditions remain separate from the advertised single-SKU price. Free shipping and credit-card discounts alone are excluded.

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

Add public product specifications, descriptions, UPCs and store aisle/bay by opening product detail. No extra product fee. A free note identifies unavailable optional details.

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

US residential is required by the verified browser route. Leave the default unchanged. Your own proxy configuration is used as provided.

## Actor input object example

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

# Actor output Schema

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

No description

## `dealsView` (type: `string`):

No description

## `dealsOnly` (type: `string`):

No description

## `summary` (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": [
        "hammer"
    ],
    "locations": [
        "28117"
    ],
    "maxItemsPerSearch": 20,
    "maxItems": 20
};

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

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

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,vant47/lowes-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/r9ccSKuo7K5BhaFc0/builds/igI715uqC2R29uImH/openapi.json
