Zalando Products Scraper
Pricing
from $1.05 / 1,000 results
Zalando Products Scraper
Product listings from Zalando catalogue and category pages across 12 country storefronts: name, brand, price and was-price, discount, silhouette and images. Relevance-checked, de-duplicated by SKU.
Pricing
from $1.05 / 1,000 results
Rating
0.0
(0)
Developer
Ibnu Adzim
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
2 days ago
Last modified
Categories
Share
Product listings from Zalando catalogue and category pages — name, brand, price and was-price, discount, silhouette and images — across 12 country storefronts.
HTTP only, no browser, no login.
Input
{"mode": "search","market": "de","keywords": ["sneaker", "laufschuhe"],"maxPages": 3,"maxItems": 200}
Or point it at catalogue/category URLs:
{ "mode": "urls", "market": "de","listingUrls": ["https://www.zalando.de/herrenschuhe/"] }
Markets
de at ch uk fr it es nl be pl se dk — each with its
own currency and language. Every one was fetched and confirmed to return
products; the market list is measured, not assumed.
This matters more than a locale toggle: each storefront has its own
catalogue path, and the path is not derivable from the language. .de,
.at, .ch, .se, .dk, .pl use /katalog/; .fr uses /catalogue/;
.it, .es use /catalogo/; .nl, .be use /catalogus/; and the UK
uses /catalog/ — /catalogue/ 404s there.
What you get
recordType | One per | Carries |
|---|---|---|
SEARCH_SUMMARY | keyword / URL | market, pages fetched, rows fetched vs returned, duplicates dropped, why paging stopped, and a relevance score |
PRODUCT | article | SKU, URL, name, brand, price, was-price, discount %, silhouette, images — plus the raw upstream object |
ERROR | failed input | why |
{"recordType": "PRODUCT","sku": "NI114D0HT-A11","productUrl": "https://www.zalando.de/NI114D0HT-A11.html","name": "AIR FORCE 1 LE UNISEX - Trainers - white","brand": "Nike Sportswear","currency": "EUR", "price": 69.95, "originalPrice": 99.95,"isDiscounted": true, "discountPercent": 30,"silhouette": "SNEAKER","imageUrls": ["https://img01.ztat.net/article/..."]}
Known limits — read these before you rely on the output
Zalando has no "no results" page. An unmatched query returns HTTP 200
with unrelated products. So every query's relevance is measured and reported:
titleMatchRate is the fraction of returned articles matching a query token
across name, brand, silhouette and model, and lowRelevanceWarning flags a
run below 20%. Measured: sneaker / nike / kleider / laufschuhe
1.00, jacke 0.96, stiefel 0.92, hemden 0.88, three different
nonsense strings 0.00. Rows are never silently dropped — a low score is a
fact about your query.
The relevance stemmer is German-aware (-e, -en, -er), because with
English-only plural handling the perfectly valid query laufschuhe scored
0.00 and would have been wrongly flagged.
price is what you pay; originalPrice only exists when discounted.
Undiscounted articles carry no promotional block at all
(priceDisplayMode: "BLACK_PRICE") and their price lives in original. This
actor resolves that, so price is always populated, and originalPrice is
left null rather than echoing price and inventing a markdown that does not
exist. isDiscounted says which case a row is.
Prices come from a mix of cents and floats. Upstream sends
promotional.amount / original.amount as integer cents (9595) and
trackingCurrentAmount as a float (95.95). The cents are converted
explicitly; the float is passed through beside them as a cross-check.
Product detail pages are not supported. Zalando listing paths end in /
and product pages end in .html; the latter are refused with an explanation.
24 articles per page. Page 1 is deterministic (fetched twice, identical 24/24), and page 2 overlaps page 1 by about one article, de-duplicated by SKU.
Technical
Server-rendered. The data is neither __NEXT_DATA__ nor JSON-LD nor an RSC
flight stream — all three were checked and are absent. It is a single JSON
object passed to window.__hydrationDataConsume({...}) containing a
graphqlCache with the full article objects. Products are located
structurally (any dict with both sku and name) because that cache is keyed
by a per-deploy query hash.
No WAF: 5/5 TLS profiles returned 200 with near-identical sizes. Past the last page Zalando answers HTTP 400, treated as an honest end of results.
robots.txt was checked at path level: the catalogue path with ?q= is
ALLOWED, no ClaudeBot / anthropic-ai group, no blanket disallow.
Full recon trail is in CRAWLING_METHOD.md.