# Lowe's Scraper — Products, Prices & Reviews (`tortuga/lowes-scraper`) Actor

Scrape Lowes.com search results, product pages and reviews: price, was-price, savings, rating, brand, model, availability, images and specifications. Any keyword or URL.

- **URL**: https://apify.com/tortuga/lowes-scraper.md
- **Developed by:** [Trevor Ortega](https://apify.com/tortuga) (community)
- **Categories:** E-commerce
- **Features:** Limited permissions
- **Stats:** 14 total users, 7 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 products

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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

> **Status, November 2026:** Lowe's protects its search pages with Akamai, and Apify's Unblocker proxy gets through on only about 1 attempt in 4 (fewer in a bad hour). Since 11 November the Actor keeps trying, up to 24 attempts per search page and several at once after a failure, instead of giving up after 3, so search runs return their products again. Most search pages load within 1-3 minutes. The proxy bills failed attempts too, see the Proxy note. We check it every hour.

Scrape Lowes.com search results, product pages and customer reviews into a clean dataset: price, was-price, savings, sale end date, rating, review count, brand, model and item numbers, availability, pickup / delivery / shipping, badges (Top Gift, Value Pick, Featured Deal, Trending Now, Tool Only), rank on the page and sponsored flag. Turn on details for the description, the full specification table, every image, videos, manuals, variants (sizes and colors with their own product ids), UPC, dimensions, warranty and the collection a product belongs to, and turn on reviews for the review texts.

No headless browser and no login on your side: the Actor reads the JSON state Lowe's embeds in its own pages, so one request returns 24 products. Download the results as JSON, CSV or Excel, or pull them through the API into your own app, Google Sheets or an LLM pipeline. You pay per item you get.

See also: the companion [Walmart Scraper](https://apify.com/tortuga/walmart-scraper), which has the same input and output shape.

### What data does Lowe's Scraper extract?

Every product from a search page (the `result` event):

| Field | Description |
|---|---|
| `type` | `"product"` |
| `omniItemId`, `itemNumber`, `modelId` | Lowe's product id (the number at the end of `/pd/.../5013207487`), the item number ("Item #4913878" on the site) and the manufacturer model |
| `name`, `url`, `imageUrl`, `images` | Product title (brand + description), canonical product URL, full-size main image and all images Lowe's lists for the tile |
| `brand` | Brand (`null` for a few marketplace listings without one) |
| `price`, `wasPrice`, `savingsAmount`, `savingsPercent`, `currency` | Current price, strike-through price, the difference and the percentage, `USD` |
| `priceText`, `saleEndDate` | Price as displayed (`$99.00`, or `$19.98 - $209.98` for products with variants); when the sale ends (`2026-10-31`) |
| `priceType` | `NETWORK` (Lowe's price for visitors without a store), `STORE` (price of the store Lowe's assigned to the request) or `SOS` (special order / marketplace, shipped by the vendor). See "How to choose a Lowe's store or ZIP code" |
| `variantPriceMin`, `variantPriceMax` | Price range across sizes / colors when the tile shows one instead of a single price |
| `unitPrice` | Per-unit price when Lowe's shows one (flooring, bulk items) |
| `rating`, `reviewCount` | Average rating (0-5) and number of ratings |
| `availability`, `inStock`, `availabilityText` | `IN_STOCK` / `OUT_OF_STOCK` / `UNKNOWN` and a boolean. `UNKNOWN` = the search page could not price or source the item ("See Product Page for Pickup and Delivery Options"); details fill it in |
| `fulfillment` | `{shipping, pickup, delivery, sameDayDelivery, freeShipping, shippingText, pickupText, deliveryText, arrivesBy, storeQuantity, networkQuantity, lowStock}`: which methods are available, the parcel promise date, units on hand at the store and in the online network |
| `badges` | `Top Gift`, `Value Pick`, `Featured Deal`, `Trending Now`, `Tool Only`, `Last Chance Savings`, `Only at Lowes`, ... |
| `promotions` | Promotion texts: `FREE GIFT WITH PURCHASE`, `Buy 1 and Get 1`, financing offers |
| `isSponsored` | `true` for sponsored tiles (usually 4 per page). A product shown as an ad and again organically is saved once, as organic |
| `isMarketplace`, `sellerName`, `programType` | Third-party marketplace listing or not; seller name (`Lowe's` or the marketplace seller); `Stock`, `Consumer-SOS` (special order) or `Marketplace` |
| `category`, `categoryPath` | Leaf category of the product (`Drills`, `Hammer Drills`); the full path comes with details |
| `collection` | The set / bundle the product belongs to (`{id, type, url}`, "Shop the Set") |
| `variantCount`, `variantAttribute`, `variants` | Number of sizes / colors, the attribute they vary by, and the color siblings the tile lists (`{omniItemId, name, options, url, imageUrl}`) |
| `specifications`, `highlights`, `packageQuantity` | The 2-3 key specs shown on the tile, marketing bullets, package quantity |
| `position`, `page` | Rank among the tiles of the page (ads included, in display order) and page number |
| `location` | Store context Lowe's priced the page for: `{storeNumber, zipCode, city, state}`; with a store chosen also `storeName` (`N. Fontana Lowe's`) |
| `requestedStore`, `storeMatched` | Only with **Store number** or **ZIP code** set: the store you asked for (`{storeNumber, storeName, city, state, zipCode, lookupZipCode}`) and `true` when Lowe's priced this item's page for that store |
| `searchTerm`, `sourceUrl`, `scrapedAt` | Provenance |

With **Include product details** on, or for product URLs (the extra `detail` event), each product also gets:

| Field | Description |
|---|---|
| `description`, `pageTitle` | Full description as plain text; the marketing title of the product page (`Kobalt Next-Gen 24-volt ... (1-Battery, Charger and Soft Bag included) KDD 2024A-03`) |
| `specifications`, `specificationGroups` | The complete specification table as a dict (`{"Battery Voltage": "24-volt", ...}`, 40+ rows) and grouped the way the page shows it (`General`, `Dimensions`, `Features`, `Warranty`, ...) |
| `images`, `videos`, `documents` | All product images; videos (`{title, url, thumbnailUrl}`); manuals and guides as PDF links (`{title, type, url}`) |
| `variantOptions`, `variants` | Option dimensions (`[{name: "Common Measurement (W x L)", values: [...]}]`) and every variant with its own product id and URL (`{omniItemId, options, name, url, available, isSelected}`). Each variant is its own product page; open it for its price |
| `upc`, `gtin`, `manufacturer` | Consumer barcode, case barcode, manufacturer (only when the spec table names one) |
| `warranty` | Warranty rows of the spec table (`Warranty: 5-year limited; Battery Warranty: 3-year limited`) |
| `dimensions` | `{height, width, depth, lengthUnit, weight, weightUnit}` of the packaged item |
| `collection` | Adds the set's name, price, was-price and member item ids |
| `lowesExclusive`, `energyStar`, `sellingChannels`, `maxPurchaseQuantity`, `prop65Warning`, `hazmat`, `hazmatInfo`, `status`, `listedAt` | Flags and facts from the catalog record |
| `socialProof` | Lowe's own popularity notes (`1K+ bought last week`) |
| `storeAisle`, `storeBay` | Where the item sits in the store of the request (the one you chose, or the one Lowe's assigned) |
| `price`, `wasPrice`, `priceType`, `location` | The product page's own price. Search tiles Lowe's could not price get one here, often a store price with a was-price |

With **Include reviews** on, or for review-page URLs (the `review` event), one item per review:

| Field | Description |
|---|---|
| `type` | `"review"` |
| `omniItemId`, `productName`, `productUrl` | Product the review belongs to |
| `reviewId`, `legacyId`, `rating`, `title`, `text`, `submittedAt` | Review ids, stars (1-5), title, text (`null` for star-only reviews), ISO date |
| `reviewerName` | Reviewer's nickname as Lowe's shows it above the review date ("Donnie57", "Kim R"; placeholders such as "No name" are kept as the reviewer typed them); `null` when the review shows no name (some star-only ratings) |
| `reviewerId`, `reviewerLocation` | Always `null` on Lowe's: the page shows no reviewer id, profile or location (the keys are kept so review items have the same shape as in our Walmart and Viator Actors) |
| `positiveFeedback`, `negativeFeedback` | Helpful / not helpful votes |
| `verifiedPurchase`, `incentivized`, `recommended`, `badges` | Verified purchaser flag, "received an incentive" flag, "would recommend", badge texts |
| `photosCount`, `photos`, `videos` | Customer photos and videos |
| `sellerResponse`, `sellerResponseDate`, `sellerResponseBy` | Brand or Lowe's reply when present (`Kobalt Team`) |
| `ratingDimensions`, `isRatingOnly`, `language`, `syndicated`, `syndicatedFrom`, `scrapedAt` | Secondary ratings, star-only flag, locale, syndication source |

When reviews are on, the product item additionally carries `ratingCounts` (1-5 star breakdown), `recommendedCount`, `notRecommendedCount`, `recommendedPercentage`, `ratingsOnlyCount` and `reviewSummary` (Lowe's AI summary of the reviews). Reviewer avatars are not collected, and neither is the internal author id that Lowe's review data carries but never shows.

### How to use Lowe's Scraper

1. Type one or more **Search terms** (one per line) and/or paste **Start URLs**: search pages, product pages (`/pd/...`) or review pages (`/reviews/...`).
2. Pick **Sort by** (Top Sellers, Most Popular, Highest Rated, New Arrivals).
3. Optionally set a **Store number** or a **ZIP code** to get the prices and stock of one Lowe's store (see below).
4. Set **Max items** to cap the run (and the cost). Products and reviews both count.
5. Turn on **Include product details** and/or **Include reviews** only if you need them: each costs extra requests and events (see below).
6. Click **Start**. Results appear in the **Dataset** tab; export from there or use the API.

Category pages (`https://www.lowes.com/pl/...`) are not supported: they do not load through the Unblocker proxy (every attempt times out). Search for the category name instead ("cordless drills", "area rugs"); Lowe's search covers the whole catalog.

### Input example

```json
{
  "searchTerms": ["cordless drill"],
  "startUrls": [
    { "url": "https://www.lowes.com/pd/Kobalt-24-volt-Max-1-2-in-Metal-Ratcheting-Brushless-Cordless-Drill-1-Battery-Included-and-Charger-Included/5013207487" }
  ],
  "maxItems": 200,
  "maxPagesPerSearch": 5,
  "sortBy": "best_sellers",
  "includeSponsored": true,
  "includeDetails": false,
  "includeReviews": false,
  "maxReviewsPerProduct": 20,
  "reviewSort": "newest",
  "proxyConfiguration": { "useApifyProxy": true, "apifyProxyGroups": ["UNBLOCKER"], "apifyProxyCountry": "US" }
}
```

### Output example

A product from a search page:

```json
{
  "type": "product",
  "omniItemId": "50040962",
  "itemNumber": "506283",
  "modelId": "DCD771C2",
  "name": "DEWALT 20-volt Max 1/2-in Keyless Cordless Drill 2 -Batteries Included, and Charger Included",
  "url": "https://www.lowes.com/pd/DEWALT-20-Volt-Max-1-2-in-Cordless-Drill-Charger-Included/50040962",
  "brand": "DEWALT",
  "price": 129.0,
  "wasPrice": 179.0,
  "savingsAmount": 50.0,
  "savingsPercent": 27,
  "priceText": "$129.00",
  "priceType": "NETWORK",
  "saleEndDate": "2026-10-31",
  "variantPriceMin": null,
  "variantPriceMax": null,
  "currency": "USD",
  "unitPrice": null,
  "rating": 4.6,
  "reviewCount": 1398,
  "availability": "IN_STOCK",
  "inStock": true,
  "availabilityText": null,
  "fulfillment": {
    "shipping": true, "pickup": false, "delivery": true, "sameDayDelivery": false, "freeShipping": true,
    "shippingText": "Ships, arrives 2026-10-06", "pickupText": null, "deliveryText": null,
    "arrivesBy": "2026-10-06", "storeQuantity": 3, "networkQuantity": 79, "lowStock": true
  },
  "badges": [],
  "promotions": [],
  "isSponsored": false,
  "isMarketplace": false,
  "sellerName": "Lowe's",
  "programType": "Stock",
  "imageUrl": "https://mobileimages.lowes.com/productimages/98901790-b0c3-4fcd-ac02-26524801b302/94752317.jpeg",
  "images": ["https://mobileimages.lowes.com/productimages/98901790-b0c3-4fcd-ac02-26524801b302/94752317.jpeg", "..."],
  "category": "Drills",
  "categoryPath": [],
  "collection": { "id": "GR_9671", "type": "PCR_GROUP", "url": "https://www.lowes.com/collections/DEWALT-20V-1-2-in-Cordless-Drill-Kit-20V-Max-Cordless-Reciprocating-Saw/GR_9671" },
  "variantCount": 0,
  "variantAttribute": null,
  "variants": [],
  "specifications": { "Cordless": "Yes", "Brushless": "No", "Battery Included": "Yes" },
  "highlights": [],
  "packageQuantity": 1,
  "position": 4,
  "page": 1,
  "location": { "storeNumber": "1539", "zipCode": null, "city": null, "state": null },
  "searchTerm": "cordless drill",
  "sourceUrl": null,
  "scrapedAt": "2026-10-02T21:10:08+00:00"
}
```

With `includeDetails: true` a product additionally carries, for example:

```json
{
  "pageTitle": "Kobalt Next-Gen 24-volt 1/2-in Keyless Brushless Cordless Drill (1-Battery, Charger and Soft Bag included) KDD 2024A-03",
  "description": "This new generation Kobalt drill driver uses the most innovative electronics and motor technology to deliver up to 850 in-lb of torque ...",
  "specifications": { "Package Quantity": "1", "Battery Type": "Lithium ion (Li-ion)", "Battery Voltage": "24-volt", "Maximum Chuck Size": "1/2-in", "Warranty": "5-year limited" },
  "specificationGroups": { "General": { "Battery Voltage": "24-volt" }, "Warranty": { "Warranty": "5-year limited", "Battery Warranty": "3-year limited" } },
  "warranty": "Warranty: 5-year limited; Battery Warranty: 3-year limited",
  "upc": "195532000342",
  "gtin": "20195532000346",
  "images": ["https://mobileimages.lowes.com/productimages/2ebf015b-659b-4c82-b909-38aa892c89c4/97265246.jpeg", "..."],
  "videos": [{ "title": "Kobalt 24V Max Brushless Drill Driver Kit Overview", "url": "https://cdnapisec.kaltura.com/p/4215793/sp/421579300/playManifest/entryId/1_itmhknf0/format/applehttp/protocol/https", "thumbnailUrl": "https://mobileimages.lowes.com/productimages/ecd5812d-35e6-4c1f-9314-3cf52f32717a/4913878-Gen3-Drill-Driver-KIT_thumbnail.jpg" }],
  "documents": [{ "title": "Use and Care Manual", "type": "USE_AND_CARE_GUIDE", "url": "https://pdf.lowes.com/productdocuments/bdb07167-0a9c-457e-87b1-f25257337efb/60077854.pdf" }],
  "dimensions": { "height": 9.45, "width": 6.3, "depth": 12.8, "lengthUnit": "Inches", "weight": 8.11, "weightUnit": "lbs." },
  "categoryPath": ["Tools", "Power Tools", "Drills & Drivers", "Drills"],
  "collection": { "id": "GR_28003", "type": "PCR_GROUP", "name": "24V Brushless Drill Driver & 105-Piece Bit Set", "url": "https://www.lowes.com/collections/Kobalt-24V-Brushless-Drill-Driver-105-Piece-Bit-Set/GR_28003", "price": 123.98, "wasPrice": 188.98, "items": ["5013207487", "5015524807"] },
  "variantOptions": [{ "name": "Common Measurement (W x L)", "values": ["2 x 3", "2 x 7", "5 x 7", "8 x 10"] }],
  "variants": [{ "omniItemId": "5017295587", "options": { "Common Measurement (W x L)": "2 x 3" }, "name": "2 x 3", "url": "https://www.lowes.com/pd/allen-roth-Tess-2-x-3-ft-.../5017295587", "available": true, "isSelected": false }],
  "socialProof": ["1K+ bought last week", "10K+ bought last month"],
  "storeAisle": "4",
  "storeBay": "ENDCAP FRONT",
  "maxPurchaseQuantity": 10,
  "prop65Warning": true,
  "hazmat": true,
  "hazmatInfo": "Item is a lithium ion battery with Watt hours (Wh) </= to 100 Wh and Battery Packed with Equipment"
}
```

A review:

```json
{
  "type": "review",
  "omniItemId": "5013207487",
  "reviewId": "4eb70325-c68f-5903-b68a-158645bd6758",
  "legacyId": "272381997",
  "rating": 1,
  "title": "Junk after a year",
  "text": "I purchased this at Christmas and now it's having a problem where it will work for one trigger pull then it won't work until I pull the battery out and reset it.",
  "submittedAt": "2026-09-27",
  "reviewerName": "Jim",
  "reviewerId": null,
  "reviewerLocation": null,
  "positiveFeedback": 0,
  "negativeFeedback": 0,
  "verifiedPurchase": false,
  "incentivized": false,
  "recommended": false,
  "badges": [],
  "photosCount": 0,
  "photos": [],
  "videos": [],
  "sellerResponse": "Hi Jim! Thank you for reviewing your Kobalt KDD 2024A-03 cordless drill kit. We're sorry to hear that you are having trouble with the trigger ...",
  "sellerResponseDate": "2026-09-28",
  "sellerResponseBy": "Kobalt Team",
  "ratingDimensions": null,
  "isRatingOnly": false,
  "language": "en_US",
  "syndicated": false,
  "syndicatedFrom": null,
  "productName": "Kobalt 24-volt 1/2-in Keyless Brushless Cordless Drill 1 -Batteries Included, and Charger Included",
  "productUrl": "https://www.lowes.com/pd/Kobalt-24-volt-Max-1-2-in-Metal-Ratcheting-Brushless-Cordless-Drill-1-Battery-Included-and-Charger-Included/5013207487",
  "scrapedAt": "2026-10-02T21:10:08+00:00"
}
```

### Proxy note

Lowes.com answers datacenter and residential proxy traffic with Akamai's "Access Denied", so this Actor uses **Apify's Unblocker proxy** (group `UNBLOCKER`, US) by default. Unblocker is billed to your Apify account per attempt: 10 Unblocker units, about **$0.0015 on the free plan** and less on paid plans. Failed attempts are billed too, except those that run into the proxy's 60-second limit (measured in November 2026: 68 of 82 attempts were billed). An attempt that loads returns a search page (24 products), one product page, or 10 reviews. The proxy renders search and product pages in a browser, 10-65 seconds per attempt; reviews answer in about a second.

Lowe's lets only part of these attempts through: in November 2026 about 1 in 4 on search pages and between 1 in 5 and 9 in 10 on product pages, depending on the hour. So the Actor tries a search page up to 24 times and a product page up to 20 times: one attempt at a time while pages load, and after a failure one more in parallel per failure (up to 5; all 5 at once when the run is close to its timeout), each on a new IP. As soon as one loads, the others are cancelled. A search page usually takes 3-8 attempts, about $0.005-0.012 of proxy cost. If a page still fails, later pages of the same kind get 4 attempts each until one loads again, so a bad hour does not run up your proxy bill, and no new attempt starts close to the run's timeout, so the run always ends with its results and `SUMMARY`.

The Actor is built around that price: it requests a further result page only when **Max items** still needs it, and opens product pages and review requests only when you ask for them. The `SUMMARY` record in the run's key-value store reports `successfulRequests` and, per page type, `attempts` (attempts sent, pages loaded, attempts cancelled), so you can check the proxy cost of every run. You can switch to another proxy group in the input; the Actor then retries block pages on fresh IPs and reports them as `blockedPages`.

### How much does it cost?

Pay per event, no subscription: **$0.003 per product**, **+ $0.004 per product page opened** (details), **$0.0008 per review**, plus the proxy requests above and a few cents of platform usage per thousand items. Worked example for 100 products from one search, on the free plan:

| Run | Requests (proxy cost) | Events | Total |
|---|---|---|---|
| 100 products, search data only | 5 search pages, about 25 attempts ($0.04) | 100 x $0.003 = $0.30 | about $0.35 |
| + product details | + 100 product pages, about 100-300 attempts ($0.15-0.45) | + 100 x $0.004 = $0.40 | about $0.90-1.20 |
| + 20 reviews per product (set Max items to 2,100) | + 200 review requests ($0.30) | + 2,000 x $0.0008 = $1.60 | about $2.80-3.10 |

Attempt counts vary with how many Lowe's lets through that hour; `SUMMARY.notes.attempts` shows the real numbers of a run.

Search data alone is by far the cheapest per product: one loaded search page brings 24 products. Turn details and reviews on only for the products you need them for, for example by running a second pass on product URLs you selected from the first dataset.

### How to scrape Lowe's search results by keyword

Put the keywords into **Search terms**. Each term is searched on Lowes.com and paginated automatically, 24 products per page including the few sponsored tiles Lowe's mixes in (`isSponsored`; turn **Include sponsored products** off for organic results only). Lowe's serves at most 600 products (25 pages) per query, so one term yields up to 600 products; use several terms or narrower terms to go deeper. `position` and `page` give the rank of every product, which makes the output usable for share-of-search and rank tracking. Four of Lowe's sort orders are supported (Top Sellers, Most Popular, Highest Rated, New Arrivals); the two price sorts need a selected store on Lowe's side and are not available to the Actor.

### How to track Lowe's prices and stock

Schedule the Actor (daily for fast-moving items) with the same search terms or product URLs and compare `price`, `wasPrice`, `saleEndDate`, `availability` and `badges` between runs, joining on `omniItemId`. Product URLs can be passed in bulk in **Start URLs**; each returns one fully detailed item. Note that 15-30 % of search tiles come without a price (Lowe's search page cannot price every item for a visitor without a store; `availability` is `UNKNOWN` for them) and that the search page shows the was-price for some sale items only; with **Include product details** on, every product gets the price of its own product page, which includes the was-price when there is one.

### How to scrape Lowe's product reviews

Either turn on **Include reviews** (reviews for every product found), or paste review-page URLs such as `https://www.lowes.com/reviews/kobalt-kdd-2024a-03-drills/5013207487` to get reviews only. **Max reviews per product** caps each product; **Sort reviews** picks newest, oldest, most relevant, most helpful, highest or lowest rating, or photo reviews first. Lowe's serves 10 reviews per request and lists star-only reviews too (`text` is `null` for them). For monitoring, sort by newest and deduplicate on `reviewId`.

### How to choose a Lowe's store or ZIP code

Lowe's prices and stock are store-specific: the same drill can have 15 units on the shelf in North Bergen, NJ and 11 in Fontana, CA, a store price in one place and the online price in another, pickup in one store and only shipping in the next. Without a store, Lowe's decides per request: search pages usually come in its default online context (store 1539, no ZIP, `priceType: "NETWORK"`), and product pages sometimes in a store guessed from the proxy's IP address, which changes between requests. So two runs can differ for no real reason.

To fix the store, set **Store number** (`storeId`) to the 4-digit Lowe's store number. You find it at the end of the store's page on lowes.com, for example `1106` in `lowes.com/store/NJ-North-Bergen/1106`, or in Lowe's store directory (`lowes.com/Lowes-Stores`). Every search page, product page and review request of the run is then made for that store, the same way Lowe's own "Set as my store" button does it, and the items show that store's price, pickup stock, aisle and delivery options:

```json
{ "searchTerms": ["cordless drill"], "storeId": "1700", "maxItems": 24 }
```

If you only know a ZIP code, set **ZIP code** (`zipCode`) instead. The Actor reads your state's page of Lowe's store directory (one extra proxy request per run, no extra charge) and uses the store in that ZIP, or else the store whose ZIP code is closest in the same area. That is usually, not always, the nearest store, so check `requestedStore` in the output and switch to **Store number** when you need an exact store. When **Store number** is set, **ZIP code** is ignored.

Every product then carries `requestedStore` (the store you asked for) and `storeMatched`. `storeMatched: true` means Lowe's priced that page for your store; `false` means Lowe's answered for another store or for its online default (`location` shows which), so do not read that item's stock as your store's. A page that comes back for another store is requested once more (one more proxy request), so `false` is rare. A store's search also lists what that store and its delivery network carry, so the result count can be lower than without a store. Without a store number or ZIP code the Actor behaves exactly as before and these two fields are not added.

### Does it work without login or a browser?

Yes. Everything comes from public pages as an anonymous visitor sees them; you do not run a browser, the Unblocker proxy does. No account. The only cookie the Actor sends is the store choice, when you set one. Member-only (MyLowe's Rewards) prices and in-cart prices are not available.

### Integrations and API

Use the run in Zapier, Make, n8n, Google Sheets, or call it from Python/Node with the Apify client. See the **API** tab for ready-made snippets.

### Is it legal to scrape Lowe's?

This Actor collects only data that Lowe's shows publicly to logged-out visitors: product listings, prices, marketplace seller names, and the text, rating and reviewer nickname of customer reviews, as the review page shows them. It does not log in and does not collect avatars, contact details or the hidden author ids in Lowe's review data. Reviewer nicknames, and review texts written by individuals, can be personal data under GDPR and similar laws: make sure you have a lawful basis for processing them, and do not use them to contact or profile individuals. You are responsible for how you use the data and for complying with Lowe's terms and applicable law.

### Support

Found a bug or need a field added? Open an issue in the **Issues** tab; it is usually answered within a day.

### Related scrapers

- [eBay Scraper](https://apify.com/tortuga/ebay-scraper): Listings, Prices & Sellers
- [Etsy Scraper](https://apify.com/tortuga/etsy-scraper): Listings, Prices, Shops & Badges
- [Walmart Scraper](https://apify.com/tortuga/walmart-scraper): Products, Prices & Reviews
- [Target Scraper](https://apify.com/tortuga/target-scraper): Products, Prices, Stock & Reviews
- [Home Depot Scraper](https://apify.com/tortuga/home-depot-scraper): Products, Prices & Reviews

### Found it useful?

A ★ review on this Actor's Apify Store page helps other users find it and tells us what to improve. Issues and feature requests are welcome in the Actor's **Issues** tab.

# Actor input Schema

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

Keywords to search on Lowes.com, one per line (e.g. "cordless drill", "area rug"). Each term is searched separately and paginated automatically: one request returns 24 products (sponsored tiles included).

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

Lowes.com URLs to scrape instead of (or in addition to) search terms: search pages (https://www.lowes.com/search?searchTerm=cordless+drill), product pages (https://www.lowes.com/pd/Kobalt-24-volt-Max/5013207487, returned with full details) or review pages (https://www.lowes.com/reviews/kobalt-kdd-2024a-03-drills/5013207487, returns reviews only). Category pages (/pl/...) do not load through the Unblocker proxy and are skipped with an error; search for the category name instead.

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

Stop after this many dataset items in total. Products and reviews both count, so with reviews on raise it accordingly (100 products with 20 reviews each = 2,100 items). Keeps cost predictable.

## `maxPagesPerSearch` (type: `integer`):

How many result pages to open per search term or search URL (24 products each, one proxy request per page). Lowe's itself stops after 600 products (25 pages).

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

Result order for search terms and search URLs, as on Lowe's own "Sort By" menu. Lowe's offers "Price (Low to High)" / "Price (High to Low)" only to visitors who selected a store, and ignores them otherwise, so they are not available here.

## `includeSponsored` (type: `boolean`):

Lowe's mixes a few sponsored tiles into the results (usually 4 on the first page). They are saved with isSponsored = true. Turn this off to keep organic results only.

## `storeId` (type: `string`):

Lowe's store number, e.g. 1106 (North Bergen, NJ) or 1700 (N. Fontana, CA): the number at the end of the store's page on lowes.com (lowes.com/store/NJ-North-Bergen/1106) or in Lowe's store directory (lowes.com/Lowes-Stores). Every page is then requested for that store, as if you had clicked "Set as my store": its pickup stock, aisle, delivery options and store price. Each product gets requestedStore and storeMatched (true when Lowe's served that page for your store). Leave empty to get Lowe's default view, which can change from request to request.

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

US ZIP code, used instead of a store number. The Actor reads your state's page of Lowe's public store directory (one extra request per run) and picks the store in that ZIP, or else the one with the closest ZIP code in the same area. That is close to, but not always, the nearest store: check requestedStore in the output and set Store number for an exact store. Ignored when Store number is set.

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

Also open every product page to get the description, full specification table, all images, videos, manuals, variants (sizes / colors with their own product ids), UPC, dimensions, warranty, collection / set, store aisle and the product page's own price (listing tiles Lowe's could not price get one here). One extra proxy request and one extra `detail` charge per product (see pricing).

## `includeReviews` (type: `boolean`):

Also collect customer reviews for every product, as separate items with type = "review" (rating, title, text, date, helpful votes, verified purchase, photos, brand response). Lowe's serves 10 reviews per request. Reviewer names are never collected.

## `maxReviewsPerProduct` (type: `integer`):

Cap on reviews per product (also applies to review-page start URLs). Lowe's lists star-only reviews too; they come with text = null.

## `reviewSort` (type: `string`):

Order in which reviews are collected (matters when a cap is set). "Newest" is Lowe's default and best for monitoring; "Lowest rating" surfaces complaints.

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

Lowes.com answers datacenter and residential proxies with Akamai's "Access Denied", so the default is Apify's Unblocker proxy (group UNBLOCKER, US). It is billed to your Apify account per attempt, most failed attempts included: 10 Unblocker units, about $0.0015 on the free plan and less on paid plans. An attempt that loads returns a search page (24 products), one product page, or 10 reviews. Lowe's lets only some attempts through (about 1 in 4 on search pages in November 2026), so the Actor tries a search page up to 24 times and a product page up to 20 times, several at once after a failure; a search page usually takes 3-8 attempts. Search and product pages are rendered in a browser by the proxy, 10-65 seconds per attempt.

## Actor input object example

```json
{
  "searchTerms": [
    "cordless drill"
  ],
  "startUrls": [],
  "maxItems": 20,
  "maxPagesPerSearch": 1,
  "sortBy": "featured",
  "includeSponsored": true,
  "includeDetails": false,
  "includeReviews": false,
  "maxReviewsPerProduct": 20,
  "reviewSort": "newest",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "UNBLOCKER"
    ],
    "apifyProxyCountry": "US"
  }
}
```

# Actor output Schema

## `results` (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": [
        "cordless drill"
    ],
    "startUrls": [],
    "maxItems": 20,
    "maxPagesPerSearch": 1,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "UNBLOCKER"
        ],
        "apifyProxyCountry": "US"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("tortuga/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": ["cordless drill"],
    "startUrls": [],
    "maxItems": 20,
    "maxPagesPerSearch": 1,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["UNBLOCKER"],
        "apifyProxyCountry": "US",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("tortuga/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": [
    "cordless drill"
  ],
  "startUrls": [],
  "maxItems": 20,
  "maxPagesPerSearch": 1,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "UNBLOCKER"
    ],
    "apifyProxyCountry": "US"
  }
}' |
apify call tortuga/lowes-scraper --silent --output-dataset

```

## MCP server setup

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