dm.de - Prices, Offers, Unit Price & Reviews
Pricing
from $1.00 / 1,000 product results
dm.de - Prices, Offers, Unit Price & Reviews
Scrape dm-drogerie markt (dm.de) products: current + strike-through Ausverkauf price with discount, per-unit pricing (per 100ml/kg/piece), brand incl. dm own-brands, EAN, ingredients, pack-size variants, and full reviews with rating breakdown. Search or paste links.
Pricing
from $1.00 / 1,000 product results
Rating
0.0
(0)
Developer
Abot API
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
6 hours ago
Last modified
Categories
Share
dm.de Product Scraper
Pull rich product data from dm-drogerie markt (dm.de), Germany's drugstore and personal-care retailer. Search by keyword, a real dm.de category, or the site's own currently-reduced Ausverkauf items, or paste product and listing links directly. Every record includes the current price, the strike-through original price with computed discount when a product is marked down, per-unit pricing (e.g. price per 100 ml or per kg) where the site shows it, and a complete customer review history with a full rating breakdown.
Why This Scraper?
- Was-price and discount, structured, not scraped from a badge. When a product carries a reduced original price, both prices are captured plus the computed discount amount and percentage.
- Real per-unit pricing. Bodycare, food, and household products return their price per 100 ml / 100 g / kg / piece separately from the pack price, exactly as the site itself breaks it down.
- Ausverkauf, first class. A
specialsOnlytoggle narrows any search or category listing to dm's own live clearance/sellout feed -- the site's real, queryable discount signal, not a guessed category. - Full review history. Overall rating, review count, a full 1 to 5 star rating breakdown, and every individual review with author, date, rating, title, and body text.
- Rich detail on demand. Full breadcrumb category path, flattened product information (description, ingredients/Inhaltsstoffe, usage notes -- varies per category, never hardcoded), pack-size variants with their own price and EAN, brand image, and online/store availability.
- Brand coverage. Every brand dm carries, including its own private-label lines (dmBio, Balea, alverde NATURKOSMETIK, and more) alongside national brands.
- Two ways in. Keyword or category search with real site filters (brand, rating, price range, Ausverkauf, sort), or paste any product/listing link and continue pagination automatically.
- Resume & recurring updates. Turn on Incremental mode to get only NEW, UPDATED, and REAPPEARED products on a scheduled re-run of the same search -- see "Resume & recurring updates" below.
- Optional export to your apps. Send results into Notion, Linear, Airtable, or any Apify MCP connector alongside the dataset.
Data You Get
| Field | Example value |
|---|---|
| productId / sku | 1234567 |
| ean | 4058172548821 |
| name | Sample Bodylotion Aloe Vera |
| brand | Sample Brand |
| category / categoryPath | Bodylotion & Hautcreme, ["Pflege & Parfum", "Körperpflege", "Bodylotion & Hautcreme"] |
| url | https://www.dm.de/p/d/1234567/sample-bodylotion-aloe-vera |
| price / currency | 3.45, EUR |
| originalPrice / discountAmount / discountPercent | 4.95, 1.50, 30.3 |
| isOnSpecial / promoLabel | true, Ausverkauf |
| unitPrice / unitPriceBasis / packSize | 0.86, 100 ml, 400 ml |
| rating / reviewCount | 4.6, 128 |
| onlineAvailable / onlineAvailabilityStatus / storeAvailabilityStatus | true, Lieferbar, Im Markt vorrätig |
| image / images | ["https://www.dm.de/sample/image.png"] |
| description / ingredients | full text fields |
| productDetails | {"Produktmerkmale": "...", "Verwendungshinweise": "..."} (flattened per category) |
| variants | [{"productId": "1234568", "ean": "4058172548838", "label": "600 ml", "isSelected": false}] |
| isPharmacy | false |
| reviews.averageRating / reviews.totalReviewCount | 4.6, 128 |
| reviews.ratingDistribution | [{"rating": 5, "count": 90}, {"rating": 4, "count": 24}, ...] |
| reviews.items[] | [{"rating": 5, "title": "Sample title", "body": "Sample review text.", "author": "Sample User", "date": "2026-07-12T07:31:15.000Z", "isRecommended": true, "verifiedPurchaser": true}] |
Sample shape: values above are illustrative placeholders, not from a live product.
Incremental mode only. When incrementalMode is on, every returned record also carries:
| Field | Description |
|---|---|
changeType | NEW | UPDATED | UNCHANGED | REAPPEARED | EXPIRED |
changedFields | Top-level fields that changed since last seen; non-empty only for UPDATED |
firstSeenAt | When this product was first observed by this monitoring campaign |
lastSeenAt | When this product was last observed |
Change detection is based on price, discount/Ausverkauf status, unit price, and online/store availability -- the fields that actually define whether a listing "changed" for monitoring purposes. Two things are deliberately excluded from change detection, and will never by themselves cause a product to come back as UPDATED: the reviews array (a new review anywhere does not mean the product changed), and rating / reviewCount (these tick on nearly every run for a high-traffic catalogue like this one -- excluded so a quiet monitoring run stays quiet). If you need to track rating drift, compare it yourself across runs; it is still returned on every record, it just does not drive changeType.
How to Use
1. Keyword search, first page, with reviews (default):
{"mode": "search","searchTerm": "bodylotion","maxItems": 20}
2. Category browse, filtered to a brand and only currently-reduced Ausverkauf items:
{"mode": "search","searchTerm": "pflege-und-parfum/koerperpflege/bodylotion-und-hautcreme","brands": ["Balea"],"specialsOnly": true,"sortBy": "PRICE_ASC","maxItems": 50,"maxPages": 5}
3. Full product detail + reviews for pasted product links:
{"mode": "url","urls": ["https://www.dm.de/p/d/1234567/sample-bodylotion-aloe-vera"],"fetchDetails": true,"fetchReviews": true,"maxReviewsPerProduct": 0}
4. Paste a listing link and keep paginating forward:
{"mode": "url","urls": ["https://www.dm.de/search?query=bodylotion"],"maxItems": 100,"maxPages": 10}
Input Parameters
| Parameter | Type | Description |
|---|---|---|
mode | string | search or url. |
searchTerm | string | Keyword or a real dm.de category path (search mode only). |
specialsOnly | boolean | Keep only products currently in dm's Ausverkauf/clearance feed (search mode only). |
brands | array | Exact brand names to keep (search mode only). |
minRating | integer | 1-5, minimum rating to keep (search mode only). |
minPrice / maxPrice | number | EUR price band to keep (search mode only). |
sortBy | string | RELEVANCE, PRICE_ASC, PRICE_DESC, NEWEST, or RATING (search mode only). |
urls | array | Product or listing URLs to scrape (url mode only). |
fetchDetails | boolean | Fetch full product detail (breadcrumb, description, ingredients, pack-size variants, brand image, availability). Pasted product URLs always fetch detail, and are charged for it, regardless of this toggle. |
fetchReviews | boolean | Fetch full review history. Shares one enrichment charge with fetchDetails, applied at most once per product. |
maxReviewsPerProduct | integer | Cap on reviews per product; 0 = all. |
maxPages | integer | Cap on result pages per search/category/URL entry; 0 (default) = unlimited -- the run stops at Max products total instead. |
maxItems | integer | Cap on total products returned; 0 = unlimited. |
resumeFromRunId | string | Continue one specific previous run/dataset: products already collected there are skipped, so this run only returns new products. For recurring daily monitoring of the same search, use Incremental mode instead -- see "Resume & recurring updates" below. |
incrementalMode | boolean | Daily/recurring monitoring of this same search. First run returns everything as NEW; later runs return only NEW/UPDATED/REAPPEARED by default. See "Resume & recurring updates" below. |
stateKey | string | Incremental mode only. Name this monitoring campaign, or deliberately share state across differently-configured runs. Leave empty to derive one automatically. |
emitUnchanged | boolean | Incremental mode only. Also return products unchanged since the last run, marked UNCHANGED. Adds and bills extra rows you already have. |
emitExpired | boolean | Incremental mode only. Also return products from a previous run no longer found, marked EXPIRED, once a run has fully scanned the search (not capped, not a resume). Adds and bills extra synthetic rows. |
proxy | object | Apify proxy configuration. Works on every plan by default. |
mcpConnectors | array | Optional MCP connectors to export results into (Notion, Linear, Airtable, Apify). |
notionParentPageUrl | string | Notion connector only: page under which item pages are created. |
maxNotifyListings | integer | Cap on items exported to each connector per run. Does not affect the dataset. |
Output Example
{"productId": "1234567","sku": "1234567","ean": "4058172548821","name": "Sample Bodylotion Aloe Vera","brand": "Sample Brand","category": "Bodylotion & Hautcreme","url": "https://www.dm.de/p/d/1234567/sample-bodylotion-aloe-vera","price": 3.45,"currency": "EUR","originalPrice": 4.95,"discountAmount": 1.5,"discountPercent": 30.3,"isOnSpecial": true,"promoLabel": "Ausverkauf","unitPrice": 0.86,"unitPriceBasis": "100 ml","packSize": "400 ml","rating": 4.6,"reviewCount": 128,"onlineAvailable": true,"image": "https://www.dm.de/sample/image.png","reviews": {"averageRating": 4.6,"totalReviewCount": 128,"ratingDistribution": [{ "rating": 5, "count": 90 }, { "rating": 4, "count": 24 }],"items": [{"rating": 5,"title": "Sample title","body": "Sample review text.","author": "Sample User","date": "2026-07-12T07:31:15.000Z","isRecommended": true,"verifiedPurchaser": true}]}}
Sample shape: values above are illustrative placeholders, not from a live product.
Resume & recurring updates
There are two different things here -- pick the one that matches what you're doing:
| Need | Use |
|---|---|
| A crawl stopped and should continue | resumeFromRunId / automatic checkpoint recovery |
| Run the same search every day and receive only changes | incrementalMode |
| Keep separate daily campaigns for similar searches | distinct stateKey values |
| Run a normal full snapshot | leave both off |
Resume (resumeFromRunId) continues one specific interrupted or previous large crawl: paste a run ID or dataset ID and this run skips products already collected there, returning only the remaining new products. An automatic same-run checkpoint also protects against platform migrations/Resurrects without any input needed.
Incremental mode (incrementalMode) is for a schedule (for example, daily): the actor remembers the previous run of the same search by itself, so you never paste a run ID. The first run returns everything as NEW. Later runs return only NEW, UPDATED, and REAPPEARED products by default -- duplicates and unchanged products are suppressed (and not charged). Turn on emitUnchanged or emitExpired only when you also want those rows returned (and billed for). State is isolated per search/category/URL and Fetch full product detail/Fetch reviews setup automatically; set stateKey to name or deliberately share a monitoring campaign.
Scheduled-run example -- same search, run daily:
Day 1 (first run ever for this search):
{ "mode": "search", "searchTerm": "shampoo", "incrementalMode": true }
→ every product comes back with "changeType": "NEW".
Day 2 (the schedule fires again, identical input):
{ "mode": "search", "searchTerm": "shampoo", "incrementalMode": true }
→ products whose price/discount/Ausverkauf status/unit price/availability changed come back as "changeType": "UPDATED" with changedFields listing what changed, brand-new products come back as "changeType": "NEW", products that vanished and came back come back as "changeType": "REAPPEARED" -- and products that are still there, unchanged, are not returned at all (suppressed, not charged) unless emitUnchanged is on.
Send results into your apps (MCP connectors)
Optionally pipe results into the apps you already use through Model Context Protocol (MCP) connectors. Authorize a connector once under Apify, Settings, Integrations, then select it in the mcpConnectors field. Each connector receives a condensed, human-readable summary per product (name plus key fields), while the complete record always stays in the Apify dataset. For Notion, set notionParentPageUrl to the page the item pages should be created under, and use maxNotifyListings to cap how many items are exported per connector per run. Supported connectors: Notion, Linear, Airtable, and Apify. Leave the field empty to skip; it never changes the dataset output.
A note on plans
The default connection works on every Apify plan, including the free tier. A residential proxy connection is optional and only worth turning on for very large or sustained runs.