# Zara Product Scraper & Price Monitor (`luminar/zara-product-scraper-price-monitor`) Actor

Search Zara by keyword, paste a category or sale URL, or paste product URLs. Get typed rows with price, discount, stock, colours, sizes and a working link, or track price and stock changes between runs.

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

## Pricing

from $0.80 / 1,000 zara product rows

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

Search Zara by keyword, paste a category or sale URL, or paste product URLs — and get clean, typed rows with price, discount, stock, colours, sizes and a **working product link**. Turn on change tracking and the same run becomes a price and stock monitor.

Every row is either a real value or `null`. No `"-"` placeholders, no unresolved image templates, no colour counts standing in for colours.

### Start in 60 seconds

Paste one keyword, press Start. That is the whole first run.

```json
{
  "searchQueries": ["blazer"],
  "country": "us",
  "language": "en",
  "section": "WOMAN",
  "maxProducts": 200
}
```

That run returns about 90–130 blazers with prices, stock, colours and a clickable link to each product, and tells you plainly whether it reached everything.

![Zara product rows with typed prices, resolved links and explicit coverage](https://api.apify.com/v2/key-value-stores/DJgPR6wwS2tIaLDQW/records/zara_product_price_monitor--27ecb9682b5e4628-product-dataset.png)

### What you get

| Field | What it is |
|---|---|
| `name`, `description` | Product name and description |
| `productUrl` | **Canonical Zara product page URL** — clickable, joinable |
| `price`, `originalPrice`, `discountPercent` | Numbers in the storefront currency. `originalPrice` is `null` when the item is not on sale — never a dash |
| `currency` | ISO currency code of that storefront |
| `availability` | `in_stock`, `low_on_stock`, `coming_soon` or `out_of_stock` |
| `colors[]` | Every colourway with name, **hex code**, price and availability |
| `sizes[]` | Per-size availability, price and SKU *(with detail enabled)* |
| `composition[]` | Fabric composition by garment part *(with detail enabled)* |
| `imageUrl`, `imageUrls[]` | **Resolved** image URLs that actually open |
| `styleId`, `colorId`, `displayReference` | Stable Zara identifiers |
| `section`, `familyName`, `subfamilyName` | Zara's own taxonomy |
| `country`, `language`, `scrapedAt` | Where and when the row came from |

Change rows add `changeType` (`NEW` / `UPDATED` / `ENDED`), `changedFields`, `previousPrice`, `currentPrice` and `priceDelta`.

### Input: three ways in

**Keyword** — `searchQueries: ["blazer"]`
Uses Zara's own product search, so results genuinely match the keyword.

**Category or sale page** — `categoryUrls: ["https://www.zara.com/us/en/woman-new-in-l1180.html"]`
Any Zara category, New In or sale page. Paged to the end unless you set a lower limit.

**Exact products** — `productUrls: [...]`
Scrape or monitor specific items. Product URLs always return full detail including per-size availability.

You can combine all three in one run. Duplicates reached through several targets are merged, not billed twice.

Prices are per storefront: `country` and `language` select the catalogue and the currency.

### Price and stock monitoring

Set `trackChanges: true` and schedule the run.

- The **first run** stores a baseline and returns products only — no flood of fake "new" rows.
- **Later runs** return the products plus `NEW`, `UPDATED` and `ENDED` change rows with `previousPrice` and `priceDelta`.
- A product must be **missing twice in a row** before it is reported as `ENDED`, because one absence is not proof of removal.
- If a run is blocked, capped or partial, **the baseline is not updated** and nothing is reported as ended. A broken run can never corrupt your history.

### Coverage — you always know what you got

Every run returns a coverage row and a run summary saying exactly what happened:

| Status | Meaning |
|---|---|
| `COMPLETE` | Every target was walked to its last page |
| `FILTERED` | Scope completed; your filters removed some products |
| `CAPPED` | A limit you set was reached first |
| `PARTIAL` | Some targets completed, others did not |
| `EMPTY_CONFIRMED` | The source loaded and confirmed nothing matches |
| `BLOCKED` / `FAILED` | The source did not answer reliably — reported as such, **never** disguised as an empty result |

#### A keyword with no matches returns nothing — and costs nothing

Zara answers a keyword it cannot match with a *"you might also like"* suggestion set: unrelated products, HTTP 200, no error. Selling those to you as search results is a real behaviour in this category.

This Actor detects that case and returns `EMPTY_CONFIRMED` with **zero rows and zero row charges**. You are never billed for products you did not ask for.

### Pricing

| Event | Price | When |
|---|---|---|
| Verified target | $0.0058 | Once per keyword, category or product URL that returned a trustworthy result. Blocked or failed targets are **free** |
| Product row | **$0.0009** | One unique product or verified change row |
| Product row with sizes and composition | $0.0015 | Replaces the product row price when `fetchDetails` is on |
| Monitoring target | $0.0068 | Once per target on a change-tracking run that produced a comparable scan |

**200 products = $0.1858.** No start fee.

`maxProducts` and `maxBuyerChargeUsd` cap the run before it begins: if the worst case would exceed your limit, the run refuses to start rather than surprising you.

Untrustworthy output is always free. Blocked targets, failed targets and suppressed non-matching search results are never charged.

### Automation and exports

- **Schedule it.** Set `trackChanges: true` and run it daily or weekly; the change feed is the point of a scheduled run.
- **Export it.** Dataset rows download as JSON, CSV, Excel, XML or HTML from the Storage tab, or through the Apify API.
- **Wire it up.** Apify integrations (webhooks, Slack, email, Make, Zapier) fire on run completion, so a price drop can reach you without polling.
- **Views.** The dataset ships Overview, Products, Changes and Coverage column views; each changes the columns, not which rows exist, and `recordType` always stays visible.

### FAQ and support

**Why does a row show `null` for the was-price?**
Because the product is not on sale. A dash or an empty string would break spreadsheet and BI typing, so the field is genuinely null.

**I asked for sizes but `sizes` is empty.**
Size data only exists when `fetchDetails` is on. Without it the run has no size data, and the size filter is honestly not applied rather than silently faked.

**The run says `CAPPED`. Did it fail?**
No. It means one of your limits was reached before the scope was finished. Raise `maxProducts`, `maxProductsPerTarget` or `maxPagesPerTarget`, or narrow the scope.

**My keyword returned nothing.**
That is `EMPTY_CONFIRMED`: Zara has no genuine match. You were not charged for rows. Try a broader keyword or a category URL.

**Can I monitor a different country?**
Yes. Each `country` and `language` combination is its own scope with its own baseline, so a US monitor and an ES monitor never mix.

Collects publicly available product information from Zara's own public storefront. No login, no account data, no personal data.

# Actor input Schema

## `searchQueries` (type: `array`):

Keywords to search on Zara, for example "blazer" or "linen dress". This is Zara's own product search, so results genuinely match the keyword. If a keyword matches nothing, the run reports EMPTY\_CONFIRMED and returns no rows instead of unrelated suggestions - and you are not charged for them.

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

Paste Zara category, New In or sale page URLs, for example https://www.zara.com/us/en/woman-new-in-l1180.html. Use these when you want a whole section rather than a keyword.

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

Paste specific Zara product page URLs to scrape or monitor exactly those items. Product URLs always return full detail including per-size availability.

## `country` (type: `string`):

Two-letter country code of the Zara storefront, for example us, es, gb, de, fr.

## `language` (type: `string`):

Two-letter language code for that storefront, for example en or es.

## `section` (type: `string`):

Which Zara section keyword searches should look in. Category and product URLs carry their own section.

## `fetchDetails` (type: `boolean`):

Adds per-size availability and price, SKU codes, fabric composition and the full description. Required if you want to filter by size.

## `trackChanges` (type: `boolean`):

The first run stores a baseline and returns products only. Later runs on the same scope also return NEW, UPDATED and ENDED rows with previousPrice and priceDelta. A product must be missing twice in a row before it is reported as ENDED.

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

Keep only products that carry a real strike-through original price.

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

Keep only products currently marked in stock.

## `minPrice` (type: `integer`):

Keep only products at or above this price, in the storefront's own currency.

## `maxPrice` (type: `integer`):

Keep only products at or below this price, in the storefront's own currency.

## `colors` (type: `array`):

Keep only products with a colourway matching one of these names, for example black or beige. Partial, case-insensitive match.

## `sizes` (type: `array`):

Keep only products with at least one of these sizes in stock, for example M or 40. Requires "Include sizes, stock and composition" - without it the run has no size data and this filter is not applied.

## `sortBy` (type: `string`):

Order the delivered rows after filtering.

## `maxProducts` (type: `integer`):

Hard cap on rows returned across every target.

## `maxProductsPerTarget` (type: `integer`):

Cap per keyword, category or product URL, so one large category cannot consume the whole run.

## `maxPagesPerTarget` (type: `integer`):

How deep to page into each keyword or category.

## `maxBuyerChargeUsd` (type: `integer`):

The run refuses to start if its own worst-case cost would exceed this amount.

## Actor input object example

```json
{
  "searchQueries": [
    "blazer"
  ],
  "country": "us",
  "language": "en",
  "section": "WOMAN",
  "fetchDetails": false,
  "trackChanges": false,
  "onSaleOnly": false,
  "inStockOnly": false,
  "sortBy": "relevance",
  "maxProducts": 200,
  "maxProductsPerTarget": 2000,
  "maxPagesPerTarget": 60,
  "maxBuyerChargeUsd": 25
}
```

# Actor output Schema

## `dataset` (type: `string`):

Every product, change and coverage row from this run.

## `products` (type: `string`):

Product columns: price, was-price, discount, stock, colours, canonical URL and image.

## `changes` (type: `string`):

NEW, UPDATED and ENDED rows with previous price, current price and delta.

## `coverage` (type: `string`):

What the run actually covered, what it filtered, and whether a limit was reached.

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

Coverage, counts, field coverage, price spread, limits used and charged events.

# 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 = {
    "searchQueries": [
        "blazer"
    ],
    "country": "us",
    "language": "en",
    "section": "WOMAN",
    "fetchDetails": false,
    "trackChanges": false,
    "onSaleOnly": false,
    "inStockOnly": false,
    "maxProducts": 200,
    "maxBuyerChargeUsd": 25
};

// Run the Actor and wait for it to finish
const run = await client.actor("luminar/zara-product-scraper-price-monitor").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 = {
    "searchQueries": ["blazer"],
    "country": "us",
    "language": "en",
    "section": "WOMAN",
    "fetchDetails": False,
    "trackChanges": False,
    "onSaleOnly": False,
    "inStockOnly": False,
    "maxProducts": 200,
    "maxBuyerChargeUsd": 25,
}

# Run the Actor and wait for it to finish
run = client.actor("luminar/zara-product-scraper-price-monitor").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 '{
  "searchQueries": [
    "blazer"
  ],
  "country": "us",
  "language": "en",
  "section": "WOMAN",
  "fetchDetails": false,
  "trackChanges": false,
  "onSaleOnly": false,
  "inStockOnly": false,
  "maxProducts": 200,
  "maxBuyerChargeUsd": 25
}' |
apify call luminar/zara-product-scraper-price-monitor --silent --output-dataset

```

## MCP server setup

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

```

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/Eu8JjaCVql3W5nsFb/builds/gSxRhtZST3BxMd4fR/openapi.json
