# Zara Price & New-In Watch: Products by Category (`superslowsloth/zara-products`) Actor

Zara products from 9 country stores as flat rows: name, price in currency units, price before the markdown, discount percent, colour, availability, NEW badge, photos, product URL. Read any category by id or URL; keep only sale or new-in items.

- **URL**: https://apify.com/superslowsloth/zara-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 $0.69 / 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

## Zara Price & New-In Watch: Products by Category

Read [Zara](https://www.zara.com) category listings as flat rows: product name,
**price in currency units**, the **price before the markdown**, the discount
percent, colour, availability, the **NEW badge**, photos and the product link.
Nine country stores, each in its own currency. Give it category ids or category
URLs, tick **Only products on sale** or **Only products tagged NEW**, and
schedule it daily: diff the runs and that is a Zara price and new-in watch.

No account, no API key, no login, no browser. One request returns a whole
category (407 products for US jeans, 766 for "the new").

### Input

| Field | Notes |
|---|---|
| `store` | `us/en` (USD), `gb/en` (GBP), `es/es`, `de/de`, `fr/fr`, `it/it` (EUR), `jp/ja` (JPY), `se/sv` (SEK), `pl/pl` (PLN). Default `us/en`. |
| `categories` | Category ids or Zara category URLs, one per line. Required. `2419242` and `https://www.zara.com/us/en/woman-jeans-l1119.html?v1=2419242` are the same category (jeans). A product that two categories both list is returned once, under the first. |
| `onlyOnSale` | Keep only products Zara marks down. |
| `onlyNew` | Keep only products carrying Zara's `NEW` badge. With `onlyOnSale` both must hold. |
| `maxItems` | Products to return per category (default 100, up to 5,000), in Zara's own order. |
| `proxyConfiguration` | Apify proxy by default. |

**Finding a category id.** It is the `v1=` number in the URL of any Zara
category page, or the `id` of a node in the store's category list,
`https://www.zara.com/us/en/categories?ajax=true`. Ids are the same in every
store (jeans is 2419242 in the US, UK and Japan); the store changes the
language, prices and range. A URL without `v1=` (only the `-l1119` part) is
resolved through the store's category list; if that fails the run says so and
asks for the id.

A category that only groups subcategories ("NEW ARRIVALS", 2642765) returns
nothing: Zara answers it with an empty list. The run log then names the
subcategories that do list products ("THE NEW", 2546081, ...) so the next run
can use them. Zara's endpoint has no sale or new filter and no paging: the
whole category is downloaded and the filters are applied here, so `maxItems`
limits what you are charged for, not what is downloaded.

### Output

| Field | Notes |
|---|---|
| `category_id`, `store` | The category the product was first met in, and the store (`us/en`, ...). |
| `product_id`, `model_id`, `reference`, `colour_reference` | Zara lists **every colour of a garment as its own product**. `product_id` is the row's identity (the `v1=` of the product URL); `model_id` (Zara's `seoProductId`) is shared by the colours of one garment; `reference` is the garment reference (`09632275-I2026`), `colour_reference` the colour's. |
| `name`, `description` | As Zara prints them, in the store's language. `description` is empty on most clothes (null) and a short line on fragrances. |
| `section`, `family`, `subfamily`, `brand`, `kind` | Zara's `sectionName` (`WOMAN`, `MAN`, `HOME`), its internal `familyName` / `subfamilyName` (Spanish codes such as `PANTALON`), `brandGroupCode` (`zara`, `zarahome`) and `kind` (`Wear`, `Other`, `Fragance`, Zara's spelling), all as sent. |
| `colour`, `colour_id`, `available_colours` | This product's colour name and Zara colour id, and the colours the garment comes in (this one included). |
| `url` | `https://www.zara.com/<store>/<keyword>-p<model_id>.html?v1=<product_id>`, built from the keyword Zara sends. |
| `currency`, `price` | The current price **in currency units** (79.9, not 7990) with its ISO code. |
| `previous_price` | The price before the markdown. **Null on every product that is not marked down.** |
| `on_sale`, `discount_percent`, `discount_label` | Zara's `isOnSale` flag, Zara's own `displayDiscountPercentage`, and the badge as printed (`-40%`). Null when not marked down. |
| `availability` | `in_stock`, `coming_soon`, ... exactly as sent. |
| `tags`, `is_new` | Zara's badges as printed (`NEW`, collaboration names), and whether one is `NEW`. |
| `image_url`, `additional_image_urls` | Photos, `https://static.zara.net/...`, at 750 px wide. Videos are not included. |

#### Honest nulls and the traps

- **Prices are scaled integers, and the scale depends on the currency.** Zara
  sends `7990` for US$79.90 and `7990` for ¥7,990. The actor divides by 100 for
  the eight two-decimal currencies and not at all for JPY. Checked on 2026-10-13
  for every offered store against the storefront's own price strings in the
  same responses (`"$ 59.90"` against `5990`, `"￥ 6,590"` against `6590`,
  `"399,00 SEK"` against `39900`).
- **Zara sends no currency code.** `currency` comes from the store table in
  this actor. Measured: an invented store (`/xx/en/`) is not refused, it answers
  with a full catalogue of some fallback store, so the stores are a fixed list
  of those whose currency was read off the page. Canada (prints a bare `$`) and
  Australia (404) are not offered.
- `previous_price`, `discount_percent` and `discount_label` are null for a
  product that is not marked down. Measured 2026-10-13 over 2,414 product
  records (US category pages, several of them the same product twice): Zara sent
  an old price on every marked-down product and on no other.
- `price` is null if Zara sends none or flags the price unavailable (never 0).
  None of the 2,414 records measured did.
- **Duplicates.** A category response lists some products twice (405 distinct
  ids in 407 for US jeans). A product is returned and charged once per run.
- **Product links.** Zara's product pages are rendered in the browser and the
  server answers HTTP 200 with a small shell for *any* address (a made-up
  product answered 200 as well), so a 200 cannot confirm a link. The address is
  built from the same keyword and ids Zara's own site uses. A product Zara
  sends without a keyword has a null `url`.
- **Hidden placeholders are dropped.** Two records in 2,414 had no name and no
  keyword and were flagged `hideProductInfo` by Zara (a perfume sample tint, in
  the "THE NEW" category). They are not rows, are not charged, and the run log
  counts them.
- A category listing `Zara Home` items (`section` `HOME`, `brand` `zarahome`)
  returns them as rows; their links use the same pattern and were not checked
  against the Zara Home site.
- `description`, `section`, `family`, `subfamily`, `brand`, `kind`, `colour`,
  `availability` and the references are null when Zara sends no value.

### 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.00069** per product row written (`product-scraped`), i.e. $0.69 per
  1,000 products.

For reference, measured on the Apify Store on 2026-10-13, the other Zara
product scrapers charge $0.003 (gauzy_penguin) and $0.00099 (shahidirfan) per
product. This actor's $0.00069 is 30% under the cheaper one.

### How it fetches

It calls the JSON endpoints behind Zara's own storefront,
`www.zara.com/<country>/<language>/category/<id>/products?ajax=true` and
`.../categories?ajax=true`, with no cookie, token or login, presenting a Chrome
TLS fingerprint through the Apify datacenter proxy. **Zara's edge (Akamai)
refused 42% to 50% of requests from Apify datacenter addresses with an `Access
Denied` page (HTTP 403)**, interleaved per exit address and flat over 45
minutes (measured 2026-10-13), so the refusal is the address, not the request
and not a block that tightens. A plain non-browser TLS client is refused
outright. The actor treats a 403, 429 or 5xx, and a 200 that is not the
products JSON, as a refusal, rotates to a fresh address and retries up to ten
times; a category that is still refused is reported in the run log and skipped.
Expect run logs to show retries. An unknown category id (HTTP 404) is permanent
and not retried.

# Actor input Schema

## `store` (type: `string`):

Which Zara country store to read. Each store has its own currency, language, prices and range. United States (USD), United Kingdom (GBP), Spain, Germany, France and Italy (EUR), Japan (JPY), Sweden (SEK) and Poland (PLN). Only stores that returned products, with a currency read off the storefront's own price strings, when measured on 2026-10-13 are listed. Category ids are the same in every store.

## `categories` (type: `array`):

Zara category ids or category page URLs, one per line. An id is the number in the URL after v1= (https://www.zara.com/us/en/woman-jeans-l1119.html?v1=2419242 is category 2419242) or in the store's category list at https://www.zara.com/us/en/categories?ajax=true. Each category is read in full and separately; a product that two categories both list is returned once, under the first. A category that only groups subcategories returns nothing, and the run log then names the subcategories to use instead.

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

Keep only products Zara currently marks down. Each row then carries the price before the markdown and the discount percent. Applied after the category is read, because Zara's endpoint has no filter, so Max products counts matching products only.

## `onlyNew` (type: `boolean`):

Keep only products carrying Zara's NEW badge: the new-in watch. Applied after the category is read, so Max products counts matching products only. Combine with Only products on sale to keep products that are both.

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

Return at most this many products for each category, in Zara's own order. Zara sends a whole category in one response (407 products for US jeans, 766 for 'the new'), so the cap limits what you are charged for, not what is downloaded.

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

Apify proxy (automatic datacenter exits) is the default. Zara's edge refuses roughly half of the requests from the datacenter pool with an Access Denied page; the actor retries each refused request from a fresh address, which is what the proxy is for.

## Actor input object example

```json
{
  "store": "us/en",
  "categories": [
    "2419242"
  ],
  "onlyOnSale": false,
  "onlyNew": false,
  "maxItems": 100,
  "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 = {
    "categories": [
        "2419242"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

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

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

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,superslowsloth/zara-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/IlcF9PyED98HUh6qA/builds/W97M5xCuoweuqqVGm/openapi.json
