Lowe's Scraper - Products, Prices, Stock & Reviews avatar

Lowe's Scraper - Products, Prices, Stock & Reviews

Pricing

from $1.70 / 1,000 product records

Go to Apify Store
Lowe's Scraper - Products, Prices, Stock & Reviews

Lowe's Scraper - Products, Prices, Stock & Reviews

Scrape Lowe's (lowes.com) home improvement, tools, appliances and building supplies by keyword search. Extract product names, brands, prices, descriptions, specifications, stock availability, ratings and customer reviews. Ideal for price monitoring, competitor analysis and retail market research.

Pricing

from $1.70 / 1,000 product records

Rating

0.0

(0)

Developer

Abot API

Abot API

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

5 days ago

Last modified

Categories

Security

Limited permissions

Share

Lowe's Product Scraper

Scrape Lowes.com product listings: keyword search, full product details, prices, stock, ratings and customer reviews. Lowe's is the second-largest US home-improvement retailer (tools, decor, building supplies, hardware), and this actor turns its catalog into clean structured rows for price monitoring, assortment analytics and competitor research.

Why This Scraper?

  • Two ways in. Point it at keyword searches, or paste storefront links - search pages, category pages and product pages all walk with the same grammar.
  • Deep product records. Every listing row carries title, brand, price (with was-price), the per-unit price when the storefront publishes one, stock quantity, rating, review count, badges, promotions and the spec table; detail enrichment adds the description, model number, barcode, availability, variants and the full image gallery.
  • Customer reviews included. Rating summary, star distribution and up to 20 review texts per product, with verified-purchaser, recommendation and helpful-vote fields.
  • Scheduled-run friendly. Incremental mode classifies every product NEW / UPDATED / UNCHANGED / EXPIRED across runs, so a daily monitor pushes only what moved; a product whose listing data did not change is neither read again nor charged. A price-and-availability scope, compact change rows and a grace period before EXPIRED keep a price monitor cheap.
  • Resume that never double-charges. Paste the ID of an interrupted run and the products it already collected are skipped (and not billed again); a run the platform restarts continues by itself.
  • Built for the wall. The storefront's edge refuses most scripted readers, so the actor reads the storefront's own listing and product data over residential US exits and moves to a fresh exit automatically whenever one is refused.

Use Cases

  • Who: pricing teams tracking Lowe's promos on competitive assortments - price and was-price land per row (plus a per-unit price where the storefront lists one), updated per scheduled run.
  • Who: sellers and brands auditing their shelf - badges, promotions, stock quantities and review velocity per product.
  • Who: analysts building home-improvement datasets - walk any keyword search with the same grammar and export straight to CSV/Excel or a warehouse.

Data You Get

Sample shape: values are illustrative placeholders, not from a live record.

FieldExample
title / brand / brandId20-volt 1/2-in Keyless Brushless Cordless Drill / CRAFTSMAN / 722
price / wasPrice129.0 / null (a marked-down product shows its reference price)
priceUnavailableReasonnull (set only when price is null: no_standalone_offer or not_published, see Limitations)
savingsAmount / savingsPercent / saleEndsAt50.0 / 27.0 / 2026-10-31T23:59:59.000Z (marked-down products only)
priceInfoThe storefront's price block: display type, selling and was price, savings, sale window and every price row
pricePerUnitnull (filled only when the storefront lists a unit price; the drill is each-priced)
stockQuantity / inStock423 / true
pickupEligible / parcelEligible / freeShippingtrue / true / true
inventoryAvailability per delivery method (parcel, pickup, truck, same day): status, units, lead time
promotionsProduct, finance and shipping promotions attached to the product, with ids, names and dates
modelId / itemNumber / lin / productIdCMCD714D1 / 6812916 / 1000742101 / 5016412045
barcode198706013438
rating / reviewsCount / badges4.9 / 96 / ["LOWES EXCLUSIVE"]
specs / measurements[{key: Cordless, value: Yes}, ...] / depth, width, height, weights with metric conversions
description / marketingBullets / availabilityFull product description text / highlights / In Stock
images / videos / guidesFull image gallery, product videos, manuals and compliance documents
variantsEvery option of the variant selector (finish, size...) with the product it leads to
categories / merchandisingHierarchy / breadcrumbsCategory ids, the business-area to assortment hierarchy, the shopper's category trail
groups / collectionCollections and bundles the product belongs to, with links
status / lowesExclusive / energyStarACTIVE / false / false
storeNumber / storeInfo1539 (the store nearest the connection) / the store the prices were read for
reviewsAverage / reviewsRecommended / ratingDistribution4.9 / 70 / [{stars: 5, count: 89}, ...]
reviews[{rating, title, text, author, verifiedPurchaser, ...}]
changeType / changedFieldsNEW / UPDATED / [price] (incremental mode)
priceChanges / variantChangesOld and new value of what moved (compact UPDATED and EXPIRED rows)

How to Use

Run it from your code

Python:

from apify_client import ApifyClient
client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("abotapi/lowes-com-scraper").call(run_input={
"mode": "search",
"searchTerms": ["cordless drill"],
"maxItems": 50,
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
print(item["title"], item["price"])

JavaScript:

import { Actor } from 'apify';
const run = await Actor.call('abotapi/lowes-com-scraper', {
mode: 'urls',
urls: ['https://www.lowes.com/search?searchTerm=drill'],
maxItems: 50,
});
const { default: dataset } = await Actor.openDataset(run.defaultDatasetId);
await dataset.forEach((item) => console.log(item.title, item.price));

Search by keyword:

{
"mode": "search",
"searchTerms": ["cordless drill", "ceiling fan"],
"maxItems": 50
}

Default links: leaving the Storefront links field unchanged scrapes one example of a keyword search page and a product page. Category pages (the /pl/<name>/<id> links in the storefront's menus) are supported too: paste one into Storefront links.

Walk a search page, a category page and one product (reviews capped):

{
"mode": "urls",
"urls": [
"https://www.lowes.com/search?searchTerm=drill",
"https://www.lowes.com/pl/christmas-decorations/christmas-trees/4294417432",
"https://www.lowes.com/pd/CRAFTSMAN-20-volt-Max-1-2-in-Keyless-Brushless-Cordless-Drill-1-Batteries-Included-Soft-Bag-Included/5016412045"
],
"maxItems": 100,
"maxReviews": 5
}

Scheduled price and availability monitor (slim change rows, two-scan grace before EXPIRED):

{
"mode": "search",
"searchTerms": ["cordless drill"],
"maxItems": 0,
"fetchDetails": false,
"fetchReviews": false,
"incrementalMode": true,
"changeScope": "priceAndStock",
"compactUpdates": true,
"emitExpired": true,
"expireAfterMissedScans": 2
}

Continue an interrupted run (nothing already collected is returned or billed again):

{
"mode": "search",
"searchTerms": ["cordless drill"],
"maxItems": 0,
"resumeFromRunId": "ID_OF_THE_INTERRUPTED_RUN"
}

Resume and scheduled monitoring

  • Resume from a previous run continues ONE interrupted run. Paste its run ID or dataset ID: the products it already collected are skipped and not billed again. A run that the platform restarts (for example after a server move) continues by itself with no ID. If the ID cannot be read the run stops with a message instead of collecting everything again. Resume never produces EXPIRED rows, because a resumed walk does not prove the whole scope was seen.
  • Incremental mode remembers the previous run of the same scope. The first run returns every product as NEW; later runs return NEW, UPDATED and REAPPEARED products only. Unchanged products are decided from the listing data alone, so they are not read again and not charged. The state is kept per scope (search terms or links, and the detail and review settings); caps, proxy and the incremental controls never start a new baseline. A State key names or deliberately shares a campaign.
  • Resume together with Incremental mode finishes an interrupted monitoring run: the products it already returned are not returned again and are tracked as seen. A monitoring run that ends early (time limit, abort, server move) remembers the rows it already returned, so the next run does not return them again either.
  • What counts as a change: price, was-price, savings and sale end, unit price, title, brand, model, in-stock, pickup and parcel availability, free shipping, packaging, badges, highlights, specifications, categories, collections and promotions. Never counted (still returned in the row): scrape time, the product's rank, the store nearest the connection, per-delivery-method availability detail, the category trail, and the counters stock units, review count and average rating, which tick between two reads of an unchanged product (turn on Count stock unit changes to compare the stock counter).
  • Changes to watch priceAndStock narrows the comparison to price, was-price, savings, sale end, unit price, availability, free shipping and per-option price and stock, so a title, description or specification edit never produces a billable UPDATED row.
  • Compact UPDATED rows carry ids, link, title, current price and availability plus priceChanges and variantChanges (old and new). They carry no product-page detail or reviews, so no detail or review charge applies and the product page is not read. NEW rows stay full.
  • EXPIRED rows appear only after a run that read the whole scope to its end: never when Max items capped it, a page could not be read, the time or spending limit stopped it, or Resume was used. A product dropping out of a result list is not necessarily unavailable: set Expire after missed scans to 2 or more to hold it until it has been missing from that many complete scans in a row. EXPIRED rows count against Max items.
  • Emit unchanged products returns the unchanged rows with the product-page details the last run collected for them. Each returned row is billed like any other row.

Tips

  • The storefront re-ranks results between runs - incremental mode treats a moving position as noise and never re-bills it.
  • Search rows carry the storefront's network price (storeNumber is null); product-page rows name the store nearest the connection in storeNumber, which incremental mode never counts as a change.
  • Flooring and other area-priced products show the storefront's per-square-foot price in price, exactly as the listing displays it.
  • A product can show no price at all (roughly one product in four on a broad search). These are items the storefront does not offer on their own: the listing card says "See Product Page for Pickup and Delivery Options" and the product record marks the item unavailable, typically because it is a component of a kit or collection or is not sold individually. The product page, the product's data record and selecting another store or ZIP code were all checked on several stores across the country and none of them carries a price for such an item, so price stays null rather than borrowing a kit's price. priceUnavailableReason says why: no_standalone_offer (the storefront flags the item as not offered on its own) or not_published (no price and no such flag). It is null whenever price is set. The field is not part of the incremental comparison.
  • Every run stores a short summary under the key OUTPUT of its key-value store: rows returned by change type, products scanned, whether every scope was read to its end, and whether the time or spending limit stopped it.
  • Max items counts the rows a run RETURNS, not the products it scans: with Incremental mode a run keeps walking the listing past unchanged products until it has returned that many changed rows (or reached the end), and Max items 0 means no limit.
  • breadcrumbs / categoryPath come from the product page itself. When only the product's data record can be read, the trail stays empty, or keeps the one the previous incremental run stored for that product.

Input Parameters

ParameterTypeDefaultDescription
modeselecturlssearch walks keyword searches; urls walks pasted storefront links.
searchTermsstring listdrillKeyword searches to walk (search mode). Sending search terms while leaving the example links untouched runs the search terms only.
urlsURL list2 example linksSearch pages, category pages (/pl/<name>/<id>) and product pages (links mode).
maxItemsinteger20Maximum records per run (up to 100000). 0 means no limit.
fetchDetailscheckboxtrueRead each product's detail page (description, model, barcode, gallery, breadcrumbs).
fetchReviewscheckboxtrueCollect rating summary and review texts per product.
maxReviewsinteger20Review texts collected per product, 0 to 100. 0 skips texts.
resumeFromRunIdstring(empty)Run ID or dataset ID of an interrupted run to continue; its collected products are skipped and not billed again.
incrementalModecheckboxfalseClassify rows NEW/UPDATED/UNCHANGED/EXPIRED across scheduled runs.
stateKeystring(empty)Names the incremental baseline; empty derives it from the scope.
emitUnchangedcheckboxfalseAlso return products that did not change, with their stored details.
emitExpiredcheckboxfalseReturn EXPIRED rows for products that left a fully scanned scope.
changeScopeselectallpriceAndStock compares only price, sale and availability.
compactUpdatescheckboxfalseSlim UPDATED and EXPIRED rows with old and new values.
expireAfterMissedScansinteger1Complete scans a product must be missing before it is EXPIRED (1 to 30).
watchStockQuantitycheckboxfalseAlso compare the stock-units counter.
ignoreFieldsForChangeslist(empty)Field names (or dotted paths such as variants.image) that never count as a change.
zipCodestring(empty)Not applied in this version: prices and stock come from the storefront's default store regardless of the connection country (see storeInfo). Leave empty.
mcpConnectorsstring list(hidden)Internal: connector ids that receive records as they push.
notionParentPageUrlstring(hidden)Internal: parent page for the Notion connector.
maxNotifyListingsinteger50 (hidden)Internal: cap on records sent to connectors per run.
proxyConfigurationproxyResidential USThe storefront refuses scripted readers on datacenter exits; residential is the supported lane. When the selected country's exits keep being refused, the run continues on the same proxy group in other countries (see Plan Requirement).

All fields are also accepted through the API exactly as documented, including the hidden internal ones.

Output Example

Sample shape: values are illustrative placeholders, not from a live record.

{
"recordId": "lowes:5016412045",
"productId": "5016412045",
"title": "20-volt 1/2-in Keyless Brushless Cordless Drill",
"brand": "CRAFTSMAN",
"price": 129.0,
"wasPrice": null,
"currency": "USD",
"stockQuantity": 423,
"rating": 4.9,
"reviewsCount": 96,
"badges": ["LOWES EXCLUSIVE"],
"availability": "In Stock",
"url": "https://www.lowes.com/pd/CRAFTSMAN-20-volt-Max-1-2-in-Keyless-Brushless-Cordless-Drill/5016412045"
}

Product rows with the detail enrichment on, from a live search walk.

Plan Requirement

This actor requires an Apify paid plan: the storefront's edge refuses datacenter exits, so every run reads pages through residential proxies, which are not available on the Apify Free plan.

Runs start on the proxy country you select (US by default). The storefront's refusals come in waves per country, so when the selected country's exits keep being refused, the run continues on residential exits of the same proxy group in other countries; the storefront answers those with the same US catalog, prices and stock, and the run log says "Served over the backup connection" when that happened. Proxy usage is billed by your Apify plan exactly as for the selected country.

FAQ

The actor collects publicly available product data from the storefront's own pages, at a polite pace, without logging into any account. Review your use case and the site's terms before running at scale.

Why does a run fail with a connection problem?

The storefront denies individual proxy exits intermittently, in waves. The actor moves to a fresh exit automatically, falls back to residential exits in other countries, and waits through short deny windows; a run that still reports a connection problem hit a fully closed window - retry later, the same input usually succeeds on the next run.

How much does a run cost?

You pay the actor's per-event pricing, plus your Apify plan's proxy usage. The charged events, exactly as the code bills them:

  • Product record - once per product row pushed to the dataset.
  • Detail enrichment - once per pushed row whose product page was read (fetchDetails on).
  • Customer reviews - once per pushed row that delivered at least one review text (not per review).

Every row the run returns bills the product record, whatever its kind: a NEW, UPDATED, compact UPDATED, UNCHANGED (only when you ask for them) or EXPIRED row. Rows that are not returned bill nothing: unchanged products in incremental mode are decided from the listing alone, so they cost no product-page read and no charge, and products skipped by Resume are not billed again. A compact UPDATED row carries no product-page detail or reviews, so it bills the record only. A run that reaches your spending limit stops before the next row is stored and says so; nothing is charged for a row that was not stored. Card-only rows (details off) and rows without reviews bill no enrichment event. Product details load on most but not all connections: when neither the product page nor the product's data record can be read after several fresh connections, a search-result row still ships with its listing fields only and no detail enrichment event is charged; a pasted product link has no listing row to fall back to, so it ships nothing and charges nothing. See the actor's Pricing tab for the registered event prices.

Can I track price changes daily?

Yes - schedule the run with incrementalMode on and the same inputs. Only NEW and UPDATED rows push, each with changedFields naming what moved. For a pure price monitor set changeScope to priceAndStock and compactUpdates on: a title or description edit then never produces a billable row, and each UPDATED row shows the old and new price.

What happens if a monitoring run is interrupted?

The rows a run returned before it ended are remembered (a run that reaches its time limit finishes cleanly with them, and a run that is aborted or moved by the platform saves them as it ends), so the next run does not return them again, and Resume skips the products an interrupted run already collected. If a run is killed outright without notice (for example out of memory), the last few rows it returned can come back once more on the next run; pasting that run's ID into Resume closes even that gap.

Send results into your apps (MCP connectors)

Connect the actor to Notion, Google Sheets and other MCP-capable apps from the Apify Console: records this run pushed are delivered as they land, capped per run. Connector settings ship hidden in the input and need no attention unless you use connectors.

๐Ÿ”— More scrapers you might like

Pair this actor with these related scrapers from the same team:

๐Ÿ‘— Kohl's Scraper
Scrape Kohl's (kohls.com) products by keyword search or category browsing. Extract...
๐Ÿงฉ CVS Scraper
Scrape CVS.com pharmacy, beauty, health and personal care products by keyword search...
๐Ÿ›’ Bunnings Scraper
Scrape bunnings.com.au products with full specifications, price, brand, stock, image...
๐Ÿ›’ The Warehouse NZ Scraper
Scrape The Warehouse New Zealand search results, category pages, and direct product URLs...
๐Ÿงฉ Joom Scraper
Scrape Joom products by keyword or URL. Extract product names, prices, discounts...
๐Ÿงฉ JB Hi-Fi AU Scraper
Scrape JB Hi-Fi Australia (jbhifi.com.au) products from the public storefront. Extract...

๐Ÿ‘‰ Browse all abotapi scrapers

๐Ÿ’ฌ Support

  • ๐Ÿž Found a bug or a missing field? Open a ticket on the Issues tab. We usually reply within hours.
  • ๐Ÿ› ๏ธ Need another site, extra fields or a private build? Email contact@abotapi.com.
  • โญ Enjoying it? A quick review on the actor page helps other users find it.