DEV Community

Cover image for How to Scrape Walmart Mexico Prices, Stock and Sellers with Python
ReventaData
ReventaData

Posted on Originally published at apify.com

How to Scrape Walmart Mexico Prices, Stock and Sellers with Python

By the end of this tutorial, you will have a Python script that turns a walmart.com.mx keyword search into structured JSON: one row per product with the price in Mexican pesos, the was-price and discount when the site shows one, stock status, the seller (Walmart itself or a marketplace seller), rating, review count and category path.

This is for anyone tracking prices on Walmart Mexico: comparing TV prices across sellers, watching discounts, or checking which products are out of stock. We use the Apify Actor Walmart Mexico Scraper (reventadata/walmart-mexico-scraper), called from Python through the Apify API. You will learn how to configure a search, filter by seller, price and stock, run the Actor and read its rows.

⏱️ Time estimate: About 20 minutes.


Prerequisites

To follow this tutorial, you will need:

  1. Python 3.11+ installed locally (apify-client 3.x requires it).
  2. The Apify API client for Python.
  3. An Apify account and API token. Your token is on the API & Integrations page in Apify Console.

Install the client:

pip install "apify-client~=3.0"
Enter fullscreen mode Exit fullscreen mode

The code loads the token from an environment variable:

export APIFY_API_TOKEN="your_apify_token_here"
Enter fullscreen mode Exit fullscreen mode

What you'll achieve

You will run a keyword search on walmart.com.mx and get back one row per product: title, url, price in MXN, wasPrice, savingsAmount and discountPercent when a was-price is shown, availability and inStock, seller and sellerType (WALMART or MARKETPLACE), rating, reviewCount, categories, badges and the instalment text.

Note: The Actor is unofficial and not affiliated with Walmart or Walmart de Mexico. It reads search result pages and /browse/ category pages, the page types the site's robots.txt leaves open, and does not open product pages.

Example use case: Suppose you want the cheapest TVs that Walmart itself sells and has in stock. Set searchQueries: ["televisor"], sort: "PRICE_ASC", seller: "WALMART" and onlyInStock: true, and the dataset holds those rows from the pages it walks (maxPages, default 5), cheapest first.


Step 1: Access Walmart Mexico Scraper

Go to the Walmart Mexico Scraper page on Apify Store. It lists every input field and the output format.

In this tutorial we run it from Python, so all we need from the page is the Actor name: reventadata/walmart-mexico-scraper.


Step 2: Configure search parameters

The main input is searchQueries: a list of keywords in Spanish, as a shopper would type them, for example televisor or leche deslactosada. The Actor walks each keyword's result pages and saves one row per product. A result page holds 40 to 54 products.

If the site treats a keyword as a category (laptop is one), the Actor follows it to that category page. You can also give category pages directly in startUrls, as https://www.walmart.com.mx/browse/... URLs copied from the site.

Let's initialize the client and define the input:

import os
from apify_client import ApifyClient

# 1. Initialize ApifyClient with token from environment
client = ApifyClient(os.environ["APIFY_API_TOKEN"])

# 2. Define the extraction parameters
run_input = {
    "searchQueries": ["televisor"],
    "maxItems": 20,
    "maxPages": 1,
    "sort": "PRICE_ASC",
}
Enter fullscreen mode Exit fullscreen mode

This asks for the first result page of televisor, cheapest first, and stops at 20 saved products.


Step 3: Configure optional settings and filters

Every filter below runs before a row is saved, so dropped products never reach your dataset:

Parameter Default What it does
searchQueries none Keywords to search.
startUrls none Category pages (/browse/...). Search and product URLs are refused.
maxItems 100 Total products to save, counted after the filters.
maxPages 5 Result pages per keyword or category, 1 to 25.
sort BEST_MATCH BEST_MATCH, PRICE_ASC or PRICE_DESC.
minPrice, maxPrice none Price band in MXN, sent to the site and checked again on each row.
minRating none Minimum average rating, 0 to 5. Unrated products are dropped when set.
onlyDiscounted false Keep only products with a was-price above the price.
onlyInStock false Drop products the site marks out of stock.
seller ANY ANY, WALMART or MARKETPLACE.
includeSponsored false Sponsored slots are dropped unless this is on.
includeKeywords, excludeKeywords none Words the title, brand or category must or must not contain, ignoring case and accents.

💡 A few combinations that work well:

  • Cheapest first: sort: "PRICE_ASC" with a small maxItems returns the bottom of the price list.
  • Deals only: onlyDiscounted: true keeps rows with a was-price above the selling price.
  • Walmart stock only: seller: "WALMART" plus onlyInStock: true.

Step 4: Run the Actor

Now we start the run with .call(), which waits until the Actor finishes, and then read the dataset:

# 3. Start the Actor run and wait for it to complete
print("Starting Walmart Mexico extraction...")
run = client.actor("reventadata/walmart-mexico-scraper").call(run_input=run_input)
if run is None or run.status != "SUCCEEDED":
    status = run.status if run else "unknown"
    raise SystemExit(f"Run did not succeed (status: {status}).")

# 4. Fetch the resulting dataset items
dataset = client.dataset(run.default_dataset_id).list_items().items

print(f"Extraction complete! Retrieved {len(dataset)} products.\n")
Enter fullscreen mode Exit fullscreen mode

Behind the scenes:

  • The Actor fetches each result page through an Apify residential proxy in Mexico, its default. In testing on 2026-10-21, requests without a proxy and through datacenter proxies got a block page instead of results.
  • When the site answers with a block page, the Actor retries on a fresh residential exit while the run has time left.
  • It reads the product data the page embeds as JSON, applies your filters, and saves each product once, even when it appears on several pages.

Step 5: View and download results

Let's print the fields that matter for price tracking:

for item in dataset:
    print(item.get("title"))
    print(f"  Price: {item.get('price')} MXN")
    if item.get("wasPrice") is not None:
        print(f"  Was: {item['wasPrice']} MXN ({item.get('discountPercent')}% off)")
    print(f"  Seller: {item.get('seller')} ({item.get('sellerType')})")
    print(f"  In stock: {item.get('inStock')}")
    if item.get("rating") is not None:
        print(f"  Rating: {item.get('rating')} from {item.get('reviewCount')} ratings")
    print("-" * 50)
Enter fullscreen mode Exit fullscreen mode

The dataset also exports as JSON, CSV, XML, Excel, HTML table, RSS or JSONL.

Here is a sample from a capture dated 2026-10-21: page 12 of the televisor search, parsed by the Actor's own row builder (not output of the snippet above; scrapedAt, added when a row is saved, is left out):

{
    "id": "00880609633127",
    "title": "TV LG 77 Pulgadas OLED AI B5 4K Smart TV webOS 2025",
    "url": "https://www.walmart.com.mx/ip/TV-LG-77-Pulgadas-OLED-AI-B5-4K-Smart-TV-webOS-2025/00880609633127",
    "brand": null,
    "price": 29990.0,
    "currency": "MXN",
    "wasPrice": 39990.0,
    "savingsAmount": 10000.0,
    "discountPercent": 25,
    "installments": "Hasta 24 meses sin intereses",
    "availability": "IN_STOCK",
    "inStock": true,
    "seller": "Walmart",
    "sellerType": "WALMART",
    "rating": 4.9,
    "reviewCount": 6490,
    "department": "Electrónica",
    "categories": [
        "TV y Video",
        "Pantallas",
        "Todas"
    ],
    "image": "https://i5-mx.walmartimages.com/asr/a65686fa-eaa8-44bd-9670-6e9c3fe8440d.e22fb0ae34da230f03548804b8d9e2e6.jpeg",
    "badges": [
        "Rebaja"
    ],
    "variantCount": null,
    "isSponsored": false,
    "sourceQuery": "televisor",
    "sourceUrl": "https://www.walmart.com.mx/search?q=televisor&z=1&page=12",
    "page": 12
}
Enter fullscreen mode Exit fullscreen mode

Understanding your results

The key fields:

  • price: selling price in MXN as shown on the result page.
  • wasPrice, savingsAmount: the was-price and the saving, null when no was-price above the price is shown.
  • discountPercent: computed by the Actor from price and wasPrice, rounded to a whole number.
  • availability, inStock: the site's stock status and whether it is IN_STOCK. Out-of-stock products keep their price.
  • seller, sellerType: seller name, and WALMART or MARKETPLACE.
  • rating, reviewCount: both null for a product nobody has rated, so an unrated product never shows as zero stars.
  • brand: given on sponsored slots only; organic results carry null.
  • badges: promotion labels on the tile, such as Rebaja.
  • sourceQuery, sourceUrl, page: where the row came from.

What one page looks like: the 2026-10-21 capture of page 12 held 54 products, 44 organic and 10 sponsored. Of the 44 organic rows, 25 were marketplace offers and 19 sold by Walmart; 39 were in stock and 5 out of stock; 28 showed a was-price and 16 did not; 24 were rated and 20 unrated.


Pro tips

  1. Sort before you cap. maxItems stops the run once that many rows are saved. With sort: "PRICE_ASC" or "PRICE_DESC", a small cap returns the cheapest or dearest products first.
  2. Sponsored slots are off by default. The site repeats 7 to 10 sponsored slots on every page of a result. Set includeSponsored: true if you want them; every row carries isSponsored.
  3. Use the price band. minPrice and maxPrice go to the site in the search URL, so pages hold fewer off-band products, and each row is checked again.
  4. Narrow with words. includeKeywords: ["oled"] keeps only products whose title, brand or category contains "oled"; excludeKeywords drops the ones that do. Matching ignores case and accents, so television also matches Televisión.
  5. Go deeper with maxPages. The default is 5 pages per keyword. Raise it up to 25 for a fuller walk of a broad keyword; the site's own last page ends the walk earlier.

Troubleshooting

  • Run ended FAILED with no rows: the Actor fails a run that saves zero products, so an empty result never looks like success. Check the keyword spelling and loosen minRating, the price band or the seller filter.
  • A start URL was refused: startUrls takes only /browse/ category pages. A search URL stops the run with a message telling you to put the keyword in searchQueries.
  • Invalid sort or seller: both accept fixed values; any other value is refused before the run starts.
  • Prices differ from what you see signed in: rows show what an anonymous visitor with no store chosen sees, which can differ from what a signed-in shopper near a given store sees.
  • Reading the log: the last line starts Run finished: N rows pushed. and lists rejections by reason. Read it with client.log(run.id).get().

Next steps

To repeat a search, save the input as a task and add an Apify schedule, which starts an Actor or task on a cron expression. To trigger your own code when a run succeeds, add a webhook on ACTOR.RUN.SUCCEEDED: its payload carries the run, including defaultDatasetId.

For current pricing and details, see the Store page.

Links:

Which Walmart Mexico prices are you planning to track? Drop a comment! 💬

Top comments (0)