Skip to main content
TutorialsOctober 28, 2026 · 12 min read

How to compare Amazon and Walmart prices with Python

Compare Amazon and Walmart prices with Python. Extract product data, match listings, calculate price differences, and export results to CSV.

How to compare Amazon and Walmart prices with Python

The same product can have different prices on Amazon and Walmart, but comparing those prices takes more than extracting two numbers. You also need to account for product variations, different sellers, and listings that bundle additional items.

In this tutorial, you'll build a Python price comparison tool that retrieves product data from Amazon and Walmart using Spidra, checks whether the listings describe the same product, calculates price differences, and exports the results to a CSV file.

We'll use real listings for the Sony WH-1000XM5 wireless headphones and extend the script to compare multiple products.

What we're comparing

We'll start with the Sony WH-1000XM5 noise-canceling headphones in black.

Here are the two product listings:

Both listings describe the same headphone model and color.

The product data retrieved on October 28, 2026, shows these prices:

AmazonWalmart
ProductSony WH-1000XM5Sony WH-1000XM5
ColorBlackBlack
SellerAmazon.comFocus Camera
Listed price$228.00$298.00
CurrencyUSDUSD

Based on these prices, Amazon is $70 cheaper, a difference of approximately 23.49% relative to Walmart's price.

But there's more to the Walmart listing.

It also has an alternative new offer for $288, an open-box offer for $189, and a restored offer for $159.99.

If your script simply chooses the lowest number, it might compare Amazon's regular product listing against Walmart's restored headphones and report the wrong result.

We'll build the comparison around the featured listing prices, while also capturing Walmart's lowest available new offer separately.

The prices are snapshots of the advertised offers, not guaranteed checkout totals. They may change when you run the script.

Set up your Python environment

You'll need Python 3.9 or later, a Spidra account, and an API key.

Spidra provides structured product data from Amazon and Walmart through its scraping API. For these product pages, you can request JSON directly without writing CSS selectors or defining a custom extraction schema.

Create a free account at Spidra, generate an API key from the dashboard, and install the Python SDK:

pip install spidra

Set your API key as an environment variable. On macOS or Linux:

export SPIDRA_API_KEY="spd_YOUR_API_KEY"

On Windows PowerShell:

$env:SPIDRA_API_KEY="spd_YOUR_API_KEY"

Now let's retrieve the product information.

Extract Amazon and Walmart product prices

Create a file named inspect_products.py:

# inspect_products.py
import json
import os
from spidra import Spidra

client = Spidra(api_key=os.environ["SPIDRA_API_KEY"])

products = {
    "Amazon": "https://www.amazon.com/dp/B09XS7JWHH",
    "Walmart": "https://www.walmart.com/ip/386006068",
}

for retailer, url in products.items():
    result = client.scrape(url, output="json")
    data = result.content

    if isinstance(data, str):
        data = json.loads(data)

    print(f"{retailer}: {data['title']}")
    print(f"Price: ${data['price']:.2f}")

Run the script:

python inspect_products.py

Using the captured product data, the output is:

Amazon: Sony WH-1000XM5 Premium Noise Cancelling Wireless Headphones, Black
Price: $228.00
Walmart: Sony WH-1000XM5 The Best Wireless Noise Canceling Headphones, Black
Price: $298.00

The SDK submits the scraping requests and waits for their results. With output="json", Spidra returns structured product information for these pages.

You can see the complete fields available for each marketplace in the Amazon and Walmart documentation.

Understand the returned product data

The actual responses contain much more than the titles and prices.

The relevant Amazon fields look like this:

{
  "asin": "B09XS7JWHH",
  "title": "Sony WH-1000XM5 Premium Noise Cancelling Wireless Headphones, Black",
  "brand": "Sony",
  "price": 228,
  "currency": "USD",
  "listPrice": 399.99,
  "buyBox": {
    "seller": "Amazon.com"
  },
  "specifications": {
    "Color": "Black",
    "Model Name": "WH-1000XM5",
    "Mfr Part Number": "WH1000XM5/B"
  }
}

The Walmart response contains these fields:

{
  "usItemId": "386006068",
  "title": "Sony WH-1000XM5 The Best Wireless Noise Canceling Headphones, Black",
  "brand": "Sony",
  "manufacturerPartNumber": "WH1000XM5/B",
  "condition": "New",
  "price": 298,
  "currency": "USD",
  "seller": {
    "name": "Focus Camera"
  },
  "selectedOptions": [
    {
      "name": "Actual Color",
      "value": "Black"
    }
  ]
}

These are shortened excerpts of the real responses.

Notice that the two marketplaces organize some information differently.

Amazon stores the manufacturer part number inside specifications and the featured seller inside buyBox. Walmart returns the part number as manufacturerPartNumber and the seller as a separate object.

That's why the next step is to normalize both responses before comparing them.

Match the same product across Amazon and Walmart

Product matching is the most important part of a reliable price comparison.

Two listings can share a brand and model name while representing different configurations, conditions, or bundles. Comparing those prices as if they were interchangeable can produce misleading results.

For our Sony example, both products have these identifying details:

AttributeAmazonWalmart
BrandSonySony
Manufacturer part numberWH1000XM5/BWH1000XM5/B
ColorBlackBlack

Those identifiers give us a reasonable basis for comparing the two selected listings.

Now consider another Walmart listing:

Sony WH-1000XM5 with headphone stand and USB adapter

This listing costs $398 and includes additional accessories. Its manufacturer part number is WH1000XM5/B_EDI_1.

Although the product title contains the same Sony WH-1000XM5 model, it isn't an equivalent standalone headphone listing.

Our script will compare the brand, manufacturer part number, and color. If any of those identifiers are missing or don't match, it will flag the comparison for review instead of declaring a cheaper retailer.

This is deliberately conservative. It may flag legitimate matches when retailers use different identifiers, but it avoids treating uncertain matches as confirmed ones.

For a larger catalog, you should also verify bundle contents, pack quantities, and product condition when selecting URL pairs.

Build the Amazon and Walmart price comparison script

Now let's turn the individual scraping requests into a reusable tool.

The script will:

  1. Read Amazon and Walmart product URL pairs from a CSV file.
  2. Retrieve structured product details using Spidra.
  3. Normalize prices and product identifiers.
  4. Check whether the listings match.
  5. Calculate the price difference and identify the cheaper retailer.
  6. Export the comparison results to CSV.

We'll also retain the lowest available new Walmart offer, giving us a second useful price without mixing in refurbished or open-box items.

Create a CSV file with product URLs

Create products.csv in your project directory:

product,amazon_url,walmart_url
Sony WH-1000XM5,https://www.amazon.com/dp/B09XS7JWHH,https://www.walmart.com/ip/386006068
Sony WH-1000XM5 bundle,https://www.amazon.com/dp/B09XS7JWHH,https://www.walmart.com/ip/20182712890

The first row contains a matching product pair. The second deliberately includes the Walmart bundle so we can demonstrate how the script handles an incorrect match.

For your own catalog, add one row for each pair of listings you've identified.

Write the comparison script

Create compare_amazon_walmart.py:

# compare_amazon_walmart.py
import csv
import json
import os
import re
from decimal import Decimal, ROUND_HALF_UP
from pathlib import Path

from spidra import Spidra, SpidraError

INPUT_FILE = Path("products.csv")
OUTPUT_FILE = Path("price_comparison.csv")
CENT = Decimal("0.01")


def money(value):
    if value is None:
        return None
    return Decimal(str(value)).quantize(CENT)


def token(value):
    return re.sub(r"[^a-z0-9]", "", str(value or "").lower())


def amazon_product(data):
    specs = data.get("specifications") or {}

    return {
        "brand": data.get("brand"),
        "part": specs.get("Mfr Part Number"),
        "color": specs.get("Color"),
        "price": money(data.get("price")),
        "seller": (data.get("buyBox") or {}).get("seller"),
        "currency": data.get("currency"),
        "availability": data.get("availability"),
    }


def walmart_product(data):
    options = data.get("selectedOptions") or []

    color = next(
        (
            option.get("value")
            for option in options
            if "color" in (option.get("name") or "").lower()
        ),
        None,
    )

    return {
        "brand": data.get("brand"),
        "part": data.get("manufacturerPartNumber") or data.get("model"),
        "color": color,
        "price": money(data.get("price")),
        "seller": (data.get("seller") or {}).get("name"),
        "currency": data.get("currency"),
        "condition": data.get("condition"),
    }


def same_product(amazon, walmart):
    for field in ("brand", "part", "color"):
        if not amazon[field] or not walmart[field]:
            return False, f"Missing {field}: review manually"

        if token(amazon[field]) != token(walmart[field]):
            return False, f"{field.capitalize()} mismatch"

    if walmart["condition"] != "New":
        return False, "Walmart featured offer is not new"

    return True, "Brand, part number and color agree"


def lowest_new_offer(data):
    offers = data.get("offers") or []

    prices = [
        money(offer["price"])
        for offer in offers
        if offer.get("condition") == "New"
        and offer.get("availability") == "IN_STOCK"
        and offer.get("price") is not None
    ]

    return min(prices) if prices else None


def fetch(client, url, cache):
    if url not in cache:
        result = client.scrape(url, output="json")
        data = result.content

        if isinstance(data, str):
            data = json.loads(data)

        if not isinstance(data, dict) or data.get("type") != "product":
            raise ValueError(f"Expected product JSON for {url}")

        cache[url] = data

    return cache[url]


def compare(client, item, cache):
    row = {
        "product": item["product"],
        "match_status": "",
        "note": "",
        "amazon_price": "",
        "amazon_seller": "",
        "walmart_price": "",
        "walmart_seller": "",
        "walmart_lowest_new_price": "",
        "cheaper_retailer": "",
        "difference_usd": "",
        "pct_below_walmart": "",
    }

    try:
        amazon_raw = fetch(client, item["amazon_url"], cache)
        walmart_raw = fetch(client, item["walmart_url"], cache)
    except (SpidraError, ValueError) as error:
        row.update(match_status="error", note=str(error))
        return row

    amazon = amazon_product(amazon_raw)
    walmart = walmart_product(walmart_raw)

    row.update(
        amazon_price=str(amazon["price"]) if amazon["price"] is not None else "",
        amazon_seller=amazon["seller"] or "",
        walmart_price=str(walmart["price"]) if walmart["price"] is not None else "",
        walmart_seller=walmart["seller"] or "",
    )

    matched, reason = same_product(amazon, walmart)

    if not matched:
        row.update(match_status="review", note=reason)
        return row

    if amazon["currency"] != "USD" or walmart["currency"] != "USD":
        row.update(
            match_status="review",
            note="Prices are not both in USD",
        )
        return row

    a_price = amazon["price"]
    w_price = walmart["price"]

    if a_price is None or w_price is None or a_price <= 0 or w_price <= 0:
        row.update(
            match_status="review",
            note="One or both prices are unavailable",
        )
        return row

    lowest = lowest_new_offer(walmart_raw)

    if lowest is not None:
        row["walmart_lowest_new_price"] = str(lowest)

    difference = abs(w_price - a_price)

    if a_price < w_price:
        winner = "Amazon"
    elif w_price < a_price:
        winner = "Walmart"
    else:
        winner = "Tie"

    percentage = (
        (difference / w_price) * 100
    ).quantize(CENT, rounding=ROUND_HALF_UP)

    row.update(
        match_status="matched",
        note=reason,
        cheaper_retailer=winner,
        difference_usd=str(difference),
        pct_below_walmart=(
            str(percentage) if winner == "Amazon" else ""
        ),
    )

    return row


def main():
    client = Spidra(api_key=os.environ["SPIDRA_API_KEY"])
    cache = {}

    with INPUT_FILE.open(newline="", encoding="utf-8") as source:
        products = list(csv.DictReader(source))

    rows = [
        compare(client, product, cache)
        for product in products
    ]

    if not rows:
        print("No products found in the CSV file.")
        return

    with OUTPUT_FILE.open("w", newline="", encoding="utf-8") as dest:
        writer = csv.DictWriter(dest, fieldnames=list(rows[0]))
        writer.writeheader()
        writer.writerows(rows)

    for row in rows:
        if row["match_status"] == "matched":
            result = (
                f"{row['product']}: "
                f"Amazon ${row['amazon_price']} | "
                f"Walmart ${row['walmart_price']} | "
                f"{row['cheaper_retailer']} lower by "
                f"${row['difference_usd']}"
            )

            if row["cheaper_retailer"] == "Amazon":
                result += (
                    f" ({row['pct_below_walmart']}% vs. Walmart)"
                )

            print(result)
        else:
            print(
                f"{row['product']}: "
                f"{row['match_status']} ({row['note']})"
            )

    print(f"Saved {len(rows)} comparisons to {OUTPUT_FILE}")


if __name__ == "__main__":
    main()

The script uses Python's Decimal type for monetary calculations and caches responses so the same URL isn't scraped again within a single execution.

The same_product() function checks the product identifiers before allowing a comparison. This means a missing or different manufacturer part number produces a review flag, not an unreliable result.

The script also checks currencies before calculating differences. For simplicity, it compares USD prices only rather than performing currency conversion.

Finally, lowest_new_offer() examines Walmart's additional offers but includes only available offers explicitly marked New.

Run the comparison

Execute the script:

python compare_amazon_walmart.py

With the product data collected for this example, the comparison produces:

Sony WH-1000XM5: Amazon $228.00 | Walmart $298.00 | Amazon lower by $70.00 (23.49% vs. Walmart)
Sony WH-1000XM5 bundle: review (Part mismatch)
Saved 2 comparisons to price_comparison.csv

The first pair matches because the brand, manufacturer part number, and color agree.

The second pair is flagged because the Walmart bundle has a different manufacturer part number.

The script doesn't calculate a price difference for that pair, even though both listings contain the Sony WH-1000XM5 model name.

Export the comparison results to CSV

The script also creates price_comparison.csv, containing the product name, match status, retailer prices, sellers, lowest new Walmart offer, and calculated difference.

The main results look like this:

ProductAmazonWalmartLowest new Walmart offerDifferenceStatus
Sony WH-1000XM5$228.00$298.00$288.00$70.00Matched
Sony WH-1000XM5 bundle$228.00$398.00——Review

Because we retain the source prices alongside the comparison status, rows needing review aren't lost.

For the matching product, the CSV identifies Amazon as the cheaper retailer. It also records the alternative $288 new Walmart offer for further analysis.

This separation matters when building pricing applications. You may want to compare featured offers, the cheapest available new offer, or a particular seller's offer. Each answers a different business question.

Compare the lowest new offer instead of the featured price

So far, we've compared the featured prices returned at the top level of each product response.

On Walmart, a product can have additional offers from several sellers. The Sony headphones provide a useful example:

Walmart sellerConditionPrice
Focus CameraNew, featured offer$298.00
Video & Audio CenterNew$288.00
Beach CameraOpen Box$189.00
SecondipityRestored: Like New$159.99

If your objective is to compare the cheapest available new offer, the relevant Walmart price is $288 rather than $298.

Against Amazon's $228 featured price, that's a $60 difference.

The lowest_new_offer() function already extracts that amount. To make it the primary comparison, replace the featured Walmart price in the calculation with the lowest available new offer, falling back to the featured price when no alternative new offer exists.

For example, after retrieving walmart_raw and confirming the products match:

lowest = lowest_new_offer(walmart_raw)
w_price = lowest if lowest is not None else walmart["price"]

Use that value in the price calculation and update the exported price field to reflect the chosen offer.

For a purchasing application, you might take the comparison further by considering shipping charges, expected delivery dates, and seller preferences. The available fulfillment details depend on the listing and the customer's location.

The main lesson is to define which price you want to compare before calculating the difference.

Compare more products in one run

The script isn't restricted to Sony headphones. You can add more matched product pairs to products.csv and run it again.

Each row represents one product comparison, and the script saves all results to the same output file.

For larger catalogs, you can improve throughput with Spidra's Batch scrape API, which processes up to 50 URLs per request and returns a separate result for each page.

Instead of making one scraping request for every URL, you can submit groups of product links and associate the returned data with their original CSV rows.

The batch response also includes individual item statuses, so an unavailable product page doesn't prevent you from processing the other products.

Our guide on scraping multiple URLs in Python and handling failures covers that workflow in detail.

For a recurring catalog comparison, it's also useful to save a timestamp with every price snapshot. That allows you to distinguish a genuine price change from an older result in your database.

Turn the price comparison into a price tracker

A one-time comparison tells you which retailer has the lower listed price at the moment you collect the data.

But pricing changes. A product that's cheaper on Amazon today may be cheaper on Walmart next week.

You can extend the script into a price tracker by storing each comparison with a timestamp and comparing new results against previous snapshots.

For example, your application could detect when a Walmart offer drops below its Amazon counterpart or when the difference between the two prices exceeds a chosen threshold.

Spidra also provides Monitors for scheduled checks and notifications when tracked data changes. You can use them to watch product information without building your own scheduling and notification system.

For e-commerce applications, this makes it possible to move from manually checking a few prices to maintaining an ongoing view of product pricing.

Build your own price comparison tool

You now have a Python script that retrieves structured Amazon and Walmart product data, validates matching listings, compares prices, and exports the results to CSV.

The same approach can support catalog enrichment, competitor price analysis, and marketplace research. You can extend it with more products, additional retailers, or historical price tracking.

When collecting data from Amazon, Walmart, or other websites, check the relevant website terms and applicable rules. Our guide to web scraping legality covers the considerations.

Create a free Spidra account and run the comparison script with your own product URLs.

Frequently asked questions

Yes. You can retrieve product data from Amazon and Walmart using a scraping API, normalize the returned information, and compare the prices with Python. For accurate results, verify that the listings represent the same product and condition before calculating the difference.

Share this article

You build the product.
Spidra handles the data work.

Start with 300 free credits, about 300 pages. No credit card required.