March 3, 2026

Lowe’s Scraping Guide: How to Extract Prices, Inventory, and Specs from Embedded JSON and Network Calls

clock
9
min read
Copied!

Tom Shaked

linkedin
No items found.
Lowe’s Scraping Guide: How to Extract Prices, Inventory, and Specs from Embedded JSON and Network Calls
March 3, 2026

Lowe’s Scraping Guide: How to Extract Prices, Inventory, and Specs from Embedded JSON and Network Calls

clock
9
min read
Copied!

Tom Shaked

linkedin
No items found.
Lowe’s Scraping Guide: How to Extract Prices, Inventory, and Specs from Embedded JSON and Network Calls

Lowe’s Scraping Guide: How to Extract Prices, Inventory, and Specs from Embedded JSON and Network Calls

If you’ve ever scraped Lowe’s using CSS selectors and wondered why your parser suddenly started returning empty fields, the problem is usually not your code.

On Lowe’s, the DOM is often not the source of truth.

Critical product data such as pricing, inventory, fulfillment options, and even specifications is frequently delivered via:

  • embedded JSON objects injected into the page
  • client-side state used by the front end
  • network calls triggered after page load

This guide walks through how scraping teams can reliably extract Lowe’s product data by targeting structured data instead of rendered HTML, and how to design parsers that survive UI changes.

Why DOM-Based Scraping Breaks on Lowe’s

Lowe’s product pages are heavily JavaScript-driven. In many cases:

  • Price elements are placeholders until store context is resolved
  • Inventory and pickup eligibility appear only after hydration
  • CSS class names are generic, nested, or frequently changed
  • A/B tests alter markup without warning

From a scraper’s perspective, this leads to the worst failure mode:
successful requests that return incomplete data with no errors.

This is why teams that scrape Lowe’s reliably treat the HTML DOM as a fallback, not the primary data source.

Where Lowe’s Product Data Actually Lives

In practice, Lowe’s exposes structured data in three main ways:

  1. Embedded JSON blobs inside script tags
  2. Client-side state objects (used by React/Redux-style apps)
  3. Network (XHR / fetch) responses returning JSON

A resilient Lowe’s scraper targets these sources in that order.

Extracting Embedded JSON from Lowe’s Product Pages

Many Lowe’s PDPs include a serialized product state object embedded directly in the page source. Even if the price is not visible in the UI, this object may already contain pricing metadata.

Python example: extracting embedded state

import json
import re
import requests

PDP_URL = "https://www.lowes.com/pd/DEWALT-20V-MAX-XR-Cordless-Drill-2-Tool-Combo-Kit/1000552693"

html = requests.get(PDP_URL, timeout=30).text

# Look for a serialized state object in a script tag
match = re.search(
    r"__PRELOADED_STATE__\s*=\s*({.*?})\s*;\s*</script>",
    html,
    re.DOTALL
)

if not match:
    raise RuntimeError("Preloaded state not found")

state_json = match.group(1)
state = json.loads(state_json)

print("Top-level keys:", list(state.keys())[:10])

Why this works

  • Embedded state often survives UI redesigns
  • Data is already normalized for frontend use
  • You avoid fragile DOM traversal

Why it still fails sometimes

  • Variable names change
  • Store-dependent fields may still be empty
  • Some PDPs rely entirely on follow-up network calls

This is why embedded JSON is step one, not the entire solution.

Extracting Product Fields Defensively

Once you’ve loaded the JSON, treat it as untrusted input. Field paths change, and not every PDP has the same shape.

Example: defensive field access

def safe_get(obj, path, default=None):
    cur = obj
    for key in path:
        if isinstance(cur, dict) and key in cur:
            cur = cur[key]
        else:
            return default
    return cur

price = safe_get(state, ["product", "pricing", "price"])
availability = safe_get(state, ["product", "fulfillment", "availability"])

print("Price:", price)
print("Availability:", availability)

Good practice:

  • Validate that price fields are numeric
  • Flag placeholder strings early
  • Log schema drift instead of silently swallowing it

Capturing Network Calls for Inventory and Fulfillment Data

Many of Lowe’s most dynamic fields—inventory counts, pickup eligibility, delivery estimates—are loaded via network requests after page load.

Rather than scraping rendered HTML, teams often intercept these responses directly.

Playwright example: capturing JSON responses

import { chromium } from "playwright";

const PDP_URL = "https://www.lowes.com/pd/DEWALT-20V-MAX-XR-Cordless-Drill-2-Tool-Combo-Kit/1000552693";

(async () => {
  const browser = await chromium.launch({ headless: true });
  const page = await browser.newPage();

  const jsonResponses = [];

  page.on("response", async (response) => {
    const contentType = response.headers()["content-type"] || "";
    if (contentType.includes("application/json")) {
      try {
        const body = await response.json();
        jsonResponses.push({ url: response.url(), body });
      } catch (_) {}
    }
  });

  await page.goto(PDP_URL, { waitUntil: "networkidle" });

  console.log("Captured JSON responses:", jsonResponses.length);
  console.log(
    "Sample URLs:",
    jsonResponses.slice(0, 5).map(r => r.url)
  );

  await browser.close();
})();

What teams do next

  • Identify which responses contain inventory or fulfillment data
  • Call those endpoints directly for efficiency
  • Keep rendering as a fallback when endpoints change

Designing a Parser Strategy Ladder for Lowe’s

A single extraction strategy is rarely sufficient. Reliable pipelines use a layered approach:

  1. Embedded JSON (fast, stable)
  2. Network JSON (accurate for live fields)
  3. DOM parsing (last resort only)

Python skeleton for a strategy ladder

def parse_lowes_product(html, network_payloads=None):
    # 1. Embedded state
    state = try_extract_state(html)
    if state:
        parsed = parse_from_state(state)
        if parsed.get("price") is not None:
            return parsed

    # 2. Network responses
    if network_payloads:
        parsed = parse_from_network(network_payloads)
        if parsed.get("price") is not None:
            return parsed

    # 3. DOM fallback
    parsed = parse_from_dom(html)
    return parsed

This approach:

  • Reduces breakage from UI changes
  • Makes failures observable
  • Improves long-term maintenance

A Higher-Level Way to Extract Lowe’s Structured Data

Maintaining this extraction stack yourself means:

  • monitoring frontend changes
  • updating parsers when schemas drift
  • keeping rendering, network capture, and fallbacks in sync

An API-based approach abstracts those mechanics while preserving the same data sources.

Example: Using Nimble’s Network Capture function to extract structured data

import json
from nimble_python import Nimble

PDP_URL = "{PDP_URL}"

nimble = Nimble(api_key="YOUR_NIMBLE_API_KEY")

res = nimble.extract(
    url=PDP_URL,
    country="US",
    render=True,
    parse=False,
    network_capture=[
        {
            "method": "GET",
            "resource_type": ["xhr", "fetch"],
            "wait_for_requests_count": 3,
            "wait_for_requests_count_timeout": 10,
        }
    ],
)

# First captured JSON response
captures = res.data.network_capture or []
results = captures[0]["result"] if captures else []
json_body = None
for item in results:
    body = item["response"].get("body")
    if isinstance(body, (dict, list)):
        json_body = body
        break
    if isinstance(body, str):
        try:
            json_body = json.loads(body)
            break
        except Exception:
            pass

print(json.dumps(json_body, indent=2))

What Network Capture Actually Means (Beyond “Listening to Requests”)

At a technical level, network capture means observing every request and response the page makes during execution, not just the final rendered HTML.

That includes:

  • initial document requests
  • API calls triggered during hydration
  • follow-up inventory checks
  • pagination and fulfillment lookups
  • retry or fallback requests the frontend makes silently

For Lowe’s, this is critical because:

  • Pricing is often resolved via background requests after store context is applied
  • Inventory and pickup eligibility are fetched independently of product metadata
  • Delivery estimates are computed by separate services
  • Some data never appears in the DOM at all

A browser-based capture layer sees all of this, whether or not it’s ever rendered visually.

How this simplifies Lowe’s scraping

Behind the scenes, this approach:

  • Executes the same JavaScript Lowe’s uses
  • Observes the same network calls a browser makes
  • Extracts structured data directly from those sources
  • Normalizes output even when page structure shifts

Instead of maintaining DOM selectors and endpoint mappings yourself, you receive analysis-ready data that stays consistent as Lowe’s frontend evolves.

Conclusion

On Lowe’s, the rendered HTML is often not the source of truth. Critical product data such as pricing, inventory, and fulfillment is delivered through embedded JSON objects and network calls that may never appear in the DOM.

This guide showed how scraping teams extract structured data directly from those sources, why layered extraction strategies are necessary, and how network-level visibility improves reliability as Lowe’s frontend evolves.

Further Reading

For full context on building a reliable Lowe’s pipeline, see the related guides in this series:

  • Lowe’s Scraping API: How to Reliably Extract Product, Price, and Availability Data
    An overview of Lowe’s scraping challenges and reliability considerations.
  • Lowe’s Store Scraping: How to Set Store Location Reliably for Accurate Pricing and Availability
    Why dynamic data often remains incomplete without proper store context.
  • Lowe’s Scraping at Scale: Why It Works at 100 URLs and Fails in Production
    How extraction strategies that work locally can fail when scaled.

FAQ

Answers to frequently asked questions

No items found.