# Walmart Price Scraper (`sellerkit/walmart-price-scraper`) Actor

Walmart search, category and product prices, including the rollback items whose price the search response leaves empty.

- **URL**: https://apify.com/sellerkit/walmart-price-scraper.md
- **Developed by:** [SellerKit](https://apify.com/sellerkit) (community)
- **Categories:** E-commerce
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $10.00 / 1,000 items

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Walmart Price Scraper

Walmart search, category and product pages, with a price on **every** row. Including the rollback
items, whose price the search response leaves empty.

![What the search response gives you, and what this actor gives you](https://img.namubase.com/apify/walmart-price-scraper/01-price-field.png)

### The problem this solves

Open a Walmart search response and look at the price fields:

```json
"price": 0,
"priceInfo": { "itemPrice": "", "linePrice": "", "wasPrice": "", "minPrice": 0 }
```

All empty. On a `laptop` search we measured, that was true for **62 items out of 62**.

The real number sits in `priceInfo.priceDetails.priceLines`, and the name of the line changes with
the state of the item:

| Item state | Line that holds the price |
|---|---|
| Regular price | `CURRENT_PRICE` |
| **On rollback** | **`DISCOUNTED_PRICE`, with `COMPARISON` holding the was-price** |
| Several options | `OPTIONS` / `OPTIONS_RANGE` |
| Subscribe and save | `SUBSCRIPTION_DUAL_PRICE` |

A scraper that reads `CURRENT_PRICE` gets a number for full-price items and a blank for discounted
ones. On that same page **26 of the 62 items were on rollback** and had no `CURRENT_PRICE` line at
all. Those 26 are the rows a price tracker exists for.

Walmart's deals pages are different again. `/shop/deals` has no `priceDetails` and still uses the
old `linePrice` / `wasPrice` / `savingsAmt` strings, on all 50 items of the page.

This actor reads every one of those places, and each row says which one the number came from
(`priceSource`), so you can tell a genuinely missing price from a parser that gave up.

### What you get

![One row per item with price, was-price, discount, seller and stock](https://img.namubase.com/apify/walmart-price-scraper/02-results.png)

| Field | |
|---|---|
| `usItemId` `name` `url` `image` | identity |
| `price` `currency` | the number a shopper sees |
| `wasPrice` `discountPercent` `onSale` | the rollback |
| `optionsFrom` `optionsTo` | the range across colours, sizes and configurations |
| `subscriptionPrice` `unitPrice` | subscribe and save, and price per unit |
| `priceSource` | which field the price was read from |
| `inStock` `availability` | stock |
| `sellerName` `sellerId` | who actually ships it, which is not always Walmart |
| `rating` `reviewCount` | as shown |
| `brand` `model` `upc` | product page only |
| `sponsored` `position` `page` `query` | where the row sat, and whether it was an ad |
| `postalCode` `storeId` | the location Walmart served these prices from |
| `scrapedAt` `enriched` | when, and whether the product page was opened |

### Input

```json
{
  "queries": ["gaming laptop"],
  "maxItems": 100,
  "maxPagesPerQuery": 5,
  "sort": "best_match",
  "enrichFromProductPage": false,
  "onSaleOnly": false
}
```

You can also pass `categoryUrls` (any browse or deals page) or `productUrls`
(`20305508298`, `/ip/...`, or the full URL). Paste a product URL into the category box and it is
still read as a product, and the other way round.

Full URLs keep the parts that decide what you see. `selectedSellerId`, `classType` and
`conditionGroupCode` change which offer Walmart shows, so they are preserved; ad tracking
parameters are dropped.

`onSaleOnly`, `inStockOnly`, `skipSponsored` and `requirePrice` filter before anything is written,
so the rows you did not want are never saved and never charged.

### Brand, model and UPC

![The product page adds brand, model and UPC to each row](https://img.namubase.com/apify/walmart-price-scraper/03-product-page.png)

Set `enrichFromProductPage` to open each item's own page. It adds `brand`, `model`, `upc` and the
seller level stock status, and confirms the price on the product page itself. This is a second
event, charged only for the rows where it worked.

### Pricing

Charged **per row that reaches the dataset**, not per minute of compute. A run that finds nothing
costs nothing, and a row you filtered out was never saved, so it was never charged. Turn on
`requirePrice` and a row without a price is not saved either.

### Notes

- **Walmart's block reads the client, not the address.** Measured on one connection at the same
  minute: `curl` got a 15 KB page titled "Robot or human?", **headless** Chromium got the same page,
  and **headed** Chromium got the real 1.4 MB page. So this actor drives a real browser with a
  display, and reuses one session for every page rather than opening a new one each time.
- **Leave the proxy off** unless you are bringing residential addresses. On a direct connection this
  actor returned 30 rows out of 30 with a price. Through datacenter proxy addresses every request
  was blocked before the first page, because those addresses are already burned for this site.
- The block arrives as `200 OK`, not as an error code. This actor recognises that page, retries on a
  fresh session, and fails loudly rather than writing an empty dataset.
- One item that fails to load does not stop the run. It is recorded in `RUN_SUMMARY` with the
  reason, and the rest still come back. `RUN_SUMMARY` also counts how many rows carried a price.
  Watch that rather than the row count.
- Sponsored placements repeat across pages and sit at pinned positions. Set `skipSponsored` when you
  are comparing prices rather than measuring the search page itself.
- **Prices come from one location, and the run tells you which one.** `RUN_SUMMARY.location` and the
  `storeId` on each row record where Walmart served the run from. This matters more than it sounds:
  two runs a minute apart came back from **store 3081** and **store 2038 (Ashburn, 20149)**, because
  the location follows the address the run went out from. Comparing yesterday's prices with today's
  is only meaningful when this field matches.
- Choosing that location is not offered, and it is worth saying why rather than leaving you to
  guess. Walmart keeps it in the session, not in the URL: `?stores=` and `?storeId=` are ignored,
  injecting the store cookie gets the session blocked, and the request that actually sets it
  (`setFulfillmentIntent`) answers with `412` and a CAPTCHA payload. Getting past that means
  defeating the bot check, which this actor will not do.

# Actor input Schema

## `queries` (type: `array`):

Walmart search terms, for example "gaming laptop". Each term is paged until Max items is reached.

## `categoryUrls` (type: `array`):

Any Walmart listing page, for example https://www.walmart.com/browse/electronics/laptops/3944\_1089430\_3951 or a deals page. Product URLs pasted here are handled as products, so it does not matter which box you use.

## `productUrls` (type: `array`):

Specific products, for example 20305508298, /ip/... or the full URL. Query parts that decide which offer you see (selectedSellerId, classType, conditionGroupCode) are kept; ad tracking parts are dropped. These always read the product page, so they carry brand, model and UPC.

## `maxItems` (type: `integer`):

Stop after this many unique items. This is also the cap on what you can be charged.

## `maxPagesPerQuery` (type: `integer`):

Walmart serves about 60 items per page.

## `sort` (type: `string`):

Result order.

## `enrichFromProductPage` (type: `boolean`):

Adds brand, model, UPC and the seller-level stock status, and confirms the price on the product page itself. Slower, and charged as a separate event.

## `onSaleOnly` (type: `boolean`):

Keep only items whose price is below the was-price. Filtered rows are never saved, so they are never charged.

## `inStockOnly` (type: `boolean`):

Keep only items Walmart currently shows as in stock. Filtered rows are never saved, so they are never charged.

## `skipSponsored` (type: `boolean`):

Sponsored rows repeat across pages and distort price comparisons.

## `proxyConfiguration` (type: `object`):

Leave this off unless you have residential proxies. Walmart's block reads the client, not the address, so this actor runs a real browser and works on a direct connection; datacenter proxy addresses are already burned and get blocked immediately.

## `requirePrice` (type: `boolean`):

Walmart leaves a handful of items with no price at all, usually out of stock ones. Turn this on to skip them. Skipped rows are never saved, so they are never charged.

## Actor input object example

```json
{
  "queries": [
    "gaming laptop"
  ],
  "maxItems": 100,
  "maxPagesPerQuery": 5,
  "sort": "best_match",
  "enrichFromProductPage": false,
  "onSaleOnly": false,
  "inStockOnly": false,
  "skipSponsored": false,
  "proxyConfiguration": {
    "useApifyProxy": false
  },
  "requirePrice": false
}
```

# Actor output Schema

## `items` (type: `string`):

One row per item: price, was-price, discount, options range, seller, stock, rating and position.

## `itemsCsv` (type: `string`):

The same rows as a spreadsheet.

## `runSummary` (type: `string`):

How many rows came back, how many carried a price, how many requests Walmart blocked, and every item that could not be read. Watch this rather than the row count.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "queries": [
        "gaming laptop"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("sellerkit/walmart-price-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "queries": ["gaming laptop"] }

# Run the Actor and wait for it to finish
run = client.actor("sellerkit/walmart-price-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "queries": [
    "gaming laptop"
  ]
}' |
apify call sellerkit/walmart-price-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,sellerkit/walmart-price-scraper"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/yJnm818NrIvvShhwi/builds/rvUpihiS4RuRG1IIf/openapi.json
