# Darty Scraper - Prices, Discounts, Specs & Reviews (`abotapi/darty-com-scraper`) Actor

Scrape Darty (darty.com) products: current price plus strike-through reference price and discount, brand, category breadcrumb, full spec sheet, and the site's own customer reviews with rating breakdown. Search by keyword or paste product/listing links.

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

## Pricing

from $1.20 / 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

## Darty Product Scraper

Pull product data from Darty (darty.com), the French consumer-electronics and appliance retailer (part of Fnac Darty). Search by keyword, or paste product and search-result links directly. Every record includes the current price, the strike-through reference price with computed discount when a product is genuinely marked down, and the site's own customer reviews.

### Why This Scraper?

- **Was-price and discount, structured, never fabricated.** When a product is marked down against darty.com's own reference price, both prices are captured plus the computed discount amount, percentage, and the site's own promo label (e.g. "Bon Plan", "Soldes"). Full-price products simply carry `null` -- nothing is guessed.
- **Real narrowing, not a filter that quietly does nothing.** Rating floor and free-delivery narrow via the site's own search facets, verified against live results; brand narrows against the site's own card-level brand field; sort reorders the full server-side result set.
- **Reviews straight from the product page.** Overall rating, total review count, recommend-percentage, per-criteria scores (e.g. ease of use, value for money), and the page's own individual reviews (author, dates, rating, title, body, purchase store) -- not a third-party review vendor.
- **Flattened spec sheet.** Full detail returns every characteristic the product page itself declares (screen size, energy class, dimensions, EAN, and whatever else that category carries) rather than a fixed field list, so nothing category-specific is silently dropped.
- **Two ways in.** Keyword search with real site filters (brand, price range, discount-only, rating, free delivery, sort), or paste any product/search-result link and continue pagination automatically.
- **Optional export to your apps.** Send results into Notion, Linear, Airtable, or any Apify MCP connector alongside the dataset.
- **Resume and recurring updates.** Turn on Incremental mode to get only NEW, UPDATED, and REAPPEARED products on every scheduled run, or resume one specific interrupted crawl with `resumeFromRunId`.

### Data You Get

| Field | Example value |
|---|---|
| productId / ean | `00000001` (Darty's own "codic"), `0000000000000` (EAN) |
| name | `TV LED Sample Brand X100 80 cm` |
| brand | `SampleBrand` |
| category / breadcrumbPath | `TV LED`, `["TV, Video, Home cinema", "Television", "TV LED"]` |
| url | `https://www.darty.com/nav/achat/.../sample-brand-x100.html` |
| price / currency | `119.99`, `EUR` |
| originalPrice / discountAmount / discountPercent | `149.99`, `30.00`, `20.0` |
| isOnSpecial / promoLabel | `true`, `Bon Plan` |
| inStock / condition / seller | `true`, `new`, `Darty` |
| image / images | `["https://image.example.com/..."]` |
| description | full product description text |
| specs | `{"Screen size (inches)": "32 \"", "Definition": "HD (720p)", "Energy class": "E"}` |
| rating / reviewCount | `4.5`, `26` |
| reviews.averageRating / totalReviewCount / recommendPercent | `4.5`, `26`, `93` |
| reviews.criteria | `[{"name": "Ease of use", "score": 4.8}]` |
| reviews.items\[] | `[{"rating": 5, "title": "Sample title", "body": "Sample review text.", "author": "JANE", "level": "confirmed", "postedDate": "01/01/2026", "purchaseDate": "20/12/2025", "purchaseStore": "DARTY SAMPLE"}]` |

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

### How to Use

**1. Keyword search, first page, with reviews (default):**

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

**2. Keyword filtered to a brand, discounted items only, 4+ stars, sorted by price:**

```json
{
  "mode": "search",
  "searchTerm": "television",
  "brands": ["Samsung"],
  "specialsOnly": true,
  "minRating": 4,
  "sortBy": "PRICE_ASC",
  "maxItems": 50,
  "maxPages": 5
}
```

**3. Full product detail (spec sheet + images) and reviews for a pasted product link:**

```json
{
  "mode": "url",
  "urls": ["https://www.darty.com/nav/achat/hifi_video/televiseurs-led/grand_ecran_led/sample-brand-x100.html"],
  "fetchDetails": true,
  "fetchReviews": true
}
```

**4. Paste a search-result link and keep paginating forward:**

```json
{
  "mode": "url",
  "urls": ["https://www.darty.com/nav/recherche?text=lave-linge"],
  "maxItems": 100,
  "maxPages": 10
}
```

### Resume and recurring updates

Two different things, both under the same input section:

- **`resumeFromRunId`** continues ONE specific interrupted crawl. Paste a previous run ID or dataset ID and this run skips every product that run already collected, picking up where it left off -- useful after a run was stopped early or hit an error partway through a large pull.
- **`incrementalMode`** is for running the SAME search or URL set again and again (daily, weekly, on a schedule) and getting only what changed. It remembers the previous run's products in a dedicated key-value store, keyed automatically from the mode, search/URL and filter settings (or your own `stateKey` if you want to name or deliberately share a campaign) -- so two differently-configured runs never mix their baselines. Every product is classified as `changeType`: `NEW` (first time seen), `UPDATED` (a comparable field changed -- see `changedFields`), `REAPPEARED` (was gone, is back), or, with `emitExpired` on and only once a run has fully scanned every tracked search/URL, `EXPIRED` (was tracked, no longer found). `UNCHANGED` products are tracked but not returned unless `emitUnchanged` is on -- both extra-emission toggles are off by default because they return, and bill, rows you already have. The state key folds in `fetchReviews` alongside the search filters (brands, minPrice, maxPrice, specialsOnly, minRating, freeDeliveryOnly, sortBy), since turning it on or off changes the emitted record shape.
- `incrementalMode` defaults to `false` -- a normal run's behavior and output shape are unchanged until you opt in. Combining it with `resumeFromRunId` bootstraps a brand-new monitoring campaign from that resumed crawl's results; if a baseline already exists for the derived (or given) state key, the run fails fast rather than silently mixing the two.

### Input Parameters

| Parameter | Type | Description |
|---|---|---|
| `mode` | string | `search` or `url`. |
| `searchTerm` | string | Keyword (search mode only; uses darty.com's own product search). |
| `brands` | array | Exact brand names to keep (search mode only). |
| `minPrice` / `maxPrice` | number | EUR price band to keep (search mode only). |
| `specialsOnly` | boolean | Keep only products currently showing a genuine discount against darty.com's own reference price (search mode only). |
| `minRating` | integer | Minimum star rating (1-5), narrowed server-side (search mode only). |
| `freeDeliveryOnly` | boolean | Keep only products with free home delivery (search mode only). |
| `sortBy` | string | `RELEVANCE`, `PRICE_ASC`, `PRICE_DESC`, `RATING_DESC`, or `NEWEST`. |
| `urls` | array | Product or search-result-listing URLs to scrape (url mode only). |
| `fetchDetails` | boolean | Fetch full product detail (description, EAN, breadcrumb category, flattened spec sheet, image gallery). |
| `fetchReviews` | boolean | Fetch the product's own reviews. |
| `maxReviewsPerProduct` | integer | Cap on written reviews per product; the product page itself only ever surfaces a recent subset. |
| `maxPages` | integer | Cap on result pages per search/URL entry; empty = unlimited. |
| `maxItems` | integer | Cap on total products returned; `0` = unlimited. |
| `resumeFromRunId` | string | Previous run ID or dataset ID to continue ONE interrupted crawl without re-returning/re-charging products it already collected. See "Resume and recurring updates" above. |
| `incrementalMode` | boolean | Recurring monitoring of the same search/URL set: returns only `NEW`/`UPDATED`/`REAPPEARED` products against a remembered baseline. Default `false`. See "Resume and recurring updates" above. |
| `stateKey` | string | Incremental mode only. Name a monitoring campaign, or deliberately share state across differently-configured runs. Leave empty to derive one automatically. |
| `emitUnchanged` | boolean | Incremental mode only. Also return `UNCHANGED` products. Off by default -- returns and bills rows you already have. |
| `emitExpired` | boolean | Incremental mode only. Also return `EXPIRED` products once a run fully scans every tracked search/URL. Off by default -- returns and bills extra synthetic rows. |
| `proxy` | object | Apify proxy configuration. Leave the default as provided -- this source needs it. |
| `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

```json
{
  "productId": "00000001",
  "ean": "0000000000000",
  "url": "https://www.darty.com/nav/achat/hifi_video/televiseurs-led/grand_ecran_led/sample-brand-x100.html",
  "image": "https://image.example.com/000000000.jpg",
  "name": "TV LED Sample Brand X100 80 cm",
  "brand": "SampleBrand",
  "category": "TV LED",
  "price": 119.99,
  "currency": "EUR",
  "originalPrice": 149.99,
  "discountAmount": 30.00,
  "discountPercent": 20.0,
  "isOnSpecial": true,
  "promoLabel": "Bon Plan",
  "rating": 4.5,
  "reviewCount": 26,
  "inStock": true,
  "condition": "new",
  "seller": "Darty",
  "description": "Sample product description as shown on the site.",
  "breadcrumbPath": ["TV, Video, Home cinema", "Television", "TV LED"],
  "specs": { "Screen size (inches)": "32 \"", "Definition": "HD (720p)", "Energy class": "E" },
  "images": ["https://image.example.com/000000000.jpg"],
  "reviews": {
    "averageRating": 4.5,
    "totalReviewCount": 26,
    "recommendPercent": 93,
    "criteria": [{ "name": "Ease of use", "score": 4.8 }],
    "items": [
      { "rating": 5, "title": "Sample title", "body": "Sample review text.", "author": "JANE", "level": "confirmed", "postedDate": "01/01/2026", "purchaseDate": "20/12/2025", "purchaseStore": "DARTY SAMPLE" }
    ]
  }
}
```

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

### A note on reviews and prices

Reviews come directly from the product page itself, not a third-party review vendor -- the page only ever surfaces its own most recent written reviews, while the overall rating, review count, recommend-percentage, and per-criteria scores reflect the full history. `originalPrice` reflects darty.com's own reference price and is only ever populated when it is genuinely higher than the current price -- a full-price product always carries `null` there, never a fabricated discount.

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

Leave the proxy default as provided -- this source needs it and it may not work on every Apify plan. Change only the region if you need a different one.

# Actor input Schema

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

'search' finds products by keyword using darty.com's own search. 'url' scrapes any product or search-result-listing page URL you paste, walking pagination forward automatically.

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

Free-text keyword, e.g. 'television' or 'lave-linge'. Uses darty.com's own product search.

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

Only return products from these brands, e.g. 'Samsung'. Matched against the site's own brand field on every card, client-side, case-insensitive. Leave empty for all brands.

## `minPrice` (type: `number`):

Only return products priced at or above this amount, in euros.

## `maxPrice` (type: `number`):

Only return products priced at or below this amount, in euros.

## `specialsOnly` (type: `boolean`):

Only return products currently showing a genuine strike-through discount against darty.com's own reference price (verified live against real price data -- never a guess).

## `minRating` (type: `integer`):

Only return products rated at least this many stars (1-5). Narrows via darty.com's own search facet server-side. 0 or empty = no rating filter.

## `freeDeliveryOnly` (type: `boolean`):

Only return products showing free home delivery, via darty.com's own search facet.

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

darty.com's own sort order, verified live to reorder the full server-side result set.

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

Paste one or more darty.com product pages or search-result-listing pages. Pagination continues forward automatically from any offset already in the URL. Filter fields above are ignored in this mode.

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

Adds full description, EAN, breadcrumb category path, and the complete flattened spec sheet (every characteristic the product page shows, not a fixed list) plus the full image gallery. Current price, was-price/discount, rating and card image are already included without this toggle.

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

Fetch the product's own customer reviews (not a third-party vendor): overall rating, total review count, recommend-percent, per-criteria scores (e.g. ease of use, value for money), and the page's own most recent individual written reviews (rating, title, body, author, dates, purchase store). Products with no reviews simply return an empty list.

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

Cap on written reviews fetched per product when 'Fetch reviews' is on, up to the site's own per-page limit.

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

Stop after this many result pages per search/URL entry. Leave empty for no page limit -- the run stops at Max products total.

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

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

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

Paste a previous run ID or dataset ID to continue a large crawl of products without returning or charging for products already collected there. Use this after an interrupted run, or when continuing a product pull in another run. For recurring monitoring of the same search, use Incremental mode below instead.

## `incrementalMode` (type: `boolean`):

Turn this on for daily or recurring monitoring. The first run returns all matching products as NEW. Later runs normally return only NEW, UPDATED, and REAPPEARED products. Turn on "Emit unchanged" or "Emit expired" only when you also want those products returned (and billed). State is kept separately for each mode/search-or-URL/filter setup; use State key when you want to name or deliberately share a monitoring campaign. To continue one specific interrupted run instead, use Resume from a previous run above.

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

Optional. Name this monitoring campaign to keep its state stable, or to deliberately share state across differently-configured runs. Leave empty to let the actor derive a key automatically from the mode, search/URL and filter settings -- different searches then never mix state with each other.

## `emitUnchanged` (type: `boolean`):

Off by default. Turn on to also return products that have not changed since the last run, marked UNCHANGED. This returns -- and bills -- extra rows you already have, so leave it off unless you specifically want the full snapshot every run.

## `emitExpired` (type: `boolean`):

Off by default. Turn on to also return products that were present in a previous run but are no longer found, marked EXPIRED. Only produced once a run has fully scanned every tracked search/URL -- not when Max products capped it or when Resume was used. This returns -- and bills -- extra synthetic rows, so leave it off unless you need expiry tracking.

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

Proxy configuration. Leave the default as provided -- this source needs it. Change only the region if you need a different one.

## `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": "television",
  "brands": [],
  "specialsOnly": false,
  "minRating": 0,
  "freeDeliveryOnly": false,
  "sortBy": "RELEVANCE",
  "urls": [
    "https://www.darty.com/nav/recherche?text=television"
  ],
  "fetchDetails": false,
  "fetchReviews": true,
  "maxReviewsPerProduct": 10,
  "maxItems": 20,
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "FR"
  },
  "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": "television",
    "brands": [],
    "urls": [
        "https://www.darty.com/nav/recherche?text=television"
    ],
    "incrementalMode": false,
    "emitUnchanged": false,
    "emitExpired": false,
    "proxy": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "FR"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("abotapi/darty-com-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": "television",
    "brands": [],
    "urls": ["https://www.darty.com/nav/recherche?text=television"],
    "incrementalMode": False,
    "emitUnchanged": False,
    "emitExpired": False,
    "proxy": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "FR",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("abotapi/darty-com-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": "television",
  "brands": [],
  "urls": [
    "https://www.darty.com/nav/recherche?text=television"
  ],
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "FR"
  }
}' |
apify call abotapi/darty-com-scraper --silent --output-dataset

```

## MCP server setup

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

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/acts/VVVSH8ggLs1uI0kaI/builds/8dUiMaKMMMCmCBQ3E/openapi.json
