# Fnac Scraper - Products, Prices, Variants & Reviews (`abotapi/fnac-com-scraper`) Actor

Scrape Fnac (fnac.com) products: current price plus strike-through discount, colour/model variant matrix, brand, breadcrumb category, and the site's own reviews with rating. Search by keyword/category or paste product and search-result links.

- **URL**: https://apify.com/abotapi/fnac-com-scraper.md
- **Developed by:** [Abot API](https://apify.com/abotapi) (community)
- **Categories:** E-commerce, Automation, Developer tools
- **Features:** Limited permissions
- **Stats:** 7 total users, 5 monthly users, 100.0% runs succeeded, 1 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/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

## Fnac Scraper: Prices, Discounts, Variants & Reviews

Fnac Scraper turns Fnac.com, the French electronics, media and entertainment retailer, into structured product data. Search by keyword and/or category, or paste product and search-result links directly, and get the current price, the strike-through reference price with a computed discount when a product is genuinely marked down, the colour/model variant matrix, a flattened spec sheet and the site's own reviews and rating. Export to JSON, CSV or Excel, pipe results into Notion, Linear or Airtable, or read them straight through the API.

### Why This Scraper?

- **Was-price and discount, never fabricated.** When a listing card shows a product marked down against the site's own reference price, both prices are captured plus the computed discount amount, percentage and the site's own promo label. A full-price product carries null there, never a guessed discount. A pasted single-product link does not carry this discount data at all, since it reads the product page directly rather than a listing card; see the FAQ below.
- **Real narrowing in search mode.** In search mode, a requested brand narrows through the site's own search facet server side, and sort reorders the full server-side result set, both verified against live results. With several brands, each gets its own search pass. A pasted category or search-result link in URL mode does not send the brand facet to the site, and cannot filter by brand client-side either, since a plain listing card never carries its own brand (a pasted product page is checked against its page brand); use search mode if you need brand narrowing. Extra filters: sold by Fnac only, condition (new or refurbished), in-stock only, and several search terms at once.
- **Colour/model variant matrix.** Products sold in more than one colour or configuration return every sibling variant, each with its own id, url, image and availability.
- **Reviews straight from the product page.** Overall rating, rating count and the product's own individual written reviews (author, date, rating, title, body, verified-purchase flag), read directly from the page, not a third-party review vendor.
- **Flattened spec sheet.** Full detail returns every characteristic the product page itself declares, storage, screen size, colour, EAN, condition and whatever else that category carries, rather than a fixed field list.
- **Two ways in.** Keyword and/or category search with real site filters, or paste any product or search-result link and let pagination continue automatically.
- **Built for schedules.** Turn on incremental mode to get only new and changed products on every recurring run, and a fully blocked run fails loudly instead of quietly returning an empty dataset.

### Use Cases

- **Price tracking and deal alerts:** monitor a search or a list of products for price drops and genuine promotions over time.
- **Catalog and competitor research:** build a dataset of prices, discounts, specs and variant availability across a category.
- **Review and reputation analysis:** collect a product's own ratings and written reviews before stocking or recommending it.
- **Assortment and variant mapping:** pull the full colour/model matrix for a product line to see what is actually available.
- **Feed a shop-comparison site or bot:** keep prices and availability current with scheduled, incremental runs.

### Data You Get

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

| Field | Example |
| --- | --- |
| `productId` / `sku` | `"77340219"` / `"9871234567890"` (EAN) |
| `name` | `"Aurivex SoundWave Pro 500 Casque Sans Fil Noir"` |
| `brand` | `"Aurivex"` (null on a plain listing card; populated only via full product detail or a pasted single-product link) |
| `category` / `breadcrumbPath` | `"Casque audio"` / `["Accueil", "Son", "Casques et ecouteurs", "Casques sans fil"]` |
| `url` | `"https://www.fnac.com/mp77340219/aurivex-soundwave-pro-500-casque-sans-fil-noir/w-4"` |
| `price` / `currency` | `89.99` / `"EUR"` |
| `originalPrice` / `discountAmount` / `discountPercent` | `119.99` / `30.00` / `25.0` |
| `isOnSpecial` / `promoLabel` | `true` / `"Vente flash"` |
| `onlineAvailable` / `condition` | `true` / `"new"` |
| `sellerName` | `"Fnac"` (or a marketplace seller's name) |
| `image` / `images` / `description` | `"https://static.example.com/000000001.jpg"` / a gallery array / full product description text |
| `characteristics` / `cardCharacteristics` | `{"Autonomie": "30h", "Couleur": "Noir", "EAN": "9871234567890"}` (full spec sheet, full detail only) / `{"Autonomie": "30h", "Couleur": "Noir"}` (short spec teaser on every listing card) |
| `variants` | `[{"axis": "Couleur", "name": "Bleu", "url": "https://www.fnac.com/mp77340220/.../w-4", "available": true}]` |
| `rating` / `reviewCount` | `4.3` / `18` |
| `reviews.averageRating` / `reviews.ratingCount` / `reviews.totalReviewCount` / `reviews.items[]` | `4.3` / `18` / `6` / `[{"rating": 5, "title": "Tres bon son", "body": "Le son est clair et la batterie tient longtemps.", "author": "Marie L.", "date": "Posted on 03 Mar 2026", "verifiedPurchase": true}]` |
| `ean` / `mpn` / `productType` / `subtitle` / `authors` / `brandUrl` / `pageTemplate` | identity and catalog fields from the product page: `"9871234567890"` / `"AX-500"` / `"Casque audio"`, plus the subtitle and authors of books and media, the brand page link and the site's page template path (full detail) |
| `metaDescription` / `descriptionHtml` / `bestRating` / `worstRating` / `ratingCount` | the page's meta description, the description as HTML, and the rating scale and count, such as `5` / `1` / `154` (full detail) |
| `availability` / `inStock` / `isRefurbished` / `conditionDetail` | the site's own availability wording such as `"En Stock"`; a true/false in-stock flag taken from it (null when the page states nothing); `true` when the offer is graded second-hand or labelled "Reconditionné"; the site's grade word such as `"Très bon"` (listing cards and full detail) |
| `sellerId` / `sellerType` / `sellerLocation` / `sellerRating` / `sellerSales` | the seller behind the offer shown (`"fnac"` or `"professional"`, the site's location class); rating and sales count only for a marketplace main offer (full detail) |
| `delivery` / `deliveryOptions` / `deliveryDate` / `deliveryPrice` / `shippingCost` / `storeAvailability` | delivery method and option text, the promised date text, delivery price in EUR, and the store pickup line for the visitor's nearest store (full detail) |
| `warrantyOptions[]` / `includedAccessories` | `[{"name": "Garantie Reparation pendant 3 ans", "price": 94.99, "pricePerMonth": 2.64}]` / `["Charging cable", "Documentation"]` (full detail) |
| `energyClass` / `repairabilityIndex` / `durabilityIndex` / `ecoTax` / `rcpTax` | energy label letter, French repairability and durability scores, and the eco-participation and private-copy amounts in EUR, when the page shows them (full detail) |
| `offersNewCount` / `offersUsedCount` / `otherSellers[]` | counts of the other new and second-hand offers, and each one: seller name, id, type, location, rating, review count, price, condition and grade, delivery promise and shipping cost (full detail) |
| `scrapedAt` | the time the row was scraped, ISO 8601 UTC, on every row |
| `changeType` / `changedFields` / `firstSeenAt` / `lastSeenAt` | `"NEW"` (also `UPDATED`, `UNCHANGED`, `REAPPEARED`, `EXPIRED`) / `["price"]` / `"2026-01-01T00:00:00Z"` / `"2026-03-01T00:00:00Z"` (incremental mode only) |

A pasted single product link always reads the full product page. **Fetch full product details** (on in search mode, always on for a pasted product link) fills `description`, `brand`, `sku`, `category`, `breadcrumbPath`, the flattened `characteristics` spec sheet, the `images` gallery, the `variants` matrix and every full-detail field listed above. Fields the page does not carry for a given product are simply left out of that row (a book has no energy class, a first-party offer has no seller rating). On a listing row `condition` is the site's own grade word (`new`, `like-new`, `very-good`, `good`, `acceptable`); on a product page it is `new` or `used`. `isRefurbished` is the one flag to filter on. `onlineAvailable` stays at its listing-card default (`true`) in search mode; use `availability` for the real wording. `unitPrice` and `storePickupAvailable` are fields the dataset always carries but the site does not currently expose a value for, so they are always `null`.

The extra seller and offer details of a product (ratings, delivery promises, the marketplace seller's sales) come from two small pages the product page itself loads; they are fetched in the same step as the product page and are part of the same `detail-enrichment` unit, never a separate charge.

### How to Use

1. Pick a **mode**: `search` (keyword and/or category) or `url` (paste product or search-result links).
2. Fill in the fields for that mode, and set the filters (brand, price range, discount-only) and sort you want.
3. Choose whether to fetch full product detail and/or reviews, and set **Max items** and **Max pages** to control run size and cost.
4. Click **Start**, then download the dataset as JSON, CSV or Excel, or read it through the API.

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

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

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

```json
{
  "mode": "search",
  "searchTerm": "smartphone",
  "brands": ["Apple"],
  "specialsOnly": true,
  "sortBy": "PRICE_ASC",
  "maxItems": 50,
  "maxPages": 5
}
```

**3. Full product detail and reviews for a pasted product link:**

```json
{
  "mode": "url",
  "urls": ["https://www.fnac.com/mp50936969/APPLE-iPhone-14-6-1-128-Go-Bleu-Reconditionne-Avec-Batterie-neuve/w-4"],
  "fetchDetails": true,
  "fetchReviews": true
}
```

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

```json
{
  "mode": "search",
  "searchTerm": "iphone",
  "incrementalMode": true,
  "maxItems": 50
}
```

#### URL mode: accepted link shapes

Paste any of these into **URLs** (URL mode); each is recognised automatically:

- **Search results** (`/SearchResult/ResultList.aspx?Search=...`), including a paginated page (`PageIndex=2`) and a filtered or sorted search (brand facet, `SortTypes=...`). Pagination continues forward.
- **Category** (`/shi<id>/...`) and **sub-category** (`/nsh<id>/...`) listing links.
- **First-party product** (`/<slug>/a<id>/w-4`, `/<slug>/a<id>`, `/a<id>/<slug>`, bare `/a<id>`) and **marketplace product** (`/mp<id>/<slug>/w-4`) links, with or without the slug and the trailing `/w-4`. The slug is ignored by the site, so repeated product ids are scraped once.

Default behaviour: if you leave **URLs** unedited, URL mode scrapes one example of each shape above, capped by **Max items**. Replace the list with your own links to scrape only those. A pasted product link always reads the full product page.

#### Run it from your code

Python:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("abotapi/fnac-com-scraper").call(run_input={"mode": "search", "searchTerm": "iphone", "maxItems": 20})
for product in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(product["name"], product["price"], product["currency"])
```

JavaScript:

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('abotapi/fnac-com-scraper').call({ mode: 'search', searchTerm: 'iphone', maxItems: 20 });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the **Integrations** tab.

#### Resume and recurring updates

- **Resume** (`resumeFromRunId`) continues one interrupted crawl. Paste a previous run or dataset id and this run skips every product it already collected, so you don't pay twice.
- **Incremental mode** (`incrementalMode`) is for running the same search or URL set again and again on a schedule. Each product is classified `NEW`, `UPDATED` (with `changedFields`), `UNCHANGED` (kept but not returned unless `emitUnchanged` is on), or, once a run has fully scanned every tracked search/URL with `emitExpired` on, `EXPIRED`. A product only ever comes back marked `REAPPEARED` if it was previously tombstoned that way, that is, only after some earlier run had `emitExpired` on and completed a full, uncapped, non-resumed scan that had already marked it gone. If `emitExpired` has never been turned on for a given search, a product that vanishes and later comes back is simply reported as `UPDATED` or `UNCHANGED` like any other rescrape, never `REAPPEARED`. `stateKey` names or shares the stored state. Leave it blank and the actor derives one automatically from your search filters (including `searchTerms`, `soldByFnac`, `condition` and `inStockOnly` when you set them) plus the `fetchReviews` setting, since turning that on or off changes the emitted record shape; set your own `stateKey` to share state deliberately across differently configured runs instead. The site serves the same product page in two layouts depending on the connection, so consecutive runs can read an unchanged product through different layouts. When the stored copy and the new copy come from different layouts, the fields one layout does not carry or fills with a default are not compared: specification sheet, photo gallery and main image, rating bounds and rating count, review count and reviews, stock status, online availability, eco tax and repair tax, energy, repairability and durability labels, included accessories, variants, authors, subtitle, brand page link, product type, category, breadcrumb, descriptions and meta description. A layout switch alone therefore never marks a product `UPDATED` and never bills it again. Price, name, seller, condition and the star rating are still compared, so a real change in them is always reported. A stock or label change that is only visible across a layout switch is reported on the next read in the same layout, and the stored copy always keeps the fuller classic layout values.
- Combining `resumeFromRunId` with `incrementalMode` 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.
- Fields that move between two scrapes of the same unchanged product (the scrape time, the other sellers' list with their prices and ratings, the new and used offer counts, delivery date and price, the store pickup line, warranty prices) never count as a change, so they do not turn a product into `UPDATED`.
- One-time upgrade note: this version adds many new fields to each row. A saved baseline from an earlier version does not hold them, and they are adopted silently, so the first scheduled run after the upgrade does not return (or bill) every tracked product as `UPDATED`. The same applies to a baseline saved by an earlier version for a product page it could not read in the newer layout: the details this version now reads from that page (brand, category, description, reference and the like) are adopted silently the first time. A change of price or discount is never adopted silently. Likewise, a longer reviews list caused only by raising `maxReviewsPerProduct` is not a change. Real changes are detected as before.
- With incremental mode off, output is exactly as before.

#### Run time limits and partial results

Every run has a time limit. If the detail/review enrichment of the last batch of products cannot finish before that limit, the run returns those products anyway with their listing data (price, discount, rating) and an empty reviews object, and says so in a status message starting with "Partial results:". Those specific records are not billed the enrichment surcharge. To enrich everything within a short limit, lower Max items or turn full detail/reviews off.

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

Optionally pipe the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. This is an extra delivery step after the scrape; the Apify dataset is never changed.

**What gets written to the connector:** a condensed, human-readable summary of each product, not the full JSON. Each item becomes one entry with a title and its key fields flattened to plain text. The complete record always stays in the Apify dataset.

1. Authorize a connector once under **Apify, Settings, Integrations** (Notion, Linear, Airtable, or Apify).
2. Select it in the **Pipe results into your apps** input field. If the picker is empty, you have not authorized a connector yet.
3. For **Notion**, also set `notionParentPageUrl` to the page where items should be created.

The connection is mediated by Apify's MCP proxy, so this actor never sees your third-party credentials. Leave the field empty to skip. Use `maxNotifyListings` to cap how many items are exported per connector per run.

### Input Parameters

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `mode` | string | `search` | `search` (keyword and/or category) or `url` (paste product or search-result-listing links). |
| `searchTerm` | string | None (prefill `"iphone"`) | Free-text keyword. Search mode only. |
| `category` | string | None | A category/rayon name, folded into the same free-text search as `searchTerm`. Search mode only. |
| `sortBy` | string | `RELEVANCE` | `RELEVANCE`, `PRICE_ASC`, `PRICE_DESC`, `BEST_SELLERS`, or `TOP_RATED`. Search mode only. |
| `urls` | array | One example of each link shape (search, paginated search, filtered and sorted search, first-party product, marketplace product, category, sub-category, bare `/a<id>` product) | Product, category or search-result-listing links to scrape. URL mode only; pagination continues forward automatically. Left unedited, URL mode scrapes one example of each shape. Repeated product ids are scraped once. |
| `brands` | array | Empty (prefill) | Brand names to keep; all of them are applied (one search pass per brand). Pasted product pages are checked against the page brand. |
| `searchTerms` | array | Empty | Extra keywords searched in turn alongside `searchTerm`. |
| `soldByFnac` | boolean | `false` | Keep only offers sold by Fnac itself (the seller type the site shows on every listing card); offers whose seller is not known yet are kept. |
| `condition` | string | `any` | `any`, `new` or `refurbished`. `refurbished` means any second-hand or refurbished offer: a graded used offer (like-new, very-good, good, acceptable) or a product labelled "Reconditionné". `new` drops all of those. |
| `inStockOnly` | boolean | `false` | Drop products the card or page marks as unavailable (for example "Ce produit n'est plus en vente"). |
| `minPrice` | number | None | Only return products priced at or above this amount, in EUR. Applies in both modes. |
| `maxPrice` | number | None | Only return products priced at or below this amount, in EUR. Applies in both modes. |
| `specialsOnly` | boolean | `false` | Only return products currently showing a genuine discount against the site's own reference price, from listing-card data. A pasted single-product link never carries this discount data, so it is always filtered out when this is on. |
| `fetchDetails` | boolean | `false` | Adds full description, EAN/SKU, breadcrumb category, the flattened characteristics spec sheet, image gallery, and the colour/model variant matrix. |
| `fetchReviews` | boolean | `true` | Fetch the product's own reviews: overall rating, rating count and recent written reviews. |
| `maxReviewsPerProduct` | integer | `20` | Cap on written reviews fetched per product when Fetch reviews is on (maximum 100). fnac.com serves at most 10 written reviews on a product page (measured on first-party, refurbished and book pages), so a value above 10 returns no more than the page carries. |
| `maxPages` | integer | None (empty means no page limit) | Stop after this many result pages per search/URL entry. |
| `maxItems` | integer | `20` | Cap on total products returned across every search/URL entry. `0` = unlimited. |
| `resumeFromRunId` | string | None | Continue one interrupted run without re-collecting or re-charging products it already returned. |
| `incrementalMode` | boolean | `false` | Recurring monitoring of the same search/URL set: returns only new and changed products against a remembered baseline. |
| `stateKey` | string | None | Name or share an incremental-mode monitoring campaign. Leave empty to derive one automatically. |
| `emitUnchanged` | boolean | `false` | Also return (and bill) products that have not changed since the last run. |
| `emitExpired` | boolean | `false` | Also return (and bill) products no longer found, and start tombstoning missing products so a later reappearance can be detected. Only takes effect once a run fully scans every tracked search/URL. |
| `ignoreFieldsForChanges` | array | `[]` | Incremental mode only: field names (any nesting depth) that never count as a change, so a product differing only in them is `UNCHANGED` and not returned or billed as `UPDATED`. |
| `proxy` | object | Apify Proxy | Connection settings. |
| `mcpConnectors` | array | None | Optional MCP connectors to export results into (Notion, Linear, Airtable, Apify). |
| `notionParentPageUrl` | string | None | Notion connector only: page under which item pages are created. |
| `maxNotifyListings` | integer | `50` | Cap on items exported to each connector per run. Does not affect the dataset. |

### Output Example

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

```json
{
  "productId": "77340219",
  "sku": "9871234567890",
  "name": "Aurivex SoundWave Pro 500 Casque Sans Fil Noir",
  "brand": "Aurivex",
  "category": "Casque audio",
  "breadcrumbPath": ["Accueil", "Son", "Casques et ecouteurs", "Casques sans fil"],
  "url": "https://www.fnac.com/mp77340219/aurivex-soundwave-pro-500-casque-sans-fil-noir/w-4",
  "price": 89.99,
  "currency": "EUR",
  "originalPrice": 119.99,
  "discountAmount": 30.00,
  "discountPercent": 25.0,
  "isOnSpecial": true,
  "promoLabel": "Vente flash",
  "onlineAvailable": true,
  "condition": "new",
  "sellerName": "Fnac",
  "sellerType": "fnac",
  "isRefurbished": false,
  "availability": "En Stock",
  "ean": "9871234567890",
  "mpn": "AX-500",
  "image": "https://static.example.com/000000001.jpg",
  "description": "Sample product description as shown on the site.",
  "offersNewCount": 1,
  "offersUsedCount": 1,
  "otherSellers": [
    { "sellerName": "Marketplace Shop", "sellerType": "professional", "sellerRating": 4.5, "price": 74.9, "condition": "used", "conditionDetail": "Tres bon", "isRefurbished": true, "shippingCost": 0, "delivery": "Livre le 04/11" }
  ],
  "scrapedAt": "2026-03-01T10:00:00Z",
  "characteristics": { "Autonomie": "30h", "Couleur": "Noir", "EAN": "9871234567890" },
  "variants": [
    { "axis": "Couleur", "name": "Bleu", "url": "https://www.fnac.com/mp77340220/aurivex-soundwave-pro-500-casque-sans-fil-bleu/w-4", "available": true }
  ],
  "rating": 4.3,
  "reviewCount": 18,
  "reviews": {
    "averageRating": 4.3,
    "ratingCount": 18,
    "totalReviewCount": 6,
    "items": [
      { "rating": 5, "title": "Tres bon son", "body": "Le son est clair et la batterie tient longtemps.", "author": "Marie L.", "date": "Posted on 03 Mar 2026", "verifiedPurchase": true }
    ]
  }
}
```

### Plan Requirement

Fnac.com is protected by strict bot detection. In our measurements, plain datacenter and residential connections were both refused (HTTP 403 with a short "Please enable JS" page), so the default connection alone will usually not load pages. The run therefore falls back automatically to a backup connection (Apify's result-unblocking proxy group, `UNBLOCKER`), which is the lane that served real pages. This backup lane is billed to your Apify account as proxy usage, at your plan's proxy rates, and requires a plan with proxy access. Apify free plans do not include residential proxies.

When the selected connection is refused by the site on every attempt (the usual case on this site), the run log first shows "Protected response detected; refreshing the connection." for each refused attempt, then once a boxed notice: "The selected connection was refused by the site on every attempt. Continuing on a backup connection; some pages may still be unavailable. The backup connection is billed to your Apify account as proxy usage.", and later "Served over the backup connection (the default connection was unavailable)." When instead your plan cannot open the selected connection at all, the boxed notice reads "The selected connection is unavailable to this run. Apify free plans do not include residential proxies. Continuing on a backup connection; some pages may still be unavailable." Either notice appears once per run.

To retire the backup lane entirely, set the environment variable `FNAC_COM_ALTERNATE_CONNECTION=0` on the run; the run then uses only the connection you selected.

If nothing can be loaded, the run fails with a clear message instead of an empty dataset: "The proxy connection was refused for this account, so no page could be read..." (no proxy access on your plan), "No results: residential proxies are unavailable on this run ... and the backup connection could not load the requested Fnac pages." (backup also refused), or "Could not load any results page (the requests failed), so nothing was collected. This is a connection problem, not an empty search." Try again later; access can be bursty.

### FAQ

#### How much does it cost?

Billing uses three pay-per-event charges: an actor start event once per run (`apify-actor-start`), a dataset item event for every product row returned (`apify-default-dataset-item`, per row), and a smaller `detail-enrichment` event for each returned product whose page was fetched for full detail and/or reviews (per pushed product, when Fetch full product detail or Fetch reviews is on). Fetch reviews is on by default, so a normal run already pays that surcharge for every product it returns; turn Fetch details and Fetch reviews off if you only need the listing-card fields (price, discount, rating). Products suppressed by incremental mode (UNCHANGED, with Emit unchanged off) are never billed, even when their page had to be fetched to check for changes. Proxy usage on the backup connection is separate and billed by Apify to your account. The Pricing tab shows the current rates, and Max items caps the size of any run.

#### Is it legal to scrape Fnac?

This actor collects only publicly available product data. You are responsible for how you use it: follow Fnac's terms and the laws that apply to you, and get legal advice if you plan commercial redistribution.

#### Can I get only new or changed products on a schedule?

Yes. Schedule the actor from the Schedules tab and turn on Incremental mode. Each run then returns only new and updated products, unchanged ones are not billed, and a product only comes back marked REAPPEARED if an earlier run had Emit expired on and had already completed a full scan that marked it gone.

#### How do multiple brands work?

Each brand in the list gets its own search pass, and results share the `maxItems` cap.

#### Why does a pasted product link always fetch the full page?

A single product link has no listing card to fall back on, so the actor always reads the full product page for it, regardless of the Fetch full product detail toggle, to get any data at all. Note that this means it never carries the listing-card discount fields (`originalPrice`, `discountAmount`, `discountPercent`, `isOnSpecial`, `promoLabel`), which always come back null there. The detail-enrichment charge itself is still gated by the toggles: it only applies when Fetch full product detail actually finds and adds data, or when Fetch reviews successfully reads the reviews endpoint at all, even if that product happens to have zero reviews.

#### Why did my run fail instead of returning an empty dataset?

If Fnac refuses every request, or serves a page that declares results but returns none of the actual product cards, the run stops with a clear connection message instead of quietly returning nothing. Run it again in a few minutes. A search that genuinely matched nothing still succeeds, with a message saying so.

#### Can I use it with AI agents or MCP?

Yes. Call it from any Apify integration or MCP client, and use the connector field to push results into Notion, Linear or Airtable.

### 🔗 Want more electronics data?

Pair this actor with these related scrapers from the same team:

<table>
<tr><td>🔌 <a href="https://apify.com/abotapi/boulanger-com-scraper"><b>Boulanger.com</b></a><br>Scrape Boulanger (boulanger.com) electronics and home-appliance products: current price...</td><td>🔌 <a href="https://apify.com/abotapi/darty-com-scraper"><b>Darty Scraper</b></a><br>Scrape Darty (darty.com) products: current price plus strike-through reference price and...</td></tr>
<tr><td>👗 <a href="https://apify.com/abotapi/otto-de-scraper"><b>OTTO.de Scraper</b></a><br>Scrape OTTO.de products by search or URL. Extract prices, discounts, variants...</td><td>🔌 <a href="https://apify.com/abotapi/biccamera-com-scraper"><b>Bic Camera Scraper</b></a><br>Scrape Bic Camera (biccamera.com) products: JPY price, list price and discount, Bic Point...</td></tr>
<tr><td>🔌 <a href="https://apify.com/abotapi/currys-co-uk"><b>Currys UK Scraper</b></a><br>Scrape Currys UK products, including electronics, home appliances, computers, TVs and...</td><td>🛒 <a href="https://apify.com/abotapi/pccomponentes-com-scraper"><b>PcComponentes Scraper</b></a><br>Scrape PcComponentes (pccomponentes.com) products: current price plus strike-through...</td></tr>
</table>

👉 [Browse all abotapi scrapers](https://apify.com/abotapi)

### 💬 Support

- 🐞 **Found a bug or a missing field?** Open a ticket on the [Issues tab](https://apify.com/abotapi/fnac-com-scraper/issues/open). We usually reply within hours.
- 🛠️ **Need another site, extra fields or a private build?** Email <contact@abotapi.com>.
- ⭐ **Enjoying it?** A quick review on the actor page helps other users find it.

# Actor input Schema

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

'search' finds products by keyword and/or category using fnac.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. 'iphone 14' or 'ps5'. Uses fnac.com's own product search.

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

A category/rayon name, e.g. 'smartphones', 'livres', 'console ps5'. Combined with Search keyword above into fnac.com's own free-text search (fnac.com does not expose a separate path-based category browse the way some catalog sites do). Search mode only: it is written into fnac.com's own search URL, so a pasted URL keeps whatever it already carries.

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

fnac.com's own sort order, verified live to reorder the full server-side result set. Search mode only: it is written into fnac.com's own search URL, so a pasted URL keeps whatever it already carries.

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

Paste one or more fnac.com product pages (including a bare /a<id> link), category pages or search-result-listing pages. Left unedited, this field scrapes one example of each link shape. Pagination continues forward automatically from any PageIndex= already in the URL. Search-mode fields are ignored here. The fields in the Filters section still apply, including to a pasted product URL.

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

Only return products from these brands, e.g. 'Apple'. Search mode runs one pass per brand through fnac.com's own brand filter; every brand is also re-checked on the product page value when known. Leave empty for all brands. Applies in BOTH search mode and URL mode.

## `searchTerms` (type: `array`):

Extra keywords for search mode, each searched in turn alongside 'searchTerm'. Duplicates are skipped.

## `soldByFnac` (type: `boolean`):

Keep only offers sold by Fnac itself, using the seller type the site shows on every listing card and product page; offers whose seller is not known yet are kept. Applies in BOTH search mode and URL mode.

## `condition` (type: `string`):

any, new, or refurbished. 'refurbished' means any second-hand or refurbished offer: a graded used offer (like-new, very-good, good, acceptable) or a product labelled 'Reconditionné'; 'new' drops all of those. Applies in BOTH search mode and URL mode.

## `inStockOnly` (type: `boolean`):

Drop products the listing card or product page marks as unavailable (for example 'Ce produit n'est plus en vente'). Applies in BOTH search mode and URL mode.

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

Only return products priced at or above this amount, in euros. Applies in BOTH search mode and URL mode, including a pasted product URL.

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

Only return products priced at or below this amount, in euros. Applies in BOTH search mode and URL mode, including a pasted product URL.

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

Only return products currently showing a genuine strike-through discount against fnac.com's own reference price (verified live against real price data -- never a guess). Combines with the keyword/category above. Applies in BOTH search mode and URL mode, including a pasted product URL.

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

Adds full description (text and HTML), EAN/MPN/SKU, breadcrumb category path, complete flattened characteristics (spec sheet -- every field the product page shows, not a fixed list), image gallery, the colour/model variant matrix, seller identity and rating, the other sellers' offers (price, condition grade, delivery), delivery, warranty options, energy label, repairability and durability scores, eco-participation taxes and included accessories from the product page. Current price, was-price/discount, rating and card image are already included without this toggle.

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

Fetch the product's own reviews (not a third-party vendor): overall rating, total rating count, and the page's own recent individual written reviews (rating, title, body, author, date, verified-purchase). fnac.com only ever surfaces its most recent reviews per product page; 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 (fnac.com serves at most 10 written reviews on a product page, so the real count is often lower). Values above 100 are treated as 100.

## `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).

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

Proxy configuration. The default works on every Apify plan, including the free tier. Change it only if you need a specific country or are running very large jobs.

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

## `ignoreFieldsForChanges` (type: `array`):

Optional. Field names that must NOT count as a change in incremental mode, for example "price" or "availability". A product whose only differences are in these fields is treated as UNCHANGED, so it is not returned or billed as UPDATED. Names match at any nesting depth. The fields still appear in the output of rows that are returned.

## `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": "iphone",
  "sortBy": "RELEVANCE",
  "urls": [
    "https://www.fnac.com/SearchResult/ResultList.aspx?Search=iphone",
    "https://www.fnac.com/SearchResult/ResultList.aspx?Search=iphone&PageIndex=2",
    "https://www.fnac.com/SearchResult/ResultList.aspx?Search=casque&f=attributes.brand__Marque-Apple&SortTypes=LowestPrice",
    "https://www.fnac.com/Apple-iPhone-15-6-1-5G-Double-SIM-128-Go-Noir/a18573408/w-4",
    "https://www.fnac.com/mp54113208/Apple-iPhone-16-6-1-5G-128-Go-e-SIM-Rose/w-4",
    "https://www.fnac.com/Apple-iMac-Mac-Mac-Mini-Mac-Pro/shi352415/w-4",
    "https://www.fnac.com/Accessoires-Apple-Watch/Apple-Watch/nsh558378/w-4",
    "https://www.fnac.com/a21960704"
  ],
  "brands": [],
  "searchTerms": [],
  "soldByFnac": false,
  "condition": "any",
  "inStockOnly": false,
  "specialsOnly": false,
  "fetchDetails": false,
  "fetchReviews": true,
  "maxReviewsPerProduct": 20,
  "maxItems": 20,
  "proxy": {
    "useApifyProxy": true
  },
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "ignoreFieldsForChanges": [],
  "maxNotifyListings": 50
}
```

# Actor output Schema

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

No description

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

No description

## `changes` (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": "iphone",
    "urls": [
        "https://www.fnac.com/SearchResult/ResultList.aspx?Search=iphone",
        "https://www.fnac.com/SearchResult/ResultList.aspx?Search=iphone&PageIndex=2",
        "https://www.fnac.com/SearchResult/ResultList.aspx?Search=casque&f=attributes.brand__Marque-Apple&SortTypes=LowestPrice",
        "https://www.fnac.com/Apple-iPhone-15-6-1-5G-Double-SIM-128-Go-Noir/a18573408/w-4",
        "https://www.fnac.com/mp54113208/Apple-iPhone-16-6-1-5G-128-Go-e-SIM-Rose/w-4",
        "https://www.fnac.com/Apple-iMac-Mac-Mac-Mini-Mac-Pro/shi352415/w-4",
        "https://www.fnac.com/Accessoires-Apple-Watch/Apple-Watch/nsh558378/w-4",
        "https://www.fnac.com/a21960704"
    ],
    "brands": [],
    "searchTerms": [],
    "proxy": {
        "useApifyProxy": true
    },
    "incrementalMode": false,
    "emitUnchanged": false,
    "emitExpired": false,
    "ignoreFieldsForChanges": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("abotapi/fnac-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": "iphone",
    "urls": [
        "https://www.fnac.com/SearchResult/ResultList.aspx?Search=iphone",
        "https://www.fnac.com/SearchResult/ResultList.aspx?Search=iphone&PageIndex=2",
        "https://www.fnac.com/SearchResult/ResultList.aspx?Search=casque&f=attributes.brand__Marque-Apple&SortTypes=LowestPrice",
        "https://www.fnac.com/Apple-iPhone-15-6-1-5G-Double-SIM-128-Go-Noir/a18573408/w-4",
        "https://www.fnac.com/mp54113208/Apple-iPhone-16-6-1-5G-128-Go-e-SIM-Rose/w-4",
        "https://www.fnac.com/Apple-iMac-Mac-Mac-Mini-Mac-Pro/shi352415/w-4",
        "https://www.fnac.com/Accessoires-Apple-Watch/Apple-Watch/nsh558378/w-4",
        "https://www.fnac.com/a21960704",
    ],
    "brands": [],
    "searchTerms": [],
    "proxy": { "useApifyProxy": True },
    "incrementalMode": False,
    "emitUnchanged": False,
    "emitExpired": False,
    "ignoreFieldsForChanges": [],
}

# Run the Actor and wait for it to finish
run = client.actor("abotapi/fnac-com-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 '{
  "mode": "search",
  "searchTerm": "iphone",
  "urls": [
    "https://www.fnac.com/SearchResult/ResultList.aspx?Search=iphone",
    "https://www.fnac.com/SearchResult/ResultList.aspx?Search=iphone&PageIndex=2",
    "https://www.fnac.com/SearchResult/ResultList.aspx?Search=casque&f=attributes.brand__Marque-Apple&SortTypes=LowestPrice",
    "https://www.fnac.com/Apple-iPhone-15-6-1-5G-Double-SIM-128-Go-Noir/a18573408/w-4",
    "https://www.fnac.com/mp54113208/Apple-iPhone-16-6-1-5G-128-Go-e-SIM-Rose/w-4",
    "https://www.fnac.com/Apple-iMac-Mac-Mac-Mini-Mac-Pro/shi352415/w-4",
    "https://www.fnac.com/Accessoires-Apple-Watch/Apple-Watch/nsh558378/w-4",
    "https://www.fnac.com/a21960704"
  ],
  "brands": [],
  "searchTerms": [],
  "proxy": {
    "useApifyProxy": true
  },
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "ignoreFieldsForChanges": []
}' |
apify call abotapi/fnac-com-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,abotapi/fnac-com-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/NZxTFckNsgO9IUgPH/builds/OM4il0nrsfxBfUjdz/openapi.json
