# Leroy Merlin France Products Scraper (`nice_dev/leroymerlin-products-scraper`) Actor

Scrape leroymerlin.fr products by keyword, category or URL: price incl. and excl. VAT, discount, seller, offers, rating, EAN, specs, reviews and stock in nearby stores. Export to JSON, CSV or Excel.

- **URL**: https://apify.com/nice_dev/leroymerlin-products-scraper.md
- **Developed by:** [Nice Dev](https://apify.com/nice_dev) (community)
- **Categories:** E-commerce, MCP servers
- **Stats:** 1 total users, 1 monthly users, 66.7% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.46 / 1,000 products

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/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

### 🛒 What is Leroy Merlin France Products Scraper?

**Leroy Merlin France Products Scraper** extracts **products from [leroymerlin.fr](https://www.leroymerlin.fr)**: **name, brand, price incl. and excl. VAT, discount, seller, rating, EAN barcode, specifications, delivery options, every marketplace offer, customer reviews and the stock in nearby stores**, for any keyword, category or URL. It works as a **Leroy Merlin API alternative**: run it on a schedule to follow prices and stock, call it from your code, or plug it into Make, Zapier or n8n.

Type a **keyword** (`perceuse`) or a **category**, or paste any leroymerlin.fr search, category or product URL, click **Start**, and download the products in JSON, CSV or Excel. No login, nothing to set up. On the Apify platform, **1,000 products take under 2 minutes**, and 200 products with their full product page about 6 minutes.

### 📋 What data can you extract from Leroy Merlin?

One item per product, 52 fields:

| Category | What you get |
| --- | --- |
| 🏷️ **Product** | name, brand, link, Leroy Merlin reference — `Perceuse à percussion filaire DEXTER Dp5, 900 W` |
| 💰 **Price** | price with and without VAT, crossed-out price, discount in € and %, quantity offer, and for products sold by the carton, roll or bundle (tiles, insulation, flooring) the unit sold, the price per m² or per litre the site shows beside the price (tiles, paint) and the size of one carton — `40.9 EUR`, `35.53 EUR / Carton`, `32.9 EUR / m²` |
| ⭐ **Rating** | average stars and number of reviews — `4.4 / 5, 755 reviews` |
| 🏪 **Seller** | sold by Leroy Merlin or a marketplace seller, seller name, number of sellers |
| 🔖 **Badges** | sponsored, refurbished, new, place in the results |
| 📐 **Product page** | EAN barcode, full description, specifications table, category path, all photos, warranty, installation service, kit |
| 🚚 **Delivery** | home delivery, relay point, store pickup: price, stock and delay of each |
| 🛍️ **All offers** | every seller of the product with its price incl. and excl. VAT |
| 💬 **Reviews** | author nickname and profile, stars, title, text, date, how long the product was used, recommended, helpful votes, photos, testers club, original site |
| 📍 **Store stock** | units in stock in up to 10 stores nearest to each city or postcode you give, with store name, city and address |

Every field, with an example, is listed in the **Output** section below.

The product page fields are filled when **Extract product details** is on; the reviews with **Include customer reviews**; the store stock with **Include stock in nearby stores**. All three are off by default: the list alone already gives name, brand, prices, discount, seller, rating and badges.

### ✅ Why use Leroy Merlin France Products Scraper?

- 📍 **Stock in the stores near you**: the units on the shelf in up to 10 nearest stores of each city or postcode, for every product — no other Leroy Merlin scraper on the Store gives it.
- 💬 **Every review, not just the rating**: text, title, stars, usage duration, recommended or not, helpful votes, photos, including the reviews written on the Spanish, Italian and Portuguese sites, translated into French (or French reviews only).
- 🛍️ **The whole marketplace**: every seller of a product with its price, and who sells it (Leroy Merlin, marketplace or both).
- 🎯 **Filters of the site in the form**: price range, minimum rating, brands, seller, discounted only, sponsored products out, keywords to exclude, and the sort (cheapest first, most expensive first).
- 🧩 **Nothing missed, nothing twice**: pagination is automatic, a product found by several searches (or shown both as sponsored and in the results) is returned once.
- 🔔 **Monitoring built in**: tick **Only new products**, schedule the Actor, and each run returns (and charges) only the products it has never delivered before.
- 🔌 API, scheduling, monitoring, integrations (Make, Zapier, n8n, Google Sheets…), proxy rotation and JSON/CSV/Excel export via the Apify platform.

### 🚀 How to scrape Leroy Merlin

1. Create a free Apify account.
2. Open **Leroy Merlin France Products Scraper** and type a **Search keyword** (e.g. `perceuse`), or a category path in **Categories** (e.g. `outillage/outillage-electroportatif/perceuse`).
3. Or paste your own leroymerlin.fr URLs into **Start URLs**: any search or category page (filters and sort set on the site are kept, pagination is automatic) or single product pages.
4. Set **Max products** (100 by default, 0 = no limit), tick the extras you need (product details, reviews, store stock), then click **Start**.
5. Download the dataset in JSON, CSV, Excel or via API.

### 💰 How much does it cost to scrape Leroy Merlin?

This Actor uses **pay per event** pricing: **$4.49 per 1,000 products**, plus **$0.01 per GB of memory at each run start** (2 GB by default). You pay for what is saved, each extra only when you tick it. Higher Apify plans pay less (prices per 1,000, Free → Bronze → Silver → Gold):

| What you pay for | Event | Price per 1,000 |
| --- | --- | --- |
| Product saved (list data: name, brand, prices, seller, rating, badges) | `apify-default-dataset-item` | $4.49 → $4.48 → $4.47 → $4.46 |
| Product page of a product saved, on top of the product (**Extract product details**, or a product URL pasted in **Start URLs**) | `product-details` | $0.99 → $0.98 → $0.97 → $0.96 |
| Customer review saved (**Include customer reviews**) | `review` | $0.49 → $0.46 → $0.39 → $0.36 |
| Product × store area saved with its store stock (**Include stock in nearby stores**) | `store-stock` | $0.99 → $0.98 → $0.97 → $0.96 |
| Product read but dropped by a filter: the ones the site does not offer (seller = marketplace, discounted only, excluded keywords; the sponsored products left out are free), and every filter of the form on a URL pasted in **Start URLs** | `filter-check` | $0.073 → $0.063 → $0.053 → $0.043 |
| Run start (counted once per GB of memory, 2 GB by default) | `apify-actor-start` | $0.01 per GB |

Examples on the Free plan: **100 products** without options ≈ **$0.47** ($0.45 for the products, $0.02 for the start); **5,000 products with their product page** ≈ **$27.42**; a **daily price and stock watch** of 200 products in one store area ≈ **$1.12 a day** (200 × ($4.49 + $0.99) / 1,000 + $0.02).

Filtering never costs more than taking everything: a product a filter drops costs only the filter fee, lower than a saved product. Platform usage (compute, proxy) is included in the price.

### ⚙️ Input

```json
{
    "query": "perceuse",
    "maxItems": 200,
    "sortBy": "priceLowToHigh",
    "minRating": "4",
    "extractDetails": true
}
```

Several searches and categories, a cap per search, reviews and the stock in two cities, only the products not delivered before:

```json
{
    "searchQueries": ["meuleuse sans fil", "scie sauteuse"],
    "categoryPaths": ["outillage/outillage-electroportatif/perceuse"],
    "maxItemsPerQuery": 50,
    "includeReviews": true,
    "maxReviewsPerProduct": 10,
    "includeStoreStock": true,
    "storeLocation": "Lyon",
    "storeLocations": ["33000"],
    "onlyNew": true,
    "stateKey": "outillage-lyon"
}
```

Or with your own URLs:

```json
{
    "startUrls": [
        { "url": "https://www.leroymerlin.fr/produits/outillage/outillage-electroportatif/perceuse/" },
        { "url": "https://www.leroymerlin.fr/produits/perceuse-a-percussion-filaire-dexter-dp5-900-w-82550081.html" }
    ],
    "maxItems": 500
}
```

| Field | Notes |
| --- | --- |
| `query`, `searchQueries` | Free-text search, exactly as typed on the site (`perceuse`, `meuleuse sans fil`); `searchQueries` adds more keywords, one search each (max 100 keywords per run). A keyword that matches a whole category returns that category, as on the site. |
| `categoryPaths` | Whole categories: the path of a category URL (`outillage/outillage-electroportatif/perceuse`) or the full URL (max 100 categories per run). |
| `startUrls` | Search or category pages (filters and sort kept, pagination automatic) or product pages. A product URL needs its name part, as copied from the site. |
| `maxItems` | Stop after this many products for the whole run (`0` = unlimited). |
| `maxItemsPerQuery` | Cap for EACH search (keyword, category or URL). `0` = no per-search cap. |
| `sortBy` | `relevance`, `priceLowToHigh` or `priceHighToLow`. With the caps above it decides WHICH products you get (e.g. the 100 cheapest). |
| `extractDetails` | Open each product page for the EAN, description, specifications, category path, all photos, warranty, deliveries and every offer. |
| `maxReviewsPerProduct`, `includeReviews` | Customer reviews of each product, and how many (`0` = all). |
| `reviewsSort` | Which reviews come first: `newest` or `lowestRating`. |
| `reviewsPostedAfter`, `reviewsFrenchOnly` | Only the reviews posted after a date (`2026-09-01`, `7 days`, `1 month`); French site only, or also the translated reviews of the sister sites. |
| `includeStoreStock`, `storeLocation`, `storeLocations` | Stock in up to 10 stores nearest to each city or postcode (`Lyon`, `33000`); a store near two places is listed once. Max 20 places in `storeLocations`, plus `storeLocation`. |
| `minPrice`, `maxPrice` | Price range in euros, VAT included. |
| `minRating` | Minimum stars: `3` or `4`. |
| `filterBrands` | Only these brands, as written on the site (`MAKITA`, `DEWALT`; case ignored). |
| `sellerFilter` | `all`, `leroyMerlin` (sold by Leroy Merlin) or `marketplace` (marketplace sellers only). |
| `onPromotionOnly`, `excludeSponsored`, `excludeKeywords` | Discounted products only (at least 1% off); drop the sponsored products; drop the products whose name or brand contains one of these words. |
| `onlyNew`, `stateKey`, `resetState` | Monitoring: only the products never delivered under this memory key (`new-drills`); `resetState` forgets the memory. |
| `proxyConfiguration`, `maxConcurrency`, `maxRequestsPerMinute`, `minRequestIntervalMs`, `maxRequestRetries`, `debugLog` | Advanced: the Apify proxy is the default and is included in the price (the residential proxy is not available); parallel requests, request budget per minute, smallest gap between requests, retries, debug messages. |

### 📦 Output

A product with **Extract product details** on, from a real product page (lists shortened to their first entries):

```json
{
    "id": "82550081",
    "url": "https://www.leroymerlin.fr/produits/perceuse-a-percussion-filaire-dexter-dp5-900-w-82550081.html",
    "name": "Perceuse à percussion filaire DEXTER Dp5, 900 W",
    "brand": "DEXTER",
    "sku": "82550081",
    "price": 40.9,
    "priceExclTax": 34.08,
    "currency": "EUR",
    "priceUnit": null,
    "referencePrice": null,
    "referencePriceUnit": null,
    "packageQuantity": null,
    "packageQuantityUnit": null,
    "originalPrice": null,
    "discountAmount": null,
    "discountRate": null,
    "promotionLabel": null,
    "rating": 4.4,
    "reviewCount": 755,
    "sellerName": "Leroy Merlin",
    "sellerType": "1P",
    "sellersComposition": "1P_only",
    "totalOfferCount": 1,
    "isSponsored": false,
    "offerType": "NEW",
    "reconditioned": false,
    "reconditionedState": null,
    "marketingTags": [],
    "searchTerm": "perceuse",
    "gtin": "3276007097771",
    "description": "Grâce à cette perceuse à percussion DEXTER 900W…",
    "breadcrumbs": ["Outillage", "Outillage électroportatif", "Perceuse", "Perceuse filaire"],
    "category": "Perceuse filaire",
    "categoryId": "39328",
    "images": ["https://media.adeo.com/media/3782009/media.png?width=640"],
    "specs": { "Type de mandrin": "Autoserrage sans clé", "Longueur du câble (en m)": "3" },
    "warranty": "garantie commerciale du fabricant de 5 ans",
    "availability": "OnlineOnly",
    "inStock": true,
    "deliveries": [
        { "type": "storeDelivery", "price": 0, "stock": 4, "stockStatus": "ONSITE", "delay": "2 HOUR" },
        { "type": "homeDelivery", "price": 4.9, "stock": 0, "stockStatus": "ONORDER", "delay": "10 OPENING_DAY" }
    ],
    "otherOffers": [
        { "sellerName": "Leroy Merlin", "sellerType": "1P", "sellerUrl": null, "price": 40.9, "priceExclTax": 34.08 }
    ],
    "photosCount": 13,
    "videosCount": 1,
    "isBundle": false,
    "hasInstallationService": false,
    "reviews": [],
    "storeStock": [],
    "scrapedAt": "2026-10-15T12:00:00.000Z"
}
```

You can download the dataset in various formats such as JSON, HTML, CSV or Excel.

#### All 52 fields

| Fields | What you get |
| --- | --- |
| `id`, `sku`, `url`, `name`, `brand` | **Product**: `82550081`, product page URL, `Perceuse à percussion filaire DEXTER Dp5, 900 W`, `DEXTER` |
| `price`, `priceExclTax`, `currency` | **Price**: `40.9`, `34.08`, `EUR` (main offer) |
| `priceUnit`, `referencePrice`, `referencePriceUnit` | **Unit of the price**: `Carton` (or `Rouleau`, `Botte`) for a product sold by the carton, roll or bundle, then the price per reference unit the site shows with it, `32.9` per `m²`; a pot of paint has no unit and `3.33` per `Litre`; all `null` when the site shows a single price |
| `packageQuantity`, `packageQuantityUnit` | **Size of one unit sold**: `1.08` `m²` per carton. Needs **Extract product details**; `null` otherwise |
| `originalPrice`, `discountAmount`, `discountRate`, `promotionLabel` | crossed-out price, discount in € and %, quantity offer (`-10% pour 10 achetés`); `null` without a discount |
| `rating`, `reviewCount` | **Rating**: `4.4`, `755` |
| `imageUrl` | main photo of the product |
| `sellerId`, `sellerName`, `sellerType` | **Seller** of the main offer: `Leroy Merlin`, `1P` (Leroy Merlin), `3P` (marketplace) or `2P` (marketplace seller, order shipped by Leroy Merlin) |
| `sellersComposition`, `totalOfferCount` | who sells it across all offers (`1P_only`, `3P_only` or both), number of sellers |
| `isSponsored`, `offerType`, `reconditioned`, `reconditionedState`, `marketingTags` | **Badges**: sponsored, `NEW` or refurbished type, refurbished state, card badges (`NEW`) |
| `position`, `searchTerm`, `searchUrl` | place in the results (1 = first), the keyword / category / URL that returned the product and its first page |
| `gtin`, `description`, `specs` | **Product page**: EAN barcode `3276007097771`, full description, specifications label → value |
| `breadcrumbs`, `category`, `categoryId` | category path, deepest category `Perceuse filaire` and its id |
| `images`, `photosCount`, `videosCount` | every photo URL (the main one only without the product page), number of photos and videos |
| `warranty`, `isBundle`, `hasInstallationService` | manufacturer warranty, sold as a kit, installation offered by Leroy Merlin |
| `availability`, `inStock`, `deliveries` | **Delivery**: availability as the page says it (`InStock`, `OutOfStock`, `OnlineOnly`…); can be bought now (from that availability, else a mode with stock); each mode (`homeDelivery`, `storeDelivery`…) with price, stock, status, delay |
| `otherOffers` | **All offers**: each seller with its name, type, page, price incl. and excl. VAT |
| `reviews` | **Reviews**: `id`, `author`, `authorProfile`, `rating`, `publishedAt`, `title`, `text`, `translated`, `usageDuration`, `usageSince`, `recommended`, `isClubTester`, `helpfulYes`, `helpfulNo`, `photos`, `sourceSite` |
| `storeStock` | **Store stock**: `storeId`, `storeName`, `city`, `address`, `stock` (units, `0` when unavailable), `area` (the city or postcode you gave) |
| `scrapedAt` | ISO timestamp of the extraction |

### 💡 Tips

#### How to get more results

Leroy Merlin shows at most 2,000 products per category, and only the first pages of a keyword search (about 500 products). For more, search by **category** rather than by keyword, and split a big category into its sub-categories (or the same category with different price ranges) as separate searches: a product found twice is saved once. When a search holds more than the site lets you reach, the log says so, with the numbers. Set `maxItems` to `0` to take everything.

#### How to reduce costs

The price is per product saved, plus each extra you tick. The levers are `maxItems`, `maxItemsPerQuery`, the filters of the form (on keyword and category searches, price, rating, brands and Leroy Merlin as seller are applied by the site itself: the products left out are never read; on a URL you paste, set those filters on the site before copying it) and `onlyNew` for recurring runs (you never pay twice for the same product). Tick **Extract product details**, reviews or store stock only when you need them, and cap the reviews with `maxReviewsPerProduct`.

#### Follow prices and stock in your stores

Schedule the Actor on a category with **Include stock in nearby stores** and your city or postcode in **Store area**: each run gives the price and the units on the shelf in up to 10 nearest stores. Add up to 20 places in **More store areas** to cover a region; a store close to two places is listed once, under the first place.

#### Several searches in one run

Fill `searchQueries` and / or `categoryPaths`: the Actor runs one search per keyword and per category (up to 100 of each per run). The single `query` field still works and is added to the list. A product found by several searches is saved — and charged — once. Set `maxItemsPerQuery` to give every search its own cap: without it the first searches can use up the whole `maxItems` budget. You can also paste several search or category URLs into `startUrls`: each one is a search of its own, with the same cap.

#### Monitoring: only the new products

Tick **Only new products** (`onlyNew`) and schedule the Actor. The first run returns everything; each later run skips the products already delivered: they are not saved, not charged, and their page is not even opened. The memory lives in a named key-value store of your account (`leroymerlin-products-scraper-seen`, up to 150,000 products per key) and is only updated with products that really reached the dataset, so a failed run never hides anything. Give each schedule its own `stateKey` (two schedules sharing a key would hide each other's products), and tick `resetState` once to start over.

### 🔌 Integrations and API

Call the Actor via the Apify API, the JavaScript or Python clients, or connect it with integrations and webhooks (Make, Zapier, n8n, Google Sheets, Slack, Airtable…). The dataset can be fetched as JSON or CSV from any tool.

### 🤖 Use with AI agents (MCP)

AI agents (Claude, ChatGPT, Cursor…) can find and run this Actor through the [Apify MCP server](https://mcp.apify.com), billed to their Apify account like any run. It returns one item per leroymerlin.fr product. Actor id: `nice_dev/leroymerlin-products-scraper`; MCP server with this Actor only: `https://mcp.apify.com/?tools=fetch-actor-details,nice_dev/leroymerlin-products-scraper`.

Smallest input, for a cheap first call:

```json
{
    "query": "perceuse",
    "maxItems": 10
}
```

Key output fields: `url`, `name`, `brand`, `price`, `priceExclTax`, `rating`, `sellerType`, and with the extras `gtin`, `specs`, `reviews`, `storeStock`.

Cost: pay per event, see the **Pricing** tab. Cap each call with `maxItems` and, through the API, with the run option `maxTotalChargeUsd`.

### ❓ FAQ

#### Is it legal to scrape Leroy Merlin?

The Actor only reads what leroymerlin.fr shows publicly to any anonymous visitor. It logs in to nothing. Products, sellers and stores are business data; customer reviews carry the nickname the author chose and may contain personal data, which is protected by GDPR: do not store it without a legitimate reason. You are responsible for using the data in compliance with Leroy Merlin's Terms of Use and applicable law. This Actor is not affiliated with Leroy Merlin.

#### Does it need a login or a proxy?

No login. The proxy is included in the price: leave the default setting (the residential proxy is not available). A request the site turns away is retried at once on a new proxy session, up to 10 times on top of the retries (without a proxy, after a pause of 5 seconds, doubled at each retry up to 150 seconds).

#### Is the data safe to open in Excel or to show on a web page?

Names, descriptions and reviews are the site's and the customers' own words, copied as they are. A text can begin with `-`, `+`, `=` or `@` (a review such as `+1 pour la batterie`): Excel and Google Sheets may read such a cell of a CSV file as a formula or as a number. The Actor leaves the text as it is, so that the JSON and the API give the real value: when you open a CSV, import these columns as text. On a web page, escape every field like any text written by a stranger.

#### Known limitations

- Leroy Merlin shows at most 2,000 products per category and about 500 per keyword search: use categories and split them (see Tips).
- A product URL must keep its name part (`/produits/perceuse-…-82550081.html`): the bare number gives no page on the site and is refused with a clear message.
- The product page fields are `null` / empty without **Extract product details** (a product URL pasted in **Start URLs** is always read with its page); `images` then holds the main photo only.
- A product whose page is gone while the run reads it keeps its list data, without the product page fields; the run summary counts these.
- The store stock covers up to 10 stores nearest to each place, as the site gives them (9 around Bordeaux); `stock` is `0` when the store says the product is unavailable, `null` when it shows something else.
- Reviews from the Spanish, Italian and Portuguese sites come in the French translation Leroy Merlin gives (`translated: true`).
- `onlyNew` remembers product ids, not their content: a product whose price changed is not returned again.
- Two runs sharing the same `stateKey` at the same time may both return the same new product.

**A run that reaches its timeout** stops itself about 45 seconds before it: no new page is asked, what it read is saved and, with `onlyNew`, remembered, and the run ends *Succeeded* with "Stopped before the run's timeout". Resurrect it to go on from there, or give the next run a longer timeout (Run options).

**A run the platform stops without warning** (out of memory)

- Resurrect it: it goes on from where it stood at most a minute before the stop. What it had read since is read again, and the products already saved are skipped: none is delivered or charged twice, and `maxItems` still counts them.
- With `onlyNew`, the memory is saved after each write (at most once every 2 seconds): resurrect the stopped run and the products it had saved meanwhile join the memory; leave it stopped for good, and the next run may return the products of its last 2 seconds once more.

#### Something doesn't work?

The last line of the log counts the products saved and filtered out, the product pages gone, the reviews pages and store stock requests that could not be read, and the requests that failed after every retry. Those requests are listed, with the reason, in the `FAILED_REQUESTS` record of the run's key-value store. A run that saved nothing and had failed requests fails, and its last message gives the cause.

If Leroy Merlin changes its pages, you are told instead of paying for blank rows. If the first 20 products read all lack a field that every product has (the name, the brand, the price with and without VAT, the seller, the type of offer, the picture, and with product details the category path, the category, the availability or the number of photos), the run stops and fails, and its last message names the missing field: at most those first products are charged. A run of fewer products (a scheduled `onlyNew` run that finds a few new ones, a small **Max products**) is checked at its end, from 5 products: its products are already saved, and the run fails with the missing field instead of ending green.

### 🛟 Support

Open an issue in the **Issues** tab with a link to your run: the run log and the `FAILED_REQUESTS` record of the key-value store show exactly which URLs failed and why.

# Actor input Schema

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

leroymerlin.fr search URLs (`https://www.leroymerlin.fr/search?q=…`), category URLs (`https://www.leroymerlin.fr/produits/outillage/outillage-electroportatif/perceuse/`, filters and sort set on the site are kept, pagination is automatic) or product URLs (`https://www.leroymerlin.fr/produits/perceuse-a-percussion-filaire-dexter-dp5-900-w-82550081.html`). When this list is not empty, the keyword and category fields below are ignored; filters, caps and monitoring still apply. Max 1 000 URLs.

## `query` (type: `string`):

Free-text search, exactly as typed in the leroymerlin.fr search box (e.g. `perceuse`, `meuleuse sans fil`). A keyword that matches a whole category (e.g. `perceuse`) returns that category, as on the site.

## `searchQueries` (type: `array`):

Several keywords in one run: one search per keyword. Added to **Search keyword**; products found by several searches are saved once.

## `categoryPaths` (type: `array`):

Whole categories: the path after `/produits/` in a leroymerlin.fr category URL (`outillage/outillage-electroportatif/perceuse`) or the full URL. Leroy Merlin shows at most 2 000 products per list: for a bigger category, add its sub-categories or a brand / price filter.

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

Maximum number of products to save for the whole run (after deduplication and filters). 0 = no limit.

## `maxItemsPerQuery` (type: `integer`):

Cap for EACH search (keyword, category or URL), so that the first search cannot use up the whole **Max products** budget. 0 = no per-search cap.

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

Order of the results, as on the site. Combined with the caps above it decides WHICH products you get (e.g. the 100 cheapest).

## `extractDetails` (type: `boolean`):

Open each product page (1 extra request per product) for the EAN / GTIN, full description, specifications table, breadcrumb, all photos, warranty, delivery options with stock and delay, and every marketplace offer (seller, price). Charged per product delivered with its page (see Pricing). Off = the fields of the results page only.

## `includeReviews` (type: `boolean`):

Add the customer reviews of each product: rating, text, date, how long the product was used, recommended or not, helpful votes, photos, original site (reviews from Leroy Merlin Spain, Italy or Portugal come translated into French). About 8 reviews per extra request. Charged per review delivered (see Pricing).

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

Reviews to add per product. 0 = all of them.

## `reviewsSort` (type: `string`):

Which reviews come first (and therefore which ones you get under the cap above).

## `reviewsFrenchOnly` (type: `boolean`):

Keep only the reviews written on leroymerlin.fr (off = also the reviews of the same product on the Spanish, Italian and Portuguese sites, translated by Leroy Merlin).

## `reviewsPostedAfter` (type: `string`):

Only reviews published on or after this date: `2026-09-01`, or a period before now such as `7 days`, `1 month`.

## `includeStoreStock` (type: `boolean`):

For each product, add the stock of up to 10 Leroy Merlin stores closest to each place below (store id, name, city, address, units in stock). 1 extra light request per product and place. Charged per product and place (see Pricing).

## `storeLocation` (type: `string`):

City or postcode around which the store stock is read (e.g. `Lyon`, `33000`). Used only with **Include stock in nearby stores**.

## `storeLocations` (type: `array`):

Several cities or postcodes: each product gets the stock of the stores around each of them (a store near two places is listed once). Added to **Store area**. Max 20.

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

Only products priced at or above this amount, VAT included.

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

Only products priced at or below this amount, VAT included.

## `minRating` (type: `string`):

Only products rated at least this many stars.

## `filterBrands` (type: `array`):

Keep only these brands, as written on leroymerlin.fr (e.g. `MAKITA`, `DEWALT`, `DEXTER`; case ignored).

## `sellerFilter` (type: `string`):

Products sold by Leroy Merlin itself, by marketplace sellers only, or both.

## `onPromotionOnly` (type: `boolean`):

Only products shown with a crossed-out price or a discount of at least 1%.

## `excludeSponsored` (type: `boolean`):

Drop the sponsored products that Leroy Merlin inserts in the results.

## `excludeKeywords` (type: `array`):

Drop the products whose name or brand contains one of these words (case and accents ignored).

## `onlyNew` (type: `boolean`):

Skip the products that a previous run (same **Memory key**) already delivered: they are not saved and not charged, and their page is not even opened. First run = everything is new.

## `stateKey` (type: `string`):

Name of the memory used by **Only new products**. Give each schedule / task its own key (e.g. `new-drills`) so that they do not share their memory. Letters, digits, `-` and `_`.

## `resetState` (type: `boolean`):

Forget everything remembered under this **Memory key** before the run: this run returns (and charges) every product again. Untick it afterwards.

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

Apify Proxy or your own proxies. Keep the default: it is included in the price. The residential Apify proxy is not available in this Actor.

## `maxConcurrency` (type: `integer`):

Maximum number of requests processed in parallel.

## `maxRequestsPerMinute` (type: `integer`):

Most requests to the site in any 60 seconds, all of them counted: results pages, product pages, reviews and store stock. A budget, not an even pace: up to this many can leave at once when the minute starts (spread them with the minimum delay below). Lower it if the site answers HTTP 429 / 403 in the log.

## `minRequestIntervalMs` (type: `integer`):

Smallest gap between two requests, in milliseconds. Unlike the per-minute rate, this spreads the requests evenly instead of letting them go out in a burst. 0 = no gap.

## `maxRequestRetries` (type: `integer`):

Retries per request before it is marked as failed. Behind a proxy, a request the site turns away is also retried on a new proxy session up to 10 times without using up these retries.

## `debugLog` (type: `boolean`):

Include debug messages in the run log.

## Actor input object example

```json
{
  "startUrls": [],
  "query": "meuleuse sans fil brushless",
  "searchQueries": [],
  "categoryPaths": [],
  "maxItems": 100,
  "maxItemsPerQuery": 0,
  "sortBy": "relevance",
  "extractDetails": false,
  "includeReviews": false,
  "maxReviewsPerProduct": 20,
  "reviewsSort": "newest",
  "reviewsFrenchOnly": false,
  "includeStoreStock": false,
  "storeLocation": "Lyon",
  "storeLocations": [],
  "minRating": "",
  "filterBrands": [],
  "sellerFilter": "all",
  "onPromotionOnly": false,
  "excludeSponsored": false,
  "excludeKeywords": [],
  "onlyNew": false,
  "stateKey": "default",
  "resetState": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "maxConcurrency": 8,
  "maxRequestsPerMinute": 120,
  "minRequestIntervalMs": 0,
  "maxRequestRetries": 5,
  "debugLog": false
}
```

# Actor output Schema

## `results` (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 = {
    "query": "meuleuse sans fil brushless",
    "storeLocation": "Lyon",
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("nice_dev/leroymerlin-products-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 = {
    "query": "meuleuse sans fil brushless",
    "storeLocation": "Lyon",
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("nice_dev/leroymerlin-products-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 '{
  "query": "meuleuse sans fil brushless",
  "storeLocation": "Lyon",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call nice_dev/leroymerlin-products-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,nice_dev/leroymerlin-products-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/pUrpTDCphE6Qefojg/builds/pOG72oAgqgdtWNVfM/openapi.json
