# Zappos Scraper - Shoe & Apparel Price Tracker (USA) (`superslowsloth/zappos-products`) Actor

Zappos scraper: search terms to one row per shoe or apparel colourway with USD price, list price, markdown dollars and percent, on-sale flag, brand, colour, gender, rating and reviews, units on hand, link and photo. For shoe and apparel price and markdown watching.

- **URL**: https://apify.com/superslowsloth/zappos-products.md
- **Developed by:** [Superslow Sloth](https://apify.com/superslowsloth) (community)
- **Categories:** E-commerce, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.50 / 1,000 product scrapeds

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?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Zappos scraper: shoe and apparel price and markdown tracker (USA)

This Zappos scraper searches [Zappos](https://www.zappos.com) (the US shoe and
apparel retailer) by keyword and returns one flat row per product colourway:
the current price in **USD**, the list price, the markdown in dollars and
percent, brand, colour, who it is for, rating and review count, units on hand,
badges, the product link and the photo. Run it on a schedule and diff the
dataset to get a **shoe and apparel price and markdown watch**: which Nike,
ASICS or Columbia colourways were reduced since last week, and by how much.

No account, no API key, no login, no browser. One request returns 100 products.

### Input

| Field | Notes |
|---|---|
| `searchTerms` | One or more search terms, one per line (`sneakers`, `nike air max`, `rain jacket`). Each term is read separately, in the site's own relevance order. A colourway found by two terms is returned once, under the first. A term with no letter or digit in it (Japanese text, `!`, `#`) is not searched by Zappos, which answers with its whole catalogue of 50,000 products; the actor skips such a term at no charge. |
| `maxItems` | Products to read per term (default 100, up to 10,000). 100 per page. |
| `onlyOnSale` | Return only products whose price is below their list price (`on_sale`). Products that are not marked down are read, not returned and not charged, and do not count toward `maxItems`. A term with few markdowns reads many pages to find them. |
| `proxyConfiguration` | Apify datacenter proxy by default. |

Example input:

```json
{
  "searchTerms": ["sneakers", "rain jacket"],
  "maxItems": 300,
  "onlyOnSale": true
}
```

### Output

Prices are numbers in US dollars; `currency` is `USD` (every page measured).

| Field | Notes |
|---|---|
| `query`, `position` | The search term that first found the colourway, and its place in Zappos's relevance order for that term (1 is first, counted over every product of the pages read). |
| `product_id`, `style_id` | Zappos's product id and colourway id, both strings. **One row is one colourway**: the same `product_id` comes once per colour, each with its own `style_id`, price and link, so `style_id` is the unique key. |
| `name`, `brand`, `color` | As the page shows them, HTML entities decoded (`PFG Tidal™ Dress`). `brand` and `color` are null if the page sends none. |
| `url` | The product link for this colour. |
| `product_type`, `genders` | The site's product type (`Shoes`, `Clothing`, `Accessories`, `Bags`, `Jewelry`...) and who it is for, any of `Men`, `Women`, `Boys`, `Girls` (empty list if none). |
| `price` | The current price. |
| `original_price` | The list price before any reduction. Zappos sends it for every colourway, equal to `price` when nothing is marked down. |
| `on_sale` | True when `price` is below `original_price`. This includes the small 1-9% markdowns: Zappos's own on-sale flag starts at 10% off. Of 2,083 colourways read on 2026-10-18, 625 were flagged by the site and 52 more had a lower price without the flag. |
| `discount_amount`, `percent_off` | Dollars off and the site's whole-number percent off. **Null when not on sale**, never 0. |
| `rating`, `review_count` | The average star rating (1 to 5) and how many reviews it is from. **`rating` is null for a colourway with no reviews** (the page sends 0, which is not a rating of 0); `review_count` is 0 then. |
| `units_on_hand` | The site's `onHand` count as sent. Zappos does not say what it counts; it reads as units in stock across sizes. Null if absent. |
| `is_new`, `badges` | Whether the page flags the colourway as new, and its badge codes as sent (`NEW`, `BS`, `NWC`, `OZ` were seen; the site does not define them). |
| `image_url` | The main photo, a JPEG on `m.media-amazon.com`. |
| `currency` | `USD`. |

Example row (real values, 2026-10-18):

```json
{
  "query": "sneakers",
  "position": 22,
  "product_id": "10054363",
  "style_id": "6652718",
  "name": "Jumper",
  "brand": "Madden Girl",
  "url": "https://www.zappos.com/p/madden-girl-jumper-sand-multi/product/10054363/color/6563",
  "image_url": "https://m.media-amazon.com/images/I/61mf74cYFwL.jpg",
  "product_type": "Shoes",
  "genders": ["Women"],
  "color": "Multi",
  "currency": "USD",
  "price": 47.96,
  "original_price": 55.0,
  "on_sale": true,
  "discount_amount": 7.04,
  "percent_off": 13,
  "rating": 4.3,
  "review_count": 6,
  "units_on_hand": 122,
  "is_new": false,
  "badges": []
}
```

#### Honest nulls

A field the page did not send is `null`, never `0` or `""`. `genders` and
`badges` are empty lists when there are none.

- **No sizes or per-size stock.** The search list does not carry them;
  `units_on_hand` is one total.
- **No sponsored flag and no sort.** Zappos's search page has none in its data;
  products come in its relevance order (see below for why no sort is offered).
- **No description or variants.** This is the search list, one row per colour.

### Pricing

Pay per event:

- **$0.002** per run (`actor-start`), charged only after your input is
  validated, so a run that fails on bad input costs nothing.
- **$0.0035** per product row written (`product-scraped`), i.e. $3.50 per
  1,000 products. A row that is not written is not charged: rows filtered by
  `onlyOnSale`, and repeats of a colourway another term already returned, are free.

### How it fetches

It reads the product list that zappos.com's own search page embeds in its page
state (`window.__INITIAL_STATE__`). Measured 2026-10-18: HTTP 200 HTML (0.4 to
2.4 MB a page) from a plain home connection and from Apify datacenter addresses
(20 of 20 pages on each of two days), no key or cookie needed. A refusal (403,
429, 5xx), a 200 that carries no page data, a page other than the one asked for,
or a dropped connection is treated as the address being blocked, and the actor
rotates to a fresh one and retries.

Things worth knowing, all measured:

- **Paging uses `page=`, 1-based, and is always sent.** The `p=` parameter is
  ignored on Zappos's search: `p=0`, `1`, `2`, `90`, `200` all return the first
  100 products. `page=2` returns the next 100. A brand term such as `nike` or
  `UGG` is redirected to the brand's own list when no `page` is sent; with
  `page=1` or higher it stays an ordinary search, in one order across all pages.
  The page says which page it rendered, and the actor treats a mismatch as a
  refusal rather than return the first page again and charge for it.
- **No sort.** Zappos's `s=` sort parameter is ignored on the search page (price,
  percent off, newest all returned the default order), so none is offered. Sort
  the dataset by `percent_off` or `price` instead, or set `onlyOnSale`.
- **Depth.** 100 products per page and no page-size setting. A page past the
  last is HTTP 200 with no products, and the actor stops there. The deepest
  term measured has 492 pages (`the`, 49,105 matches); at most 10,000 products
  per term are read.
- **Totals drift.** The same search reported 9,013, 9,015 and 9,016 matches
  within minutes.
- **Terms are normalised by the site.** Case does not matter, a plural is
  stemmed (`sneaker` and `SNEAKERS` read the same 9,013) and punctuation inside a
  term is dropped (`a/b` searches `a`). A term with no match returns nothing and
  costs nothing. A term containing `<` or `>` is refused by Zappos's firewall.

If Zappos changes its page data, the run fails with a message instead of
returning empty rows.

# Actor input Schema

## `searchTerms` (type: `array`):

What you would type into the Zappos search box, one term per line (for example sneakers, nike air max, rain jacket or dress). Each term is read separately, 100 products per page. A term with no letter or digit in it (for example Japanese text or punctuation) is not searched by Zappos, which answers with its whole catalogue; the actor skips such a term at no charge. A product colour that two terms both find is returned once, under the first.

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

Read at most this many products for each search term. Zappos lists 100 per page, so 250 reads three pages. Up to 10,000 are accepted.

## `onlyOnSale` (type: `boolean`):

Return only products whose current price is below their list price (the on_sale field, which includes small markdowns under 10% that the site itself does not flag). Products that are not marked down are read but not returned and not charged, and do not count toward Max products. A term with few markdowns reads many pages to find them.

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

Use Apify's automatic datacenter proxy (the default). zappos.com answered Apify datacenter addresses with HTTP 200 when this actor was built (20 of 20 pages on two days); the residential group was not needed. A refused request is retried from a fresh address.

## Actor input object example

```json
{
  "searchTerms": [
    "sneakers"
  ],
  "maxItems": 100,
  "onlyOnSale": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

No description

# 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 = {
    "searchTerms": [
        "sneakers"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("superslowsloth/zappos-products").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 = {
    "searchTerms": ["sneakers"],
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("superslowsloth/zappos-products").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 '{
  "searchTerms": [
    "sneakers"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call superslowsloth/zappos-products --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,superslowsloth/zappos-products"
        }
    }
}
```

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/ekfG6LtKbdUaSs7D1/builds/zJdg7nZdSWpHdWaJs/openapi.json
