# Cdiscount Scraper: Products, Marketplace Sellers & Reviews (`abotapi/cdiscount-scraper`) Actor

Scrape product data from Cdiscount.com by keyword, category, or URL. Apply filters and sorting, then enrich results with full product details, marketplace seller information, pricing, availability, specifications, ratings, and customer reviews.

- **URL**: https://apify.com/abotapi/cdiscount-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.80 / 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

## Cdiscount Product Scraper

Extract products from **Cdiscount.com**, one of France's largest online marketplaces. Search by keyword and category with the site's own filters and sort, or pass your own product/category/search URLs. Every product includes marketplace seller identity, and detail enrichment adds the full description, characteristics, variants, and customer reviews.

### What you get

Each product includes:

- **Identity**: product id, offer id, name, brand, EAN, URL, category path, breadcrumb (detail)
- **Price**: current price (EUR), was-price, discount %/amount, promo label
- **Availability**: in-stock flag, condition (new/used/refurbished)
- **Media**: preview image and the full image gallery
- **Variants**: colour/size/capacity combinations, when the product has any
- **Marketplace seller**: seller id, name, rating, number of ratings, sales count — Cdiscount is a marketplace, so most listings are sold by a third-party seller, not Cdiscount itself
- **Reviews** (detail enrichment): overall rating, review count, a per-criterion rating breakdown, and every individual review (author, date, rating, title, body)
- **Detail enrichment** (optional): full description and the complete characteristics table, flattened per category (phones, appliances, furniture, etc. each expose different attributes — every attribute the product page shows is included, not a fixed list)
- **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`

### Input

| Field | What it does |
|-------|--------------|
| `mode` | `search` (keyword/category + filters) or `url` (your own URLs) |
| `searchWord` | Keyword to search for (search mode) |
| `departmentId` | Optional numeric category id to scope the search (search mode) |
| `filterIds` | Optional raw facet filter ids, exactly as Cdiscount's own filter sidebar uses them (search mode, advanced) |
| `sortBy` | Relevance, best sellers, price low→high, price high→low, or customer rating |
| `urls` | Product, category, or search URLs (URL mode). Category/search URLs are forward-paginated |
| `fetchDetails` | Add full description, characteristics, variants, seller detail, and reviews per product |
| `maxItems` | Maximum number of products to return |
| `maxPages` | Maximum number of result pages to read per search/URL. Leave empty for no page limit |
| `resumeFromRunId` | 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" below. |
| `incrementalMode` | 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" below. |
| `stateKey` | Incremental mode only. Name a monitoring campaign, or deliberately share state across differently-configured runs. Leave empty to derive one automatically. |
| `emitUnchanged` | Incremental mode only. Also return `UNCHANGED` products. Off by default — returns and bills rows you already have. |
| `emitExpired` | 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. |
| `mcpConnectors` | Optionally forward results to Notion, Linear, Airtable, or Apify via MCP |
| `notionParentPageUrl` | URL or id of the Notion page under which item pages are created (Notion connector only) |
| `maxNotifyListings` | Cap on items written to each MCP connector per run. Does not affect the dataset |

#### Discovering filter ids

Run once with `maxItems: 1` on your category of interest, then inspect the run's dataset/log — the actor's search response includes the site's own facet list (brand, price bands, condition, seller, colour, etc.) with the exact id string each one expects. Pass any of those ids in `filterIds` to narrow subsequent runs.

#### Example input

```json
{
  "mode": "search",
  "searchWord": "telephone",
  "departmentId": "10",
  "sortBy": "priceAsc",
  "fetchDetails": true,
  "maxItems": 50,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": ["RESIDENTIAL"],
    "apifyProxyCountry": "FR"
  }
}
```

```json
{
  "mode": "url",
  "urls": [
    "https://www.cdiscount.com/telephonie/telephone-mobile/l-14404.html"
  ],
  "fetchDetails": true,
  "maxItems": 30
}
```

**Recurring monitoring of the same search, only what changed:**

```json
{
  "mode": "search",
  "searchWord": "telephone",
  "departmentId": "10",
  "incrementalMode": true,
  "maxItems": 50
}
```

For large or sustained runs, use a France-based proxy for best reliability.

### 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 (`searchWord`, `departmentId`, `filterIds`, `sortBy`, `urls`, `fetchDetails`) — 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.
- `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.

### Output

Results are written to the default dataset, with four ready-made views: **Overview**, **Full detail**, **Marketplace sellers**, and **Reviews**.

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

Optionally pipe results into the apps you already use via Model Context Protocol (MCP)
connectors — Notion, Linear, Airtable, or Apify. Authorize a connector under Apify →
Settings → API & Integrations, then select it in the input. Notion gets a rich
page-per-item export; other connectors get a best-effort write or digest. This never
changes the dataset output.

### Pricing

Pay-per-event: a per-result charge for every product returned, plus an optional per-product surcharge only when `fetchDetails` is enabled.

# Actor input Schema

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

Choose Search to use a keyword, category and filters. Choose URLs to paste Cdiscount product, category or search links.

## `searchWord` (type: `string`):

Keyword to search for, e.g. "telephone" or "aspirateur". Used in search mode.

## `departmentId` (type: `string`):

Numeric Cdiscount department id to scope the search to one category (e.g. "10" for Téléphonie). Copy it from a Cdiscount category URL (the number after /l- or /search/). Leave empty to search all categories.

## `filterIds` (type: `array`):

Raw Cdiscount facet filter ids to narrow results, exactly as the site's own filter sidebar uses them (e.g. "Neuf ou occasion/"neuf"", "Marque/"samsung"", "categorycodepath/"07/0703""). Run once with maxItems=1 and read the `facets` debug field logged at INFO level to discover the ids for your category, then pass the ones you want here.

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

Order in which search results are returned. Used in search mode.

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

Cdiscount product URLs (.../f-NNNNN-XXXXXXXX.html), category/listing URLs (.../l-NNNNN.html), or search URLs (/search/{department}/{word}.html). Listing/search URLs are forward-paginated.

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

Open each product's page to add the full description, complete characteristics table, variants, marketplace seller detail, and customer reviews (rating breakdown + individual reviews). Adds one detail request per product.

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

Maximum number of products to return. The run stops once this many items are collected.

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

Maximum number of result pages to read per search/listing URL. Leave empty (0) for no page limit — the run stops at Max items instead.

## `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 items capped it or when Resume was used. This returns -- and bills -- extra synthetic rows, so leave it off unless you need expiry tracking.

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

For large or sustained runs, use a France-based proxy for best reliability.

## `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. Leave empty to skip.

## `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 MCP connector per run. Does not affect the dataset.

## Actor input object example

```json
{
  "mode": "search",
  "searchWord": "telephone",
  "filterIds": [],
  "sortBy": "relevance",
  "urls": [
    "https://www.cdiscount.com/telephonie/telephone-mobile/l-14404.html"
  ],
  "fetchDetails": false,
  "maxItems": 20,
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "FR"
  },
  "maxNotifyListings": 20
}
```

# Actor output Schema

## `overview` (type: `string`):

No description

## `details` (type: `string`):

No description

## `sellers` (type: `string`):

No description

## `reviews` (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 = {
    "searchWord": "telephone",
    "urls": [
        "https://www.cdiscount.com/telephonie/telephone-mobile/l-14404.html"
    ],
    "incrementalMode": false,
    "emitUnchanged": false,
    "emitExpired": false
};

// Run the Actor and wait for it to finish
const run = await client.actor("abotapi/cdiscount-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 = {
    "searchWord": "telephone",
    "urls": ["https://www.cdiscount.com/telephonie/telephone-mobile/l-14404.html"],
    "incrementalMode": False,
    "emitUnchanged": False,
    "emitExpired": False,
}

# Run the Actor and wait for it to finish
run = client.actor("abotapi/cdiscount-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 '{
  "searchWord": "telephone",
  "urls": [
    "https://www.cdiscount.com/telephonie/telephone-mobile/l-14404.html"
  ],
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false
}' |
apify call abotapi/cdiscount-scraper --silent --output-dataset

```

## MCP server setup

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

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/KeeWD5DmzKtkVuOl7/builds/JYw6FXrbQs1hVdQtl/openapi.json
