# Lowe's Scraper (`dami_studio/lowes-scraper`) Actor

Lowe's scraper: search lowes.com by keyword, or paste category and product links, and get one row per product: title, brand, item and model number, price with the was/now saving, rating, review count, and shipping, pickup and delivery stock for the store you pick by ZIP. $4.40 per 1,000 products.

- **URL**: https://apify.com/dami_studio/lowes-scraper.md
- **Developed by:** [Dami's Studio](https://apify.com/dami_studio) (community)
- **Categories:** E-commerce, Lead generation
- **Features:** Limited permissions, Multi-tenant, Server interface
- **Stats:** 48 total users, 30 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.25 / 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 for a search, a Lowe's link or a product id, one row per product**: title,
brand, item and model number, price with the was/now saving, rating and review count, and whether it
ships, sits on a shelf for pickup or can be delivered.

Your own store's price and stock need `full` detail. And one search or category stops at 744 products.

| | |
|---|---|
| **Input** | Search terms, Lowe's search, category or product links, or bare product ids |
| **Output** | One row per product: item and model number, price and was/now saving, rating, reviews, shipping, pickup and delivery stock; specs and dimensions in `full` |
| **Ceiling** | 5,000 products per run, and 744 per search term or category |
| **Account needed** | None |
| **Price** | $4.40 per 1,000 products on the Free plan, a little less on paid plans. The free plan's $5 a month covers about 1,100 |

| Using an AI agent? | |
|---|---|
| Actor | `dami_studio/lowes-scraper` |
| Set one field | `searchQueries`, labelled "Search terms" in the Console |
| Cap the bill | `maxTotalChargeUsd` on the run, for example `0.50`: it never charges past that |
| MCP, one link | `https://mcp.apify.com/?tools=fetch-actor-details,dami_studio/lowes-scraper` |
| How often | Daily, to follow prices and stock |

### 🔍 What Lowe's Scraper does

It pages through Lowe's results 24 products at a time until it has `maxItems` or reaches the 744
Lowe's shows for one term or category.

`listing` mode takes what the results page carries: title, brand, price, rating, review count and
stock flags. Some tiles say "See Product Page" instead of a price, and on some categories that is
every tile, so those rows get their product page opened and the price filled in. They are marked
`listing+price`.

`full` mode opens the product page for every row. That brings the store's own price, which is often
the one carrying the was/now saving, stock at your store, the full specifications, the barcode,
weight and dimensions. It adds one lookup per product, so a big run takes longer. The price per product is the same either way.

Prices, promotions and pickup stock differ from store to store. Give a ZIP and it finds the nearest
store: `90210` resolves to store 2714 in Los Angeles, `28202` to store 2348 in Charlotte. Give a store
number if you know it. Give neither and it uses store 0595. `priceScope` on each row says whether the
price is that store's (`STORE`) or Lowe's national one (`NETWORK`).

### 📋 What data you get from each Lowe's product

| What you get | Field |
|---|---|
| Product id, item and model number, brand, title and link | `productId`, `itemNumber`, `modelNumber`, `brand`, `title`, `url` |
| Price, range and whose price it is | `price`, `priceMax`, `priceIsRange`, `priceScope` |
| The was/now saving and when the promotion ends | `listPrice`, `savingsAmount`, `savingsPercent`, `promotionEndsAt`, `isDealOfTheDay`, `promotions` |
| Rating and review count | `rating`, `reviewCount` |
| Every way Lowe's offers it, with quantities | `fulfillment` |
| The same stock picture as columns | `availableForShipping`, `availableForPickup`, `availableForDelivery`, `pickupStoreNumber`, `pickupQuantity`, `deliveryLeadTimeDays`, `inStock`, `freeShipping`, `availabilityMessage` |
| The store the row was asked for | `storeNumber` |
| Images and bullets | `imageUrl`, `images`, `bulletPoints` |
| Lowe's classification | `department`, `category`, `subCategory` |
| In `full`: specs, barcode, weight, dimensions and flags | `specifications`, `barcode`, `weight`, `dimensions`, `lowesExclusive`, `energyStar`, `maxPurchaseQuantity` |
| How the row was read, and what found it | `detailLevel`, `searchQuery`, `sourceUrl`, `position` |

### ▶️ How to scrape Lowe's products

1. Open [Lowe's Scraper](https://apify.com/dami_studio/lowes-scraper) and click **Try for
   free**.
2. Type what you want into **Search terms**, or paste pages into **Lowe's URLs or product IDs**.
3. Set **Maximum products**.
4. For your store's price and stock, set **How much detail per product** to Full and fill in **ZIP
   code** or **Store number**.
5. Click **Start**, then download the dataset as JSON, CSV or Excel.

The sample row appears only when a run has no search terms and no links. It is not charged.

### 💰 How much does it cost to scrape Lowe's products?

**$4.40 per 1,000 products** on the Free plan. Paid plans pay a little less: $4.35 on Starter, $4.30 on Scale and $4.25 on Business. `full` costs the same per
product as `listing`. On the free plan, the $5 Apify gives you each month covers about 1,100
products.

Not charged: the sample row, every note listed further down, and a product that turns up under two
of your search terms, which is delivered once. A run that finds nothing charges for no products.
When a product's price could not be read, that product
is left out with a free `REFUSED` note instead of arriving with a blank price. If you set a maximum
cost for the run, it reads no more products than that covers and stops there.

### 📥 What you give it

```json
{
  "searchQueries": ["smoke detector", "door knob"],
  "maxItems": 45,
  "detailLevel": "full",
  "storeNumber": "1636"
}
```

| Field | Default | What it is |
|---|---|---|
| `searchQueries` | none | Search terms, one per line, as you would type them into Lowe's search box. A model number works too. |
| `startUrls` | none | Lowe's search pages, category (`/pl/...`) pages or product (`/pd/...`) pages. A bare product id like `5013207487` works too. Mixes freely with the terms above. |
| `maxItems` | `50` | 1 to 5,000 products, across everything you asked for. You pay per product, so this is also your budget. |
| `detailLevel` | `listing` | `listing` for speed, `full` for store price, store stock, specs and dimensions. |
| `zipCode` | none | A US ZIP. The nearest Lowe's store is looked up and used. |
| `storeNumber` | store `0595`, unless a ZIP is given | A four-digit store number, such as `0595` or `1636`. Wins over the ZIP. It is used exactly as typed, so check it. |
| `proxyConfiguration` | none | Only for your own servers, which are used as given. Leave it alone otherwise. |

### 📤 What you get back

![Real run output](https://api.apify.com/v2/key-value-stores/BTBHU3DdiuoF4JOsK/records/lowes-scraper-output.png)

A real row from a `full` run for store 1636 on 7 November 2026, with the photo list, bullets,
specifications (83 on this fan) and dimensions cut short:

```json
{
  "productId": "5016379485",
  "itemNumber": "6034476",
  "modelNumber": "37000153",
  "brand": "Harbor Breeze",
  "title": "Northbrook 52-in Matte Black LED Covered Outdoor Ceiling Fan With Light and Remote Control Included",
  "url": "https://www.lowes.com/pd/Harbor-Breeze-Northbrook-52-in-Matte-Black-with-Matte-Blades-Color-Changing-Integrated-LED-Covered-Outdoor-Ceiling-Fan-With-Light-5-Blade/5016379485",
  "price": 129.98,
  "priceMax": null,
  "priceIsRange": false,
  "listPrice": null,
  "savingsAmount": null,
  "savingsPercent": null,
  "promotionEndsAt": null,
  "priceScope": "STORE",
  "isDealOfTheDay": false,
  "promotions": ["Free Delivery", "Delivery"],
  "freeShipping": null,
  "availabilityMessage": null,
  "rating": 4.7,
  "reviewCount": 588,
  "fulfillment": [
    { "method": "Parcel", "available": true, "quantity": 4, "onHandQuantity": 15, "leadTimeDays": 0, "storeNumber": "2879" },
    { "method": "Pickup", "available": true, "quantity": 17, "onHandQuantity": 17, "leadTimeDays": 0, "storeNumber": "1636" },
    { "method": "Delivery", "available": true, "quantity": 40, "onHandQuantity": 0, "leadTimeDays": 1, "storeNumber": "1636" },
    { "method": "ExpeditedDelivery", "available": true, "quantity": 40, "onHandQuantity": 17, "leadTimeDays": null, "storeNumber": "1636" }
  ],
  "availableForShipping": true,
  "availableForPickup": true,
  "availableForDelivery": true,
  "pickupStoreNumber": "1636",
  "pickupQuantity": 17,
  "deliveryLeadTimeDays": 1,
  "storeNumber": "1636",
  "inStock": true,
  "imageUrl": "https://mobileimages.lowes.com/productimages/a7f88911-096b-4d89-9ecc-42e5cd99b6b9/77323837.jpeg",
  "images": ["https://mobileimages.lowes.com/productimages/a7f88911-096b-4d89-9ecc-42e5cd99b6b9/77323837.jpeg"],
  "bulletPoints": ["ENERGY-SAVING DC MOTOR: with a reversible airflow motor reaching a maximum of 4743 cubic feet per minute, at 6 different speed levels"],
  "specifications": [
    { "key": "Housing Color/Finish", "value": "Matte Black" },
    { "key": "Number of Fan Speeds", "value": "6+" },
    { "key": "Lowe's Exclusive", "value": "Yes" },
    { "key": "ENERGY STAR Certified", "value": "Yes" }
  ],
  "department": "ELECTRICAL",
  "category": "CEILING FANS",
  "subCategory": "INDOOR OUTDOOR CEILING FANS",
  "barcode": "058219761212",
  "detailLevel": "full",
  "searchQuery": "harbor breeze ceiling fan",
  "sourceUrl": null,
  "position": 3,
  "scrapedAt": "2026-11-07T15:31:15.397Z",
  "weight": 11.49,
  "dimensions": {
    "depth": { "uom": "Inches", "value": "23.5", "conversions": [{ "uom": "Centimeters", "value": "59.69", "system": "Metric" }] }
  },
  "lowesExclusive": true,
  "energyStar": true,
  "maxPurchaseQuantity": null
}
```

| Field | How to read it |
|---|---|
| `price`, `priceScope` | The selling price, and whether it is the store's (`STORE`) or Lowe's national figure (`NETWORK`). |
| `listPrice`, `savingsAmount`, `savingsPercent`, `promotionEndsAt` | The "was" price and the saving. `listPrice` is null when the item is not on promotion, not when a price is missing. |
| `priceIsRange`, `priceMax` | Items with variants at different prices. `price` is then the "from" figure and `priceMax` the top. |
| `fulfillment` | One entry per way Lowe's offers the item. `Parcel` is shipped, `Pickup` is in store, `Delivery` comes by truck. The `storeNumber` inside each is where Lowe's would source it; for parcel that is usually a warehouse, not your store. |
| `onHandQuantity`, `quantity` | What is physically at that location, and what Lowe's can pull in from elsewhere. |
| `storeNumber`, `pickupStoreNumber` | The store the row was asked for, and the store the pickup figures belong to. In `listing` mode these can differ. |
| `detailLevel` | `listing`, `listing+price` (price fetched from the product page) or `full`. |
| `barcode`, `weight`, `dimensions` | `barcode` is often null on `listing` rows, and `weight` and `dimensions` only appear when the product page was read (`full` or `listing+price`). |
| `specifications` | Rows read from the product page carry its whole spec table, 40 to 100 entries on most products. `listing` rows carry the few specs on the results tile, if any. |
| `lowesExclusive`, `energyStar` | `true` or `false` as Lowe's lists the product, null when Lowe's says neither. Product page rows only. |

### 🧾 Reading the output

Real product rows have no `charged` field. The free rows carry `charged: false`, so dropping those
leaves only products.

| Row | How to spot it | Billed |
|---|---|---|
| A product | A `productId` and no `charged` field | yes |
| The sample | `_sample: true` and `charged: false` | no |
| A note | `_diagnostic: true`, `charged: false`, with an `errorCode`, an `error` and a `hint` | no |

The sample looks like a full product row, so filter on `_sample` or `charged` rather than on the
fields.

| `errorCode` | What it means |
|---|---|
| `REFUSED` | Lowe's did not answer that search, category or product this time, or would not give the price of a product it listed without one. Re-running usually gets it. |
| `NO_RESULTS` | Lowe's returned no products for that term or category. Try a broader term or check the spelling. |
| `PRODUCT_NOT_FOUND` | No product behind that id. Ids change when an item is discontinued or merged. |
| `BAD_URL` | Not a Lowe's search, category or product link, and not a product id. |
| `STORE_NOT_FOUND` | No store came back for that ZIP. The run carries on with store 0595. |
| `TIME_LIMIT` | The run got close to its time limit and stopped on its own. The products delivered before that are in the dataset and charged as usual. Give the run more time, or ask for fewer products, to get the rest. |
| `UNEXPECTED_ERROR` | The run stopped early on an unexpected error. The results before this row are kept. |

A run still ends as succeeded, with an amber status message, even when the time limit stopped it before any product came back.

### 💡 What people use it for

- Tracking prices by model number on a schedule. The was/now pair comes through, so a promotion
  shows up as a promotion rather than as a lower number.
- Pulling a category, such as `/pl/Drills/4294857977`.
- Checking pickup stock at one store before a job, with `full` and that store's ZIP.
- Matching Lowe's items to a product list by brand and model number, with the barcode from `full`.

### 🚧 What it does not do

- **US only.** lowes.com. Lowe's Canada is a separate site.
- **744 products per search term or category.** Split a big pull
  into narrower terms, or use category links, which each have their own 744.
- **No review text.** Star rating and review count only.
- **No sorting or filtering.** Rows come back in Lowe's default order. Sort after export.
- **No MyLowe's or Pro pricing.** These are signed-out prices, with no member or cart discounts.
- **Special orders, quote-only items and installed services** come
  back with `price: null` rather than a guess.
- **A store number is not checked.** Lowe's answers for any four-digit number, so a typo gives you a
  store that does not exist. A ZIP is looked up, and a wrong one gets a free note.
- It is not an official Lowe's tool. Dami's Studio is independent and is not affiliated with or endorsed by Lowe's, or by any other company named on this page.

### 🧭 Which home and retail scraper do you need?

| If you want | Use |
|---|---|
| Lowe's products with store price, stock and specs | This one |
| Target products with promotions and pickup stock | [Target.com Product Scraper](https://apify.com/dami_studio/target-scraper) |
| Amazon products by keyword or ASIN, with the on-page reviews | [Amazon Scraper](https://apify.com/dami_studio/amazon-scraper) |
| A Shopify store's whole catalogue | [Shopify Products Scraper](https://apify.com/dami_studio/shopify-products-scraper) |
| Restock and price-drop alerts on stores that publish product data, such as Shopify or WooCommerce shops | [Product Price & Stock Monitor](https://apify.com/dami_studio/structured-product-price-stock-monitor) |

### ❓ Questions people ask

#### Do I need a Lowe's account or an API key?

No. Nothing to sign up for and nothing to paste.

#### How do I get Lowe's prices for my local store?

Put your ZIP in `zipCode` and set `detailLevel` to `full`. Check `priceScope` on each row: `STORE` is
your store's price.

#### How many products can I get for one search term?

744. For more, use several narrower terms or category links.

#### Why is price null on some rows?

Usually the item is a special order, a quote-only product or an
installed service.

#### Can I call it from code or connect it to an AI assistant?

The [API tab](https://apify.com/dami_studio/lowes-scraper/api/python) has code for Python, JavaScript and the CLI. MCP
clients like Claude and ChatGPT use `https://mcp.apify.com/?tools=fetch-actor-details,dami_studio/lowes-scraper`;
coding assistants can load [our skill
file](https://api.apify.com/v2/key-value-stores/BTBHU3DdiuoF4JOsK/records/apify-ecommerce-SKILL.md). All three bill
your Apify account at the same price.

```python
## pip install apify-client
from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("dami_studio/lowes-scraper").call(
    run_input={
        "searchQueries": ["cordless drill"],
        "maxItems": 50,
    },
    max_total_charge_usd=0.50,  # the most this run may spend
)
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
    if row.get("ok") is False:  # free note rows, not results
        continue
    print(row.get("price"), (row.get("title") or "")[:80])
```

#### Is this affiliated with Lowe's?

No. It reads what lowes.com shows anyone signed out. "Lowe's" is a trademark of Lowe's Companies,
Inc., used here only to say what the actor reads. On the legal side, Apify's write-up on
[the legality of web scraping](https://blog.apify.com/is-web-scraping-legal/) is a good starting
point, and we are not lawyers.

### 🆘 If something breaks

Open the **Issues** tab on the actor page and send the input you used and the run ID; the note rows
in the dataset usually name the reason already.

# Actor input Schema

## `searchQueries` (type: `array`):

What to look for on lowes.com, one term per line: "cordless drill", "kitchen faucet", "KDD 2024A-03". Up to 744 products per term, so split a big pull across several terms rather than one broad one.

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

Paste Lowe's search pages (https://www.lowes.com/search?searchTerm=drill), category pages (https://www.lowes.com/pl/Drills/4294857977) or product pages (https://www.lowes.com/pd/.../5013207487). A bare product id such as 5013207487 works too. Mixes freely with the search terms above.

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

Hard ceiling on products returned across everything you asked for. You are charged per product returned, so this is also your budget.

## `detailLevel` (type: `string`):

Listing gives you what the results page shows (title, brand, price, rating, stock) and opens the product page for any row listed without a price. Full opens the product page for every row instead, which adds the store-level price, the was/now saving, stock at your store, specs, weight and dimensions. Full is slower; the price per product is the same either way.

## `zipCode` (type: `string`):

A US ZIP, used to pick the nearest Lowe's store. Prices, promotions and pickup stock differ store to store, so this changes what you get back.

## `storeNumber` (type: `string`):

A four-digit Lowe's store number such as 0595 or 1636, if you already know it. Wins over the ZIP code above.

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

Optional. Leave this alone unless you need the run to go out through a particular network. Your own proxy servers are used exactly as given.

## Actor input object example

```json
{
  "searchQueries": [
    "cordless drill"
  ],
  "startUrls": [],
  "maxItems": 50,
  "detailLevel": "listing",
  "zipCode": "",
  "storeNumber": "",
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `results` (type: `string`):

One row per Lowe's product: title, brand, item and model number, price with the was/now saving, promotion labels, rating and review count, shipping, store pickup and delivery availability with quantities and lead times, images, department and category, and the product URL. Full detail adds store-level pricing, stock at your store, specifications and dimensions. Empty input, a URL Lowe's does not recognise, or a request Lowe's turns away writes an uncharged sample or diagnostic row instead.

# 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 = {
    "searchQueries": [
        "cordless drill"
    ],
    "startUrls": [],
    "maxItems": 50,
    "detailLevel": "listing",
    "zipCode": "",
    "storeNumber": "",
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("dami_studio/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 = {
    "searchQueries": ["cordless drill"],
    "startUrls": [],
    "maxItems": 50,
    "detailLevel": "listing",
    "zipCode": "",
    "storeNumber": "",
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("dami_studio/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 '{
  "searchQueries": [
    "cordless drill"
  ],
  "startUrls": [],
  "maxItems": 50,
  "detailLevel": "listing",
  "zipCode": "",
  "storeNumber": "",
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call dami_studio/lowes-scraper --silent --output-dataset

```

## MCP server setup

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