# Zara Scraper - Prices, Sizes, Stock and Images (`dami_studio/zara-scraper`) Actor

Reads Zara's own catalogue: name, price, previous price and discount, currency, every colour a garment comes in, sizes with per-size stock and SKU, the full image gallery and the product link. Search by keyword, paste category or product links, or give category ids. Works on any Zara country store.

- **URL**: https://apify.com/dami\_studio/zara-scraper.md
- **Developed by:** [Dami's Studio](https://apify.com/dami_studio) (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 $1.84 / 1,000 product returneds

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 Scraper: prices, sizes and stock from any Zara country store

Type a search term, paste Zara category or product links, or give category ids, and pick a country
store. Each product colour comes back as a row with its name, price and markdown, currency, colours,
sizes with SKU and stock, photos, reference and link. Stock is Zara's own word for each size, like
in stock or sold out, never a number, because Zara publishes no count.

| | |
|---|---|
| **Input** | Search terms, Zara category or product links, or category ids, plus a country |
| **Output** | One row per product colour |
| **Ceiling** | 5,000 products per run. With sizes on, about 2,500 fit in the default hour |
| **Account needed** | None |
| **Price** | $1.84 per 1,000 products, flat on every plan |

### 🔍 What Zara Scraper does

It lists products the way Zara's own grid does, once per colour. A black blazer and the same blazer
in brown are two rows. Each row carries the garment's full colour list, so group on
`parentReference` if you would rather count garments than colours.

Prices arrive as plain numbers in the store's currency. Japan, Korea, Vietnam, Chile and Iceland
quote whole units, so the decimal count comes from each market rather than a guess. Seventy-one
country stores are recognised, and the same shirt is often marked down in one and full price in the
next.

Zara never answers "no results". Search it for something it does not sell and it sends back about
300 products picked by similarity, and they look convincing: `refrigerator` on the US store gives
cooler boxes. Those are dropped, not charged, and you get one free note so you can fix the term.
Ordinary misspellings like `jeens` or `linnen shirt` still come back as real matches, and so does a
reference like `1957/102`.

Zara also pins the same garment into several spots on one grid. Repeats are dropped, so a category
page showing 76 tiles gives you its 51 distinct garment colours, and you pay for 51.

Thirty linen shirts with sizes took 43 seconds on a test run.

### 📥 What you give it

```json
{
  "searchTerms": ["linen shirt"],
  "country": "us",
  "maxItems": 30
}
```

| Field | If you leave it out | What it is |
|---|---|---|
| `searchTerms` | nothing is searched | What to look for, one per line. A reference works too: `1957/102` or `01957102` returns that garment. |
| `startUrls` | none | Zara category, product or search links. Copy a category link after the page has loaded, so it carries the `v1=` number. A link without it still works, it just has to be looked up by name first. |
| `categoryIds` | none | The `v1=` numbers of categories, if you already have them. Not the number after `-l` in the link. |
| `country` | `us` | The country code from a Zara address: `zara.com/es/es/` is `es`. It decides the currency and what is on sale. Links keep their own country. |
| `language` | `en` | The language code from the same address. Copy both from a real Zara link. |
| `maxItems` | `60` | 1 to 5,000, counted across everything in the run. |
| `includeSizes` | on | One extra lookup per product for size names and per-size stock. Off is much faster and costs the same per row. |
| `onlyInStock` | off | Keeps only items Zara marks `in_stock`. Anything else, low stock included, is dropped before the dataset and not charged. |
| `includeRelatedMatches` | off | Keeps Zara's look-alike suggestions when a search has no real match. They are then delivered and charged, marked `related`. |
| `proxyConfiguration` | not used | Only for your own servers. Addresses you list here are tried first. Leave it alone otherwise. |

The form opens with `linen shirt` and a cap of 60 filled in. That is an example to edit, not a
setting to keep.

### 📤 What you get back

A real row from a real run on the US store, with the photo list cut to one:

```json
{
  "productId": "545493196",
  "productReference": "C09621886121000-I2026",
  "parentReference": "09621886-I2026",
  "displayReference": "9621/886",
  "seoProductId": "09621886",
  "name": "100% LINEN CHECKED SHIRT",
  "description": "Regular fit shirt made of linen fabric. Button-down collar and long sleeves with buttoned cuffs. Front button-up closure.",
  "brand": "zara",
  "section": "MAN",
  "family": "SHIRT",
  "subfamily": "Summer Shirt LS",
  "productTags": [],
  "price": 79.9,
  "oldPrice": null,
  "discountPercentage": null,
  "currency": "USD",
  "availability": "in_stock",
  "colorId": "121",
  "colorName": "Khaki",
  "colorHex": "#68564A",
  "allColors": [{ "name": "Khaki", "hex": "#68564A" }],
  "sizes": [
    { "size": "S", "availability": "in_stock", "sku": "545493200", "price": 79.9 },
    { "size": "M", "availability": "in_stock", "sku": "545493197", "price": 79.9 },
    { "size": "L", "availability": "in_stock", "sku": "545493198", "price": 79.9 },
    { "size": "XL", "availability": "in_stock", "sku": "545493199", "price": 79.9 }
  ],
  "sizesInStock": ["S", "M", "L", "XL"],
  "sizeLookup": "ok",
  "images": [
    "https://static.zara.net/assets/public/d346/b06f/92e048cbbcb1/30dcbe20d0b9/09621886121-f1/09621886121-f1.jpg?ts=1785938821924&w=1024"
  ],
  "url": "https://www.zara.com/us/en/100-linen-checked-shirt-p09621886.html?v1=545493196",
  "country": "us",
  "language": "en",
  "store": "11719",
  "categoryId": null,
  "categoryPath": null,
  "searchTerm": "linen shirt",
  "matchType": "exact",
  "position": 2,
  "scrapedAt": "2026-09-20T02:35:52.941Z"
}
```

| Field | What it is |
|---|---|
| `productId` | This colourway's id, the `v1=` number in its link. Stable enough to join runs on. |
| `parentReference`, `displayReference` | The garment across all its colours, and the reference as the website prints it. |
| `price`, `oldPrice`, `discountPercentage`, `currency` | Today's price in the store's currency. `oldPrice` and `discountPercentage` stay null unless Zara shows a struck-through price. |
| `availability` | Zara's word for it: `in_stock`, `low_on_stock`, `out_of_stock` or `coming_soon`. |
| `colorName`, `colorHex`, `allColors` | This row's colour, and every colour the garment comes in. |
| `sizes`, `sizesInStock`, `sizeLookup` | Each size with its stock word, SKU and price. `sizes` is null, never an empty list, when the lookup did not come back or was switched off, and `sizeLookup` says which. |
| `images`, `url` | The photo gallery and the product link. |
| `searchTerm`, `matchType`, `categoryId`, `categoryPath`, `position` | Where the row came from. `matchType` is `exact`, or `related` for a suggestion you chose to keep. |
| `section`, `family`, `subfamily`, `productTags` | Zara's own classification, such as `MAN` and `SHIRT`. |

### 🧾 Reading the output

Real product rows have no `charged` field at all. Every free row has one, set to `false`, so
dropping rows where `charged` is false leaves you with just the products.

| Row | How to spot it | Charged |
|---|---|---|
| A product | a `productId` and no `charged` field | yes |
| The sample | `_sample: true` | no |
| A note | `_diagnostic: true`, with an `errorCode`, an `error` and a `hint` | no |

The sample row appears when a run has no search terms, links or category ids. It shows the shape of
a row and is not live data.

| `errorCode` | What it means |
|---|---|
| `NO_DIRECT_MATCH` | Zara had nothing matching the term and offered look-alikes, which were dropped. Check the spelling. |
| `NO_RESULTS` | The search came back empty in that country. Try a plainer word. |
| `UNKNOWN_MARKET` | Zara runs no store at that country and language pair. |
| `BAD_URL`, `BAD_CATEGORY_ID` | Not a Zara link, or a category id that is not a number. |
| `CATEGORY_NOT_FOUND` | A category link that could not be matched. Copy it again once the grid has loaded. |
| `CATEGORY_EMPTY` | A menu entry with no products in it, usually a landing page. |
| `PRODUCT_NOT_FOUND` | Zara no longer sells that product in this store. |
| `SEARCH_REFUSED`, `BLOCKED`, `NETWORK`, `API_ERROR`, `BAD_JSON` | Zara did not answer that part of the run, or answered in a shape the actor cannot read. Run it again later. |
| `RUN_OUT_OF_TIME` | The run reached its time limit. What was written before it is yours, and what was not is not charged. |

### ▶️ How to run it

1. Open [Zara Scraper](https://apify.com/dami_studio/zara-scraper) and click **Try for free**.
2. Type what you want into **Search terms**, or paste category or product pages into **Zara links**.
3. Set **Country** and **Language** to the two codes in a Zara address, `us` and `en` for
   zara.com/us/en.
4. Set **Maximum products**. Leave **Fetch sizes and per-size stock** on unless prices and photos are
   all you need.
5. Click **Start**, then download the dataset as JSON, CSV or Excel.

### 💰 How much does it cost?

**$1.84 per 1,000 products**, flat on every Apify plan, whether sizes are on or off.

Not charged: the sample row, every note above, Zara's look-alike suggestions unless you switch them
on, repeated tiles, items dropped by `onlyInStock`, and anything a run did not get to write before
its time limit.

`maxItems` caps the whole run, so it is also the most you can spend on products in one go.

### 💡 What people use it for

- Pricing the same garment in several countries, each in its own currency. Sale timing differs
  between countries too.
- Following a category through a sale, since `oldPrice` and `discountPercentage` fill in the moment
  Zara strikes a price through.
- Keeping a size-by-size stock sheet for a short list of products, on a schedule.
- Turning a list of Zara references into full product rows, one search term per reference.

### 🚧 What it does not do

- **No stock counts.** Zara publishes a word per size and no number behind it. Nobody can give you
  "7 left" from the public site.
- **No per-shop stock.** This is the online catalogue, not what a particular shop has on its rails.
- **No fabric composition or care instructions.** They are not collected.
- **No reviews or ratings.** Zara does not publish them.
- **No price history.** You get what the store shows during the run. Schedule it and compare runs.
- **Search order is Zara's own and shifts between runs.** For a repeatable set, use a category.
- **Zara only.** A link from another site comes back as a free `BAD_URL` note.
- Editorial banners inside a grid are skipped rather than returned as empty rows.

### 🧭 Which fashion and retail scraper do you need?

| If you want | Use |
|---|---|
| Zara products with sizes, stock and prices | This one |
| Shein's catalogue: ids, titles, links and images, without prices | [Shein Catalog Scraper](https://apify.com/dami_studio/shein-catalog-scraper) |
| A Shopify store's whole catalogue, variants and stock | [Shopify Products Scraper](https://apify.com/dami_studio/shopify-products-scraper) |
| An alert when a product page changes price or goes out of stock | [Product Price and Stock Monitor](https://apify.com/dami_studio/structured-product-price-stock-monitor) |
| Target products with promotions and store pickup stock | [Target.com Product Scraper](https://apify.com/dami_studio/target-scraper) |

### ❓ Questions people ask

**Do I need a Zara account or an API key?** No. It reads the public catalogue, signed out.

**How do I get every product in a category?** Paste the category link and set **Maximum products**
above the number of items in it. With sizes on, allow about a minute for every 85 products.

**Why are there two rows with the same name?** They are two colours of one garment. Group on
`parentReference`, and tell them apart with `colorName` or `colorId`.

**Why is `oldPrice` usually null?** Because most things are not on sale. It fills in when Zara shows
a struck-through price.

**Can I track prices over time?** Yes. Run it on a schedule against the same categories and compare
`price` between runs, joining on `productId`.

**Is this legal?** It reads public product pages and collects no personal data. Apify's write-up on
[the legality of web scraping](https://blog.apify.com/is-web-scraping-legal/) is a good starting
point. We are not lawyers.

### 🆘 If something breaks

Open the **Issues** tab on the actor page and send the input you used and the run ID. The note rows
in the dataset usually name the reason already.

# Actor input Schema

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

What to search for on Zara, one per line — "linen shirt", "black blazer", "sneakers". A product reference works too: paste 1957/102 or 01957102 and you get that product back.

## `startUrls` (type: `array`):

Paste Zara links, one per line. A category page (https://www.zara.com/us/en/woman-blazers-l1055.html?v1=2420942) returns everything in that category. A product page (…-p01957102.html) returns that product. A search link works too. Copy the link after the page has loaded so it carries the v1= number.

## `categoryIds` (type: `array`):

If you already know the numeric category ids, put them here instead of full links. This is the v1= number in a Zara category URL, not the number after the -l.

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

Two-letter country code of the Zara store to read, exactly as it appears in the URL — us, gb, es, fr, de, it, jp, mx, ae, in, br and the rest. This decides the currency and which products are on sale there.

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

Two-letter language code for that store — en for us/gb, es for es, fr for fr, and so on. If you get an "unknown store" note back, the pair is wrong; copy both from a real Zara URL.

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

Hard cap on rows across everything you asked for (1–5000). You are charged per product row returned, so this is also your spend cap.

## `includeSizes` (type: `boolean`):

On by default. Zara's grid does not carry size names, so each product needs one extra lookup to get them. Turn this off for a faster, lighter run when you only need prices and images — the price you pay per row does not change either way.

## `onlyInStock` (type: `boolean`):

Keep only products Zara marks as in stock. Anything marked otherwise is dropped before it reaches the dataset, so you are not charged for it, and that includes low on stock, which can still be bought. A product that comes with no stock status is kept.

## `includeRelatedMatches` (type: `boolean`):

Zara never replies "no results". A term it cannot match comes back as about 300 loosely related products, and they look convincing — search "refrigerator" and you get cooler boxes. By default those are dropped and you are not charged for them; the run tells you it happened. Switch this on if you want the suggestions anyway. Rows are marked "related" rather than "exact" either way.

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

Optional. Leave this alone unless you need the run to go out through a particular network. Your own proxy servers are used exactly as given.

## Actor input object example

```json
{
  "searchTerms": [
    "linen shirt"
  ],
  "startUrls": [],
  "categoryIds": [],
  "country": "us",
  "language": "en",
  "maxItems": 60,
  "includeSizes": true,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `results` (type: `string`):

One dataset row per product colour: name, description, price, previous price and discount, currency, availability, colour name and hex, every colour the product comes in, sizes with per-size stock and SKU, image URLs, references, section and family, and the Zara link. Empty input writes a single uncharged sample row; a search Zara refuses or a category it does not recognise writes an uncharged note saying so.

# 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": [
        "linen shirt"
    ],
    "startUrls": [],
    "categoryIds": [],
    "country": "us",
    "language": "en",
    "maxItems": 60,
    "includeSizes": true,
    "onlyInStock": false,
    "includeRelatedMatches": false,
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("dami_studio/zara-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 = {
    "searchTerms": ["linen shirt"],
    "startUrls": [],
    "categoryIds": [],
    "country": "us",
    "language": "en",
    "maxItems": 60,
    "includeSizes": True,
    "onlyInStock": False,
    "includeRelatedMatches": False,
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("dami_studio/zara-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 '{
  "searchTerms": [
    "linen shirt"
  ],
  "startUrls": [],
  "categoryIds": [],
  "country": "us",
  "language": "en",
  "maxItems": 60,
  "includeSizes": true,
  "onlyInStock": false,
  "includeRelatedMatches": false,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call dami_studio/zara-scraper --silent --output-dataset

```

## MCP server setup

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