# HORNBACH Products, Prices & Reviews Scraper (`abotapi/hornbach-de-scraper`) Actor

Scrape HORNBACH (hornbach.de) DIY, building & garden products: current + strike-through price with discount, per-unit pricing (m2/kg/piece), full technical specs, category path, online & in-store availability, and complete product reviews with rating breakdown.

- **URL**: https://apify.com/abotapi/hornbach-de-scraper.md
- **Developed by:** [Abot API](https://apify.com/abotapi) (community)
- **Categories:** E-commerce, Developer tools, Automation
- **Stats:** 1 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 product results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## HORNBACH Product Scraper

Pull rich product data from HORNBACH (hornbach.de), the German DIY, building materials and garden retailer. Search by keyword and/or category with real site filters, or paste product, search, and category links directly. Every record includes current price, the manufacturer's suggested retail price (UVP) with computed discount when a product is marked down, per-unit pricing (per square metre, per kilogram, per piece) where the site shows it, online and in-store availability, and a full customer review history with rating breakdown.

### Why This Scraper?

- **Was-price and discount, structured, not scraped from a badge.** When a product carries a manufacturer's suggested price alongside HORNBACH's own price, both are captured plus the computed discount amount and percentage.
- **Unit pricing for building materials.** Tiles, flooring, and similar bulk goods return their per-square-metre (or per-kilogram, per-piece) selling price alongside the pack price, exactly as shown on the product page.
- **Full technical specifications, not a wall of text.** Every category's characteristics table (dimensions, voltage, material, capacity, and dozens more per category) comes back as clean key/value pairs, plus the full breadcrumb category path.
- **Complete 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.
- **Online and in-store availability.** Home delivery status alongside exact in-store stock text, aisle location, and seller information (HORNBACH itself or a marketplace partner).
- **Three ways in.** Keyword search, category browse, or paste any product/search/category URL and let pagination continue automatically.
- **Optional export to your apps.** Send results into Notion, Linear, Airtable, or any Apify MCP connector alongside the dataset.

A note on specials: HORNBACH does not run a distinct "deals" or "clearance" collection the way some retailers do; its pricing model is everyday low prices rather than flash sales. There is no dedicated specials category to select, so this actor does not include one. The manufacturer's-price/discount field above still surfaces every individual product that happens to be marked down.

### Data You Get

| Field | Example value |
|---|---|
| productId / sku | `12314733` |
| name | `Sample Cordless Drill Driver, incl. 2 batteries and charger` |
| brand | `Sample Tools Professional` |
| url | `https://www.hornbach.de/p/sample-product-name/12314733/` |
| price / currency / priceUnit | `199.0`, `EUR`, `ST` |
| originalPrice | `249.0` |
| discountAmount / discountPercent | `50.0`, `20.1` |
| packPrice / packPriceUnit | `14.93`, `Pack` |
| rating / reviewCount | `4.9`, `11` |
| badges | `["New in range"]` |
| onlineAvailable / onlineAvailabilityText | `true`, `Delivery in about 3 working days` |
| storePickupAvailable / storeStockText | `true`, `20 pcs in stock at the store` |
| onlineMerchant / storeMerchant | `{"name": "Sample Retailer", "isMarketplaceMerchant": false}` |
| categoryPath | `[{"name": "Home", "url": "..."}, {"name": "Machines, Tools & Workshop", "url": "..."}]` |
| ean | `4000000000000` |
| specifications | `{"Drive type": "Battery", "Voltage": "18 V", "Weight incl. battery": "2.1 kg", ...}` |
| images | `[{"url": "https://media.hornbach.de/...", "alt": "Sample product image"}]` |
| variantGroupList | `[]` (populated when the product offers colour/size variants) |
| datasheets | `[{"title": "Technical data sheet", "url": "https://..."}]` |
| reviews.averageRating / reviews.totalReviewCount | `4.9`, `11` |
| reviews.ratingDistribution | `[{"rating": 5, "count": 10}, {"rating": 4, "count": 1}]` |
| reviews.items\[] | `[{"rating": 5, "title": "Sample title", "body": "Sample review text.", "author": "Sample User", "date": "2026-05-01T00:00:00.000Z"}]` |

> Sample shape: values above are illustrative placeholders, not from a live product.

### How to Use

**Basic keyword search**

```json
{
  "mode": "search",
  "searchTerm": "akkuschrauber",
  "maxItems": 20
}
```

**Search narrowed by category, brand and price**

```json
{
  "mode": "search",
  "searchTerm": "akkuschrauber",
  "category": "S1937",
  "brands": ["Bosch Professional"],
  "minPrice": 50,
  "maxPrice": 250,
  "sortBy": "PRICE_ASC",
  "maxItems": 30
}
```

**Category browse with full detail and review enrichment**

```json
{
  "mode": "search",
  "category": "S594",
  "fetchDetails": true,
  "fetchReviews": true,
  "maxReviewsPerProduct": 0,
  "maxItems": 50
}
```

**Paste product, search, or category links**

```json
{
  "mode": "url",
  "urls": [
    "https://www.hornbach.de/p/sample-product-name/12314733/",
    "https://www.hornbach.de/s/fliesen/?page=1",
    "https://www.hornbach.de/c/baustoffe/S594/"
  ],
  "maxItems": 40
}
```

**Continue a previous run, collecting only new products**

```json
{
  "mode": "search",
  "category": "S594",
  "resumeFromRunId": "<a previous run id or dataset id>"
}
```

### Input Parameters

| Parameter | Type | Default | Description |
|---|---|---|---|
| `mode` | string | `search` | `search` (keyword/category with filters) or `url` (paste links). |
| `searchTerm` | string | - | Free-text keyword for search mode. |
| `category` | string | - | A category id, e.g. `S1937`. Narrows a keyword search or browses a whole department alone. |
| `brands` | array | `[]` | Only these brands. |
| `sellers` | array | `[]` | Only these named sellers. |
| `minPrice` / `maxPrice` | integer | - | Price range in EUR. |
| `availability` | string | \`\` (any) | `IN_STOCK`, `ONLINE`, `RESERVABLE`, or `NEW`. |
| `sortBy` | string | `SCORE` | `SCORE`, `PRICE_ASC`, `PRICE_DESC`, `RATING_DESC`, `BRAND_ASC`, `BRAND_DESC`, `TITLE_ASC`, `TITLE_DESC`. |
| `urls` | array | sample URL | Product, search, or category links for URL mode. |
| `fetchDetails` | boolean | `false` | Adds full breadcrumb path, description, flattened specifications, EAN, media gallery, variant matrix, and safety hints. |
| `fetchReviews` | boolean | `true` | Fetches each product's full review history. |
| `maxReviewsPerProduct` | integer | `20` | Cap on reviews per product; `0` = all. |
| `maxPages` | integer | `0` | Cap on result pages per search/category/URL entry; `0` (default) = unlimited, walk every page until the site's own last page or Max products total is hit. |
| `maxItems` | integer | `20` | Cap on total products; `0` = unlimited. |
| `resumeFromRunId` | string | - | Optional: a previous run id (or its dataset id). Products it already collected are skipped, so this run returns only new products (a delta). Leave empty for a normal run. |
| `proxy` | object | Apify default | Connection configuration. |
| `mcpConnectors` | array | `[]` | Optional MCP connectors to also receive results. |
| `notionParentPageUrl` | string | - | Required only when a Notion connector is selected. |
| `maxNotifyListings` | integer | `50` | Cap on items sent to each connector. |

### Output Example

```json
{
  "productId": "12314733",
  "sku": "12314733",
  "name": "Sample Cordless Drill Driver, incl. 2 batteries and charger",
  "url": "https://www.hornbach.de/p/sample-product-name/12314733/",
  "brand": "Sample Tools Professional",
  "price": 199.0,
  "currency": "EUR",
  "priceUnit": "ST",
  "originalPrice": null,
  "discountAmount": null,
  "discountPercent": null,
  "packPrice": null,
  "packPriceUnit": null,
  "badges": [],
  "rating": 4.9,
  "reviewCount": 11,
  "onlineAvailable": true,
  "onlineAvailabilityText": "Delivery in about 3 working days",
  "storePickupAvailable": true,
  "storeStockText": "20 pcs in stock at the store",
  "onlineMerchant": { "name": "Sample Retailer", "isMarketplaceMerchant": false },
  "categoryPath": [
    { "name": "Home", "url": "https://www.hornbach.de/" },
    { "name": "Machines, Tools & Workshop", "url": "https://www.hornbach.de/c/sample/S1937/" }
  ],
  "ean": "4000000000000",
  "specifications": {
    "Item type": "Screwdriver",
    "Drive type": "Battery",
    "Voltage": "18 V",
    "Weight incl. battery": "2.1 kg"
  },
  "images": [
    { "url": "https://media.hornbach.de/hb/packshot/sample.jpg", "alt": "Sample product image" }
  ],
  "reviews": {
    "averageRating": 4.9,
    "totalReviewCount": 11,
    "ratingDistribution": [
      { "rating": 5, "count": 10 },
      { "rating": 4, "count": 1 }
    ],
    "items": [
      {
        "rating": 5,
        "title": "Sample review title",
        "body": "Sample review text describing the product experience.",
        "author": "Sample User",
        "date": "2026-05-01T00:00:00.000Z",
        "isRecommended": true,
        "secondaryRatings": { "Value": 5, "Quality": 5 }
      }
    ]
  }
}
```

### Send results into your apps (MCP connectors)

Optionally pipe results into an app you already use through a Model Context Protocol connector. Authorize a connector once under your account's Integrations settings, then select it in the input; for Notion, also set the parent page. Each connector receives a condensed, human-readable summary per item (a title plus key fields), not the full JSON record; the complete data always stays in the dataset.

### A Note on Access

Running this actor requires an active account with default proxy access, which is included on every plan. No further configuration is needed for typical use.

# Actor input Schema

## `mode` (type: `string`):

'search' finds products by keyword and/or category with HORNBACH's own filters. 'url' scrapes any product, search-result, or category page URL you paste, walking pagination forward automatically.

## `searchTerm` (type: `string`):

Free-text keyword, e.g. 'akkuschrauber' or 'fliesen'. Combine with Category below to narrow a keyword search, or leave empty and set only Category to browse a whole department.

## `category` (type: `string`):

A HORNBACH category id, e.g. 'S1937' (Maschinen, Werkzeug & Werkstatt) or 'S594' (Baustoffe). Find it in a category page URL: hornbach.de/c/<name>/S<id>/. Narrows a keyword search, or browses the whole department when Search keyword is left empty.

## `brands` (type: `array`):

Only return products from these brands, e.g. 'Bosch Professional', 'Makita', 'Einhell'. Match the exact brand name as shown on the site. Leave empty for all brands.

## `sellers` (type: `array`):

Only return offers from these named sellers (HORNBACH itself plus third-party marketplace sellers), e.g. 'Rubart GmbH'. Leave empty for all sellers.

## `minPrice` (type: `integer`):

Only return products priced at or above this amount.

## `maxPrice` (type: `integer`):

Only return products priced at or below this amount.

## `availability` (type: `string`):

Narrow by stock/availability, or by new-in-range products.

## `sortBy` (type: `string`):

Order of the returned results.

## `urls` (type: `array`):

Paste one or more HORNBACH product pages (/p/.../<id>/), search-result pages (/s/<term>/), or category pages (/c/.../S<id>/). Pagination continues forward automatically from any page number already in the URL. Filter fields above are ignored in this mode.

## `fetchDetails` (type: `boolean`):

Adds the full breadcrumb category path, complete description, flattened technical specifications, EAN/GTIN, media gallery, variant matrix, safety/legal hints, and exact in-store stock text. Current price, was-price/discount, per-quantity price (e.g. per square metre) and rating are already included without this toggle.

## `fetchReviews` (type: `boolean`):

Fetch each product's full review history: overall rating, review count, rating breakdown, and per-review author/date/rating/title/body. Products with no reviews simply return an empty list.

## `maxReviewsPerProduct` (type: `integer`):

Cap on reviews fetched per product when 'Fetch reviews' is on. 0 = all available reviews.

## `maxPages` (type: `integer`):

Stop after this many result pages per search/category/URL entry. 0 (default) = unlimited, walk every page until the site's own last page or Max products total is hit.

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

Hard cap on total products returned across every search/category/URL entry. 0 = unlimited (still bounded by Max pages per search).

## `resumeFromRunId` (type: `string`):

Paste a previous run id (or its dataset id) to continue that run: products already collected there are skipped, so this run only returns new products (a delta). Leave empty for a normal run.

## `proxy` (type: `object`):

The default connection works on every Apify plan, including the free tier. A residential connection is optional and only needed for very large or sustained runs.

## `mcpConnectors` (type: `array`):

Optionally send the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. Authorize a connector once under Apify -> Settings -> Integrations, then select it here. The connector receives a condensed, human-readable summary per item (title + key fields), not the full JSON; the complete record stays in the dataset. Leave empty to skip. Supported: Notion (https://mcp.notion.com/mcp), Linear (https://mcp.linear.app/sse), Airtable (https://mcp.airtable.com/mcp), Apify (https://mcp.apify.com).

## `notionParentPageUrl` (type: `string`):

URL (or id) of the Notion page under which item pages are created. Required to enable the Notion export; ignored by other connectors.

## `maxNotifyListings` (type: `integer`):

Cap on items written to each connector per run. Does not affect the dataset.

## Actor input object example

```json
{
  "mode": "search",
  "searchTerm": "akkuschrauber",
  "brands": [],
  "sellers": [],
  "availability": "",
  "sortBy": "SCORE",
  "urls": [
    "https://www.hornbach.de/s/akkuschrauber/"
  ],
  "fetchDetails": false,
  "fetchReviews": true,
  "maxReviewsPerProduct": 20,
  "maxPages": 0,
  "maxItems": 20,
  "proxy": {
    "useApifyProxy": true
  },
  "maxNotifyListings": 50
}
```

# Actor output Schema

## `overview` (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 = {
    "mode": "search",
    "searchTerm": "akkuschrauber",
    "brands": [],
    "sellers": [],
    "urls": [
        "https://www.hornbach.de/s/akkuschrauber/"
    ],
    "proxy": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("abotapi/hornbach-de-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 = {
    "mode": "search",
    "searchTerm": "akkuschrauber",
    "brands": [],
    "sellers": [],
    "urls": ["https://www.hornbach.de/s/akkuschrauber/"],
    "proxy": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("abotapi/hornbach-de-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "mode": "search",
  "searchTerm": "akkuschrauber",
  "brands": [],
  "sellers": [],
  "urls": [
    "https://www.hornbach.de/s/akkuschrauber/"
  ],
  "proxy": {
    "useApifyProxy": true
  }
}' |
apify call abotapi/hornbach-de-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=abotapi/hornbach-de-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/4DOgQLuMmoUNiqVE7/builds/wOxgjRhDZiz22jiWo/openapi.json
