Douglas Germany Scraper - Beauty Products, Prices & Reviews avatar

Douglas Germany Scraper - Beauty Products, Prices & Reviews

Pricing

from $1.50 / 1,000 product results

Go to Apify Store
Douglas Germany Scraper - Beauty Products, Prices & Reviews

Douglas Germany Scraper - Beauty Products, Prices & Reviews

Scrape Douglas.de beauty and fragrance products by keyword, category, brand, or URL. Extract brands, current and original prices, discounts, unit prices, size variants, stock, images, ratings, reviews, ingredients, and specifications.

Pricing

from $1.50 / 1,000 product results

Rating

0.0

(0)

Developer

Abot API

Abot API

Maintained by Community

Actor stats

0

Bookmarked

9

Total users

3

Monthly active users

18 hours ago

Last modified

Share

Douglas DE Scraper: Beauty, Perfumery & Cosmetics Products

Douglas DE Scraper turns douglas.de, Germany's leading beauty, perfumery and cosmetics retailer, into a clean product data API. Get full pricing (current price, crossed-out original price and discount), unit price, availability, image URLs and rating summary, plus a best-effort layer of ingredient lists, how-to-use text, spec tables and per-review text when detail or review enrichment is requested. Search by keyword, browse a category or brand, or paste Douglas links, then export to JSON, CSV or Excel, or pull results straight into your app through the API.

Why This Scraper?

  • Two ways to find products. Search by keyword or category, or paste any Douglas product, category or search URL. A brand filter or a pasted brand URL without a keyword or category covers the Parfum department only.
  • Real specials, never guessed. Douglas has no dedicated sale category; a special is detected from the crossed-out original price, and every product carries an explicit isOnSpecial flag plus the exact savings amount and percent.
  • Filters that actually narrow. Price band and discounted-only filters apply to every mode, including a pasted category, brand or search URL, not just keyword search.
  • Deep product data, best effort. Turn on ingredient lists (INCI), how-to-use text and the full spec table, or pull rating breakdowns and per-review text; these come from a separate, harder-to-reach route than the listing data and can come back empty on a given run, and you're never charged the enrichment surcharge when they do.
  • A single pasted product is never filtered out. Paste one product URL and you get that exact product back, whatever its price or discount, though the record is thinner than a listing result (see the FAQ).
  • Unresolvable URLs are named, not guessed. A pasted link that matches no Douglas category is reported by name and skipped rather than silently swapped for a different department's products.
  • Built for schedules. Set Max items and Max pages to control run size and cost, and a run that never gets a usable connection fails loudly instead of returning an empty dataset billed as a success.

Use Cases

  • Price and discount tracking: monitor current price, was price and savings percent across a brand or category to spot restocks and markdowns.
  • Catalog and assortment analysis: pull a whole category or brand with availability and images to compare range depth against competitors.
  • Review and rating research: turn on review enrichment to attempt the rating histogram and per-review text for a product line, best effort.
  • Ingredient and formulation research: turn on detail enrichment to attempt INCI ingredient lists and spec tables across a category, best effort.
  • Affiliate and content catalogs: build product feeds with clean pricing and images for a beauty comparison site or newsletter.

Data You Get

Sample shape: values are illustrative placeholders, not from a live record.

FieldExample
id / sku"5010207032" / "1269078"
ean"4064666325144" (only with fetchReviews, empty otherwise, including for a pasted product URL)
title"TOM FORD Soleil Neige Eau de Parfum"
brand{name: "TOM FORD", code: null} (name is null when the listing carries no brand; code is not populated)
url"https://www.douglas.de/de/p/5010207032?variant=1269078"
price{current: 76.99, original: 115.0, currency: "EUR", discountPercent: 33.1} (formattedCurrent/formattedOriginal are not populated)
unitPrice / basePriceUnit{amount: 1470.4, currency: "EUR", unit: "per 1 l"} / "1 l" (the price per reference quantity, not per pack; null when the listing has none)
recommendedRetailPrice125 (the manufacturer's recommended retail price, null when the listing shows none; separate from the strike-through price.original)
lowestPrice30d71.41 (lowest price of the last 30 days when the listing shows it, else null)
isOnSpecial / wasPricetrue / 115.0 (full-price products return false / null)
savingsAmount / savingsPercent38.01 / 33.1
availability{code: "AVAILABLE", inStock: true}
images[{url, alt}, ...] (alt is not populated)
rating4.8
ratingBreakdown[{rating: 5, count: 77}, ...] (when reviews are fetched)
reviews[{id, author, date, rating, title, body}, ...] (when reviews are fetched)
ingredients"ALCOHOL, PARFUM / FRAGRANCE, AQUA / WATER / EAU, ..." (when detail is fetched)
howToUsehow-to-use instructions (when detail is fetched)
specifications{Duftrichtung: "frisch, blumig", Kopfnote: "Jasmin", ...} (when detail is fetched)
scrapedAt"2026-09-29T10:15:00+00:00"

Every record also carries currency ("EUR"), flags (the store's promotion flags, a list), contentAmount and contentUnit (pack size, e.g. 50 and ml, null when unknown), imageCount, thumbnail (first image URL or null), brandLine, productType, productFamily, promoLabel, isMarketplace and characteristics. variants, breadcrumbs, description, variantLabel and reviewCount are never populated in any mode: the store's listing backend carries no such data, and the product page route reads only the spec table, ingredients and how-to-use text. They are always [], "" or null. ratingStars is always null; reviewSummary is filled only with fetchReviews, and only when the store provides one. All nested detail fields are nullable or empty rather than missing, so a non-discounted product, a brandless marketplace listing, a product-URL row or a listing-only run (no ingredients, reviews or spec table fetched) is an explicit negative in the output, never an absent key. A product-URL row has the same keys as a listing row; only id, sku, title, url, scrapedAt, specifications, ingredients and howToUse can carry values (plus the review fields and ean with fetchReviews).

How to Use

  1. Pick a mode: search (keyword, category and/or brand) or url (paste Douglas links).
  2. Fill in the fields for that mode. The price and discount filters below them apply in both modes.
  3. Turn on product detail and/or review enrichment if you need ingredients, spec tables or per-review text.
  4. Set Max items and Max pages to control run size and cost, then click Start. Download the dataset as JSON, CSV or Excel, or read it through the API.

Keyword search:

{
"mode": "search",
"queries": ["vitamin c serum"],
"gender": "FEMALE",
"maxItems": 30
}

Browse a brand's discounted products under a price cap:

{
"mode": "search",
"category": "01",
"brand": "Dior",
"priceMax": 80,
"specialsOnly": true
}

Search with detail and review enrichment on:

{
"mode": "search",
"queries": ["eau de parfum"],
"fetchDetails": true,
"fetchReviews": true,
"maxReviewsPerProduct": 50,
"maxItems": 20
}

Paste a category URL, filtered by price band:

{
"mode": "url",
"urls": ["https://www.douglas.de/de/c/parfum/01"],
"priceMin": 40,
"priceMax": 100,
"maxPages": 3
}

Run it from your code

Python:

from apify_client import ApifyClient
client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("abotapi/douglas-de").call(run_input={"mode": "search", "queries": ["parfum"], "maxItems": 20})
for product in client.dataset(run["defaultDatasetId"]).iterate_items():
print(product["title"], product["price"])

JavaScript:

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('abotapi/douglas-de').call({ mode: 'search', queries: ['parfum'], maxItems: 20 });
const { items } = await client.dataset(run.defaultDatasetId).listItems();

Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the Integrations tab.

Filters apply in both modes, with one exception

priceMin, priceMax and specialsOnly narrow the results in search mode and in URL mode alike, including the products found behind a pasted category, brand or search URL. The one exception: a single pasted product URL is always returned, whatever its price or discount, because you asked for that exact product.

URL mode defaults and product codes

URL mode without urls scrapes the four example URLs (one product, one category, one brand, one search), up to maxItems products in total. The category, brand and search examples are full listings, so with the default maxItems of 100 most of the rows come from the first listing; lower maxItems for a smaller sample. Setting productCodes and leaving urls at those four examples scrapes only your product codes. productCodes accepts numeric ids or /de/p/ URLs. Brand-line URLs (/de/b/<brand>/<line>/<code>) are not resolved by the store index and are named in the log and skipped.

Unresolved URLs and caps

A pasted URL that douglas.de's own listing backend does not recognize as a category or brand (a content page, a store-finder page, the homepage) is named in the run log and skipped, never replaced with a different department's products. Max items stops the run once that many products have been collected (default 100); Max pages caps how many result pages are walked per query, category, brand or URL (one page holds 48 products, default 0 meaning unlimited). With Max pages at its default, every page is walked until Max items is reached.

Recurring monitoring (incremental mode)

Turn on incrementalMode and schedule the run. The actor keeps a baseline per search/URL/filter setup (or per stateKey). Products that did not change are not returned and are not billed as results; the detail-enrichment charge applies once per returned row that actually received product-page or review data, after that row is delivered, and never for a row that was suppressed as unchanged. Products missing from a run (capped, filtered or partial walks) are left in the baseline and never reported as removed.

Send results into your apps (MCP connectors)

Optionally pipe the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. This is an extra delivery step after the scrape: the Apify dataset is never changed.

What gets written to the connector: a condensed, human-readable summary of each record, not the full JSON. Each item becomes one entry with a title and its key fields flattened to plain text. The complete record always stays in the Apify dataset.

  1. Authorize a connector once under Apify → Settings → Integrations (Notion, Linear, Airtable, or Apify).
  2. Select it in the "Pipe results into your apps" input field. (If the picker is empty, you haven't authorized a connector yet.)
  3. For Notion, also set notionParentPageUrl to the page where items should be created.

The connection is mediated by Apify's MCP proxy, so this actor never sees your third-party credentials. Leave the field empty to skip.

Input Parameters

ParameterTypeDefaultDescription
modestringsearchsearch (keyword, category, brand) or url (paste Douglas links).
queriesarrayno default (prefill: ["parfum"])Keywords to search, e.g. "vitamin c serum", "parfum". Each query runs separately.
categorystringno default (prefill: "01")A Douglas category code (e.g. 01 Parfum) or a full category URL. Search mode only.
brandstringno defaultA brand display name (e.g. Dior) or a full brand URL. Search mode only. Without a keyword or category, this covers the Parfum department only; add a category to search a brand elsewhere.
genderstringanyRestrict to women's, men's or unisex products. Search mode only, no effect in URL mode.
sortBystringrelevanceBest match, price ascending, price descending, newest first or top rated. Price ordering applies in search mode and to pasted category, brand and search URLs; newest and rating currently return the same order as best match.
keywordsarrayno defaultAlias of queries, used when queries is empty.
scrapeModestringno defaultAlias: detail turns fetchDetails on. Other values change nothing.
sortstringno defaultAlias of sortBy (price_low, price_high, top_rated).
maxResultsintegerno defaultAlias of maxItems, used only while maxItems is left at its default of 100; a maxItems you changed always wins.
promoFlagsarrayno default (prefill: none)Promotion facet codes such as travel size, new or limited edition. Currently has no effect on results; kept for compatibility.
urlsarrayone example of each shape (product, category, brand, search)Douglas product, category, brand or search URLs. URL mode only. URL mode without urls scrapes these four examples, up to maxItems products in total.
productCodesarraynoneNumeric Douglas product ids or /de/p/ URLs, scraped like pasted product URLs. URL mode only. Set without urls, only these products are scraped.
priceMinnumberno defaultMinimum price in EUR. Applies in both modes; a pasted product URL is always returned regardless.
priceMaxnumberno defaultMaximum price in EUR. Applies in both modes; a pasted product URL is always returned regardless.
specialsOnlybooleanfalseKeep only products currently reduced (crossed-out original price). Applies in both modes.
fetchDetailsbooleanfalseOpen each product page for ingredients (INCI), how-to-use text and the spec table. Off = listing fields only.
fetchReviewsbooleanfalsePull the rating histogram, EAN and per-review text for each product.
maxReviewsPerProductinteger20Cap on reviews returned per product (1-200, hard maximum 200). Ignored unless fetchReviews is on.
maxPagesinteger0Max result pages to walk per query/category/brand/URL (0 = unlimited). One page holds 48 products.
maxItemsinteger100Hard cap on the total number of products returned across all targets.
proxyConfigurationobjectApify Proxy, residential, GermanyConnection settings, used for a pasted product URL and for detail/review enrichment; a Germany exit is required for those, and changing the country can make them return nothing. Search, category, brand and search-URL runs don't use this setting.
resumeFromRunIdstringemptyPaste a previous run ID or dataset ID to continue an interrupted pull. Products that run already collected are skipped and not billed again. An unreadable ID stops the run instead of re-collecting everything.
incrementalModebooleanfalseRecurring monitoring. The first run returns every product as NEW; later runs return only NEW and UPDATED products. Each row carries changeType, changedFields, firstSeenAt and lastSeenAt.
stateKeystringno defaultName a monitoring campaign to keep its state stable or share it across runs. Empty derives a key from the search/URL/filter settings.
emitUnchangedbooleanfalseAlso return products unchanged since the last run, marked UNCHANGED. They are returned and billed like any other result.
ignoreFieldsForChangesarrayno defaultField names (or dotted names such as variants.size) excluded from UPDATED detection. Reviews, review summary, rating histogram, rating, rating stars, review count and scrapedAt are never compared. Incremental mode only.
mcpConnectorsarrayno defaultOptional: send a summary of each record to apps you authorized under Integrations.
notionParentPageUrlstringno defaultNotion connector only: page under which records are created.
maxNotifyListingsinteger50Cap on items written to each connector per run. Does not affect the dataset.

Output Example

Sample shape: values are illustrative placeholders, not from a live record.

{
"id": "5010207032",
"sku": "1269078",
"ean": "4064666325144",
"title": "TOM FORD Soleil Neige Eau de Parfum",
"variantLabel": "",
"url": "https://www.douglas.de/de/p/5010207032?variant=1269078",
"brand": { "name": "TOM FORD", "code": null },
"brandLine": "Soleil Neige",
"productFamily": "Eau de Parfum",
"productType": "PERFUME",
"isMarketplace": false,
"breadcrumbs": [],
"price": { "current": 76.99, "original": 115.0, "currency": "EUR", "formattedCurrent": null, "formattedOriginal": null, "discountPercent": 33.1 },
"unitPrice": { "amount": 1539.8, "currency": "EUR", "unit": "per 1 l" },
"basePriceUnit": "1 l",
"recommendedRetailPrice": 125,
"lowestPrice30d": 71.41,
"isOnSpecial": true,
"wasPrice": 115.0,
"savingsAmount": 38.01,
"savingsPercent": 33.1,
"promoLabel": "reduziert",
"availability": { "code": "AVAILABLE", "inStock": true },
"variants": [],
"images": [
{ "url": "https://images.douglas.de/is/image/rc/5010207032-01", "alt": null }
],
"description": "",
"characteristics": ["Eau de Parfum"],
"rating": 4.8,
"ratingStars": null,
"reviewCount": null,
"ratingBreakdown": [
{ "rating": 5, "count": 77 },
{ "rating": 4, "count": 6 },
{ "rating": 3, "count": 2 },
{ "rating": 2, "count": 1 },
{ "rating": 1, "count": 0 }
],
"reviews": [
{ "id": "1001", "author": "Sample User", "date": "2026-05-01", "rating": 5, "title": "Wonderful scent", "body": "Long lasting and elegant.", "isRecommended": true, "variant": "50 ml", "verified": true }
],
"ingredients": "ALCOHOL, PARFUM / FRAGRANCE, AQUA / WATER / EAU, LIMONENE, LINALOOL",
"howToUse": "Auf die Haut auftragen, nicht verreiben.",
"specifications": { "Duftrichtung": "frisch, blumig", "Kopfnote": "Jasmin", "Art-Nr.": "5010207032" },
"reviewSummary": null,
"scrapedAt": "2026-09-29T10:15:00+00:00"
}

Plan Requirement

Search, category, brand and search-URL runs read douglas.de's listing backend and need no proxy. A pasted product URL and detail/review enrichment read the product pages, which sit behind an edge access wall that refuses plain datacenter connections; those parts need the default setting (Apify Proxy, residential, Germany), which requires a plan that includes residential proxies. The actor retries on a backup connection when one is refused. If your plan has no proxy access, the product-page parts of a run may come back empty and the run log says so; listing-only runs are unaffected. Changing the country away from Germany can make those parts return nothing.

FAQ

How much does it cost?

You pay per product returned, plus a one-time actor-start charge. The detail-enrichment event is charged once per delivered row that actually received product-page or review data, right after that row is pushed, never for a row without that data. In incremental mode, rows suppressed as unchanged are neither returned nor charged. The Pricing tab shows the current rates. Use Max items and Max pages to cap the cost of any run.

This actor collects only publicly available product data. You are responsible for how you use it: follow Douglas's terms and the laws that apply to you, and get legal advice if you plan commercial redistribution. Product prices and specifications are generally facts, but brand names, images and review text may be subject to third-party rights.

Can I get only the products I care about on a schedule?

Yes. Schedule the actor from the Schedules tab with a fixed keyword, category or brand input, and use priceMin/priceMax/specialsOnly to keep each run scoped to what you track.

Why does turning on detail or review enrichment add nothing?

Product detail and reviews are fetched from a separate, harder-to-reach route than the listing data, and that route is currently blocked more often than not; ingredients, how-to-use text, spec tables, the rating breakdown and per-review text can come back empty on most or all products in a given run. The listing fields (price, availability, images, rating summary) are unaffected either way, and the enrichment surcharge is only charged on products where it actually added data, so a run where enrichment adds nothing costs the same as one with it turned off.

What does a pasted product URL return?

A thinner record than a search or category result. A pasted product page is fetched through the same route used for detail/review enrichment, so it needs the default German proxy setting and can fail if that route is blocked. When it succeeds without detail or reviews turned on, the row has the same keys as a listing row, but only id, sku, title, url, scrapedAt and whatever the product page's spec table, ingredients and how-to-use text yield carry values; price, availability, images and rating are empty or null. Turn on detail and/or review enrichment on the same run to fill in more, best effort. To get full listing fields (price, availability, images, rating) for a specific product, search or browse for it instead of pasting its URL.

Why did my run fail instead of returning an empty dataset?

If the store refuses every connection, or none of the pasted URLs matched a real Douglas category, the run stops with a clear message instead of billing a quiet, empty success. Running again usually succeeds. If only some URLs matched, you still get products for the ones that did, and the others are named in the run log.

Can I use it with AI agents or MCP?

Yes. Call it from any Apify integration or MCP client, and use the connector field to push results into Notion, Linear or Airtable.

🔗 Want more e-commerce data?

Pair this actor with these related scrapers from the same team:

🛒 MediaMarkt Germany Scraper
Scrape MediaMarkt.de products by keyword or URL. Extract current and original prices...
🛒 dm.de
Scrape dm-drogerie markt (dm.de) products: current + strike-through Ausverkauf price with...
🥦 REWE.de Scraper
Scrape REWE Germany (rewe.de) grocery products: current price, was-price and discount...
🛒 ROSSMANN.de Scraper
Scrape ROSSMANN (rossmann.de) drugstore products: current + original strike-through price...
🛒 OBI.de Scraper
Scrape OBI (obi.de) DIY/home-improvement products: current + strike-through original...
🛒 Idealo.de
Scrape Idealo.de products with identity details, GTIN, price ranges, specifications...

👉 Browse all abotapi scrapers

💬 Support

  • 🐞 Found a bug or a missing field? Open a ticket on the Issues tab. We usually reply within hours.
  • 🛠️ Need another site, extra fields or a private build? Email contact@abotapi.com.
  • ⭐ Enjoying it? A quick review on the actor page helps other users find it.