Skip to content

ChocoData-com/walmart-product-scraper

Repository files navigation

Walmart Product Scraper

Walmart Product Scraper

Walmart Product Scraper for extracting product prices, titles, specifications, ratings, images and seller data from Walmart.com. This repo has a free Walmart product script you can run right now, and a Walmart product data API that returns real structured JSON.

Last updated: 2026-07-20. Working against Walmart.com as of July 2026, and re-verified whenever Walmart changes their markup.

The uncut responses behind every block on this page are committed in walmart_product_api_data/, captured 2026-07-20, and each block states what was trimmed from it. Every code example calls the API and is runnable from walmart_product_api_codes/.

This repo covers one endpoint in depth. For Walmart search listings, reviews, bestsellers and category filters, see the Walmart Scraper.

pip install requests
export CHOCODATA_API_KEY="your_key"     # free: 1,000 requests, one-time, no card
python walmart_product_api_codes/product.py 19075520026

Those three lines return this, live from Walmart.com (general.images and specifications shown as counts, and description truncated where marked, to keep the lead readable; the full sample is uncut):

{
  "general": {
    "title": "Lenovo IdeaPad Slim 3x 15.3\" Laptop - AI Snapdragon X, 16GB RAM, 512GB SSD, Luna Grey",
    "brand": "Lenovo",
    "description": "Elevate your computing experience with the Lenovo IdeaPad Slim 3x...",
    "images": "[ 18 urls ]",
    "meta": { "gtin": "199274646943", "sku": "19075520026" }
  },
  "price": { "currency": "USD", "price": 449 },
  "rating": { "rating": 4.5, "count": 222 },
  "seller": { "name": "Walmart.com" },
  "specifications": "[ 36 key/value pairs ]"
}

...multiplied out to 10 top-level fields, 36 specifications and 18 image URLs:

Retrieved Walmart product data

That is the whole point of this repo. The rest of this page is the free script, the wall it hits, and the endpoint with its parameters and response.


Contents


Free Walmart Product Scraper

Walmart server-renders its item pages into a __NEXT_DATA__ JSON blob, and the full product rides along in it, so when a request gets through you can extract the title, price, specifications and images without a headless browser or JavaScript rendering. No key, no cost:

python free_scraper/walmart_product_free_scraper.py 19075520026

Source: free_scraper/walmart_product_free_scraper.py. It fetches /ip/{id}, finds the __NEXT_DATA__ blob, walks to props.pageProps.initialData.data.product, and emits title, brand, price, currency, rating, review_count, images, model.

After running the command, your terminal should look something like this:

Free Walmart product scraper blocked by the bot check

That is the outcome, and the next section is about why.

Avoid getting blocked when scraping Walmart products

We ran exactly that script against Walmart.com while writing this README, 6 times, on 6 different item ids. All 6 returned the same thing, and every run is recorded in free_scraper_survey.json:

NO DATA: the response carried no product node.
A 200 does not mean success. Check for the interstitial before parsing.

Here is the raw evidence behind that message:

What we measured Value
HTTP status 200 (not 403, not 429)
Response size 15,195 bytes (the same page through the API is 406,096 bytes)
<title> Robot or human?
__NEXT_DATA__ blob absent
Product parsed 0 fields on 6 of 6 item ids
Swapping in a browser User-Agent changed nothing: same 200, same 15,195 bytes, same title
walmart.com/ from the same client 200, 256,504 bytes
robots.txt, User-agent: * no Disallow covering /ip/

Free scraper blocked vs Chocodata API parsed

A 15 KB page titled "Robot or human?" returned with a 200 status. That single row is the whole problem in miniature, and it is the most important thing to understand about scraping Walmart product pages:

What bites you Why What it costs you
HTTP 200 is not success Walmart serves its bot check with a 200 status, not a 403. A naive scraper parses it, finds nothing, and logs "0 fields" rather than "blocked". The expensive failure is not a crash. It is weeks of empty rows that looked like "this product has no data".
Complying does not get you in Walmart's robots.txt carries no Disallow for /ip/, so the product page is robots-allowed. It is refused anyway, and a browser User-Agent did not change the result. Reading robots.txt and obeying it does not get you the product. The block happens before your parser runs.
The same client gets served elsewhere walmart.com/ returns a normal 256 KB page to the same client, same IP, same headers, seconds later. Only /ip/ came back as the interstitial. Your smoke test against the homepage passes and tells you nothing about the path you actually need.
Where you run it is part of the input Every measurement on this page was taken from a residential connection. Cloud egress ranges are published and widely filtered, so a result from your laptop does not carry over to CI unchanged. Whatever you measure locally has to be re-measured where the job will actually run.
out_of_stock is not deterministic On 12 identical back-to-back calls to one item, price, rating and specifications were identical every time; out_of_stock came back True on one of the 12 (see availability_survey.json). Treat availability as a signal, not a guarantee. Track price on its own field, and poll a few times before trusting a stock flag.
The JSON path moves The __NEXT_DATA__ shape changes a few times a year. Your parser silently returns nothing. Ongoing maintenance, plus alerting smart enough to tell "empty" from "broken".

Price is stable across calls; availability is not

Worth knowing before you go shopping for a free alternative: a plain HTTP client fetching /ip/ is the shape of request that got the interstitial here, and it is the shape most free Walmart scrapers use, so check a repo's own recent output rather than its README. Several of the popular ones that look like they work are wrappers that require a paid vendor's API key, which is worth checking for before you clone.


Using the Chocodata Walmart Product Scraper API

The managed option, and the one this repo is built around. The Chocodata Walmart Product Scraper API returns the full Walmart product as parsed JSON for Walmart product data extraction at scale, with a ~99% success rate against the bot check and no proxy management. Free for the first 1,000 requests.


Walmart Product Scraper API reference

Below is the Walmart Product Scraper API reference to get you started: authentication, the parameters, errors and rate limits, then the endpoint with its response.

Quickstart

curl "https://api.chocodata.com/api/v1/walmart/product?api_key=YOUR_KEY&id=19075520026"
import requests

r = requests.get(
    "https://api.chocodata.com/api/v1/walmart/product",
    params={"api_key": "YOUR_KEY", "id": "19075520026"},
    timeout=90,
)
d = r.json()
print(d["general"]["title"], d["price"]["price"], d["price"]["currency"])
# Lenovo IdeaPad Slim 3x 15.3" Laptop - AI Snapdragon X, 16GB RAM, 512GB SSD, Luna Grey 449 USD

After running the command, your terminal should look something like this:

Running the Walmart Product Scraper API

Get a key at chocodata.com (1,000 requests, one-time, no card).

Authentication

Pass api_key as a query parameter on every request. That is the whole auth model.

Parameters

Param Type Required Default Description
api_key string yes - Your Chocodata API key.
id string (digits) one of id/url - Walmart us_item_id, e.g. 19075520026. /ip/{id} is built from it.
url string (URL) one of id/url - Full Walmart item URL. Takes precedence over id.
country string (ISO-2) no us Egress location. Walmart localises its pages to the location it sees, so pin this for reproducible results.
add_html boolean no false Also return the raw upstream HTML alongside the parsed JSON (debugging).

Each request costs 5 credits (= 1 request). Responses are billed only on success (2xx).

There is no page and no sort parameter on this surface: one call returns one item. Unknown parameters are ignored rather than rejected, so page=2 returns HTTP 200 with the same item.

id and url are two ways to name the same item and they agree: on 2026-07-20 both forms returned identical payloads for 19075520026, field for field.

Errors

Nothing below is billed: you are only charged on a 2xx.

Status error code Meaning Billed What to do
400 invalid_params A required param is missing or the wrong type. Body lists the exact issue and path. no Fix the query string.
401 INVALID_API_KEY Key missing, unrecognised, or revoked. no Check api_key. Get one at chocodata.com.
402 INSUFFICIENT_CREDITS Balance exhausted. no Top up or upgrade at chocodata.com.
404 item_not_found The target returned 404: the us_item_id does not exist. retryable: false. no Fix the id. Retrying will not help.
429 RATE_LIMITED Over the rate limit or your plan's concurrency. no Back off and retry; see Rate limits.
502 target_unreachable Walmart refused every attempt for this request. retryable: true. no Retry once after a few seconds. See the note below.

Two response shapes exist: auth and billing errors nest under error.code (uppercase), while scrape-layer errors are flat with a lowercase error string plus retryable.

The scripts in this repo map each status onto an actionable message, so a typo'd key does not hand you a stack trace:

Walmart Product Scraper API error handling

A bad key, verbatim:

curl "https://api.chocodata.com/api/v1/walmart/product?api_key=totally_invalid_key_123&id=19075520026"
{"error":{"code":"INVALID_API_KEY","message":"Api key not recognised."}}

Calling it with neither id nor url, verbatim:

curl "https://api.chocodata.com/api/v1/walmart/product?api_key=YOUR_KEY"
{"error":"invalid_params","issues":[{"code":"custom","message":"either url or id is required","path":[]}]}

walmart.product is a flaky surface rather than an unreliable one. On 2026-07-20 a single item id returned 502 target_unreachable on 1 of 20 back-to-back single calls and 200 on the other 19. The 502 is retryable: true, it is not billed, and the same id succeeds on the next attempt, so the scripts here retry once after an 8-second pause before giving up. A 404 item_not_found is different: it is retryable: false, it means the id does not exist, and retrying will not change it.

Rate limits and concurrency

Two limits apply at once, and the one that binds first is usually the rate limit, not the concurrency cap.

Rate limit: 120 requests per 60 seconds, per key (sliding window). That is a hard ceiling of 2 requests/second sustained, whatever your plan.

Concurrency: how many requests you may have in flight at once, which varies by plan:

Plan Concurrent requests
Free 10
Vibe 30
Pro 50
Custom 100 to 500+

Exceed either and you get 429, not a queue. Every request is a synchronous GET: there is no webhook, callback, or async job to poll. The examples use timeout=90 because a request that runs into the bot check and is re-attempted takes longer than the median suggests.

Size against the rate limit rather than the concurrency figure. One call covers one item, so a 500-product catalogue is 500 calls, or roughly 4.2 minutes at 120/minute, and a daily sweep of 5,000 products is about 42 minutes. Keep your pool at or under your plan's concurrency and pace it to the rate limit:

from concurrent.futures import ThreadPoolExecutor
import requests

def one(item_id):
    r = requests.get("https://api.chocodata.com/api/v1/walmart/product",
                     params={"api_key": KEY, "id": item_id}, timeout=90)
    d = r.json() if r.ok else {}
    return item_id, d.get("price", {}).get("price"), d.get("rating", {}).get("count")

items = ["19075520026", "13626250252", "10226953067", "5250574665"]
with ThreadPoolExecutor(max_workers=10) as pool:   # <= your plan's concurrency
    for item_id, price, reviews in pool.map(one, items):
        print(item_id, price, reviews)

Product: title, price, specifications, ratings and images

Full detail for a single Walmart item: the product object Walmart renders on the item page, flattened into nested leaf names (general, price, rating, seller, fulfillment, breadcrumbs, specifications) plus the canonical us_item_id, product_id and model.

Param Type Required Default Description
id string (digits) one of id/url - Walmart us_item_id.
url string (URL) one of id/url - Full Walmart item URL.
curl "https://api.chocodata.com/api/v1/walmart/product?api_key=YOUR_KEY&id=19075520026"

Real response. general.images cut to 1 of 18 and specifications cut to 3 of 36 (both noted inline); every other field is complete and verbatim, description truncated where marked (full sample):

{
  "general": {
    "title": "Lenovo IdeaPad Slim 3x 15.3\" Laptop - AI Snapdragon X, 16GB RAM, 512GB SSD, Luna Grey",
    "brand": "Lenovo",
    "url": "https://www.walmart.com/ip/IdeaPad-Slim-3x-15-Laptop-Snapdragon-X-X1-26-100-16GB-RAM-512GB-SSD-Luna-Grey/19075520026",
    "description": "Elevate your computing experience with the Lenovo IdeaPad Slim 3x. This 15.3-inch laptop provides crisp visuals...",
    "main_image": "https://i5.walmartimages.com/seo/IdeaPad-Slim-3x-15-Laptop-Snapdragon-X-X1-26-100-16GB-RAM-512GB-SSD-Luna-Grey_c2916a8d-bbad-4bad-a8a9-3b185447a088.8043aee12ac65bae952d1723d1df243d.jpeg",
    "images": ["... 18 image urls ..."],
    "meta": { "gtin": "199274646943", "sku": "19075520026" }
  },
  "price": { "currency": "USD", "price": 449 },
  "rating": { "rating": 4.5, "count": 222 },
  "seller": { "id": "F55CDC31AB754BB68FE0B39041159D63", "name": "Walmart.com", "official_name": "Walmart.com" },
  "fulfillment": { "out_of_stock": false, "free_shipping": false },
  "breadcrumbs": [
    { "category_name": "Electronics", "url": "/cp/electronics/3944" },
    { "category_name": "... 5 more ...", "url": null }
  ],
  "specifications": [
    { "key": "Brand", "value": "Lenovo" },
    { "key": "Proc. brand", "value": "Qualcomm (Snapdragon)" },
    { "key": "Processor", "value": "Snapdragon X X1-26-100" },
    { "key": "... 33 more ...", "value": "..." }
  ],
  "us_item_id": "19075520026",
  "product_id": "24H72HS7PA9J",
  "model": "83N3006BUS"
}

general.meta.gtin is the field most people come for: it is the barcode (UPC) that lets you join Walmart rows to Amazon catalogues or a supplier feed. It was present on all 6 items in product_survey.json. Two more things worth coding against, both visible in the committed samples:

  • seller.name tells 1P from 3P. On first-party items it is Walmart.com; on marketplace items it is the third-party seller, e.g. Hamilton Beach Brands Inc or Yiwu Yingli Electronic Commerce Co., Ltd. in the survey. The seller.id is stable across items sold by the same seller.
  • specifications length is item-specific. Across the six sampled items it ran from 9 to 41 pairs, and Walmart occasionally repeats a key (the laptop returns Resolution twice with different values). Treat it as a list of pairs, not a dict, or you will drop the duplicate.

A note on url vs id: they resolve to the same item. When you pass a non-Walmart URL the endpoint fetches it but fails cleanly rather than guessing: url=https://example.com returns 502 extraction_failed (no product node), not a fake 200. The control run is committed.

Runnable: walmart_product_api_codes/product.py


Monitor Walmart price and stock changes

Watching a product's price and availability move is the main reason people scrape Walmart product pages, so that use case is in the repo end to end rather than as a snippet. price_monitor.py polls a list of items, stores every observation as a local dataset in SQLite, and prints what moved since the last run:

python walmart_product_api_codes/price_monitor.py 19075520026 13626250252 10226953067

Polling three Walmart items and recording price and stock

That is a real first run: nothing to compare against yet, so it seeds the database and says so. Run it again after prices move and each change prints as a diff line (CHG 13626250252 ... | price 238 -> 219 (-19.00) | +14 review(s)). Track price on its own field rather than inferring it from the stock flag, which (as measured above) is not deterministic per call.

Export the history with one command:

sqlite3 -header -csv walmart_products.db "select * from observations;" > out.csv

One API call per item per run. 1,000 free requests covers a month of daily checks on ~30 products.


Measured latency

Real end-to-end wall-clock, measured from a laptop against the live API on 2026-07-20. This includes the upstream fetch, the anti-bot handling, and the parse:

Endpoint Median Range n
/walmart/product 3.9s 3.0 to 5.4s 14

Read the range, not just the median. A request that runs into Walmart's bot check is re-attempted upstream until it comes back with real data, which is where the tail comes from, and absorbing that silently is the thing you are actually buying. The free script in this repo hits the same wall and simply stops. Budget a retry rather than a failure for the occasional 502, which is retryable and not billed. Small sample (n=14); reproduce it yourself with the scripts here.


License

MIT. See LICENSE.