# Carrefour Spain Scraper - Products, Prices, Nutrition (`abotapi/carrefour-es-scraper`) Actor

Scrape Carrefour Spain (carrefour.es) products. Search by keyword, browse a category, or paste product/category links. Returns name, brand, price, unit price, promotion, stock, category, image, and, with details, the barcode (EAN), ingredients, allergens, nutrition, extra images and rating.

- **URL**: https://apify.com/abotapi/carrefour-es-scraper.md
- **Developed by:** [Abot API](https://apify.com/abotapi) (community)
- **Categories:** E-commerce, Automation, Developer tools
- **Features:** Limited permissions
- **Stats:** 19 total users, 4 monthly users, 91.4% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

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

## Carrefour Spain Scraper: Products, Prices, Nutrition and Stock

Carrefour Scraper turns the Carrefour Spain (carrefour.es) supermarket catalog into structured product data. Search by keyword, browse a category, or paste product and category links, and get back price and category for every product; category browsing and category links add unit price, promotion and stock; and switching on detail adds barcode (EAN), ingredients, allergens, the full nutrition panel, extra images, the aggregate rating and the most recent customer reviews. Filter by price, rating, promotion or stock (promotion and stock filters work in every mode: search, category and URL; when the listing card itself doesn't already carry that status in search mode, the product page is read to resolve it, and that product is then billed as detail-enriched), then export to JSON, CSV or Excel, or pull results straight into your app through the API.

### Why This Scraper?

- **Three ways to find products.** Search by keyword, browse a category page, or paste product links, bare product ids and category links in one mixed list.
- **Full product detail on demand.** Add barcode (EAN), ingredients, allergens, the complete nutrition panel, extra images, the aggregate rating and up to 20 recent customer reviews per product (adjustable), or turn detail off for a lighter, listing-only run.
- **Built-in filters and sorting.** Price range, brand, minimum rating, promotion-only and in-stock-only, and eight sort orders (including top rated and promotions first), applied without writing any code.
- **Built for schedules.** Incremental mode returns only new and changed products on repeat runs against the same search, and unchanged products are never billed unless you explicitly ask for them.
- **Resume without duplicate charges.** Continue one interrupted run from its run or dataset id, or let the actor recover on its own after a platform restart, with no products re-collected or re-billed.
- **Send results into your apps.** Pipe a condensed summary of every product into Notion, Linear or Airtable through MCP connectors, no extra code required.
- **Built for a residential connection.** The actor reaches the storefront through a Spanish residential connection by default and automatically retries with a different connection when one is refused. See Plan Requirement for what to expect on a free plan.

### Use Cases

- **Grocery price monitoring:** track price, promotion and unit price changes across a shopping basket over time.
- **Catalog and range research:** build a structured view of Carrefour Spain's assortment by category for pricing or assortment analysis.
- **Nutrition and ingredient research:** collect ingredients, allergens and the full nutrition panel for dietary, labeling or compliance projects.
- **Deal and promotion tracking:** filter to promoted or in-stock products only, and get notified of new deals on a recurring schedule.
- **Wine and specialty catalog research:** pull tasting notes and wine-specific detail fields alongside standard product data.

### Data You Get

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

| Field | Example |
| --- | --- |
| `productId` | `"520661336"` |
| `name` | `"Leche semidesnatada marca ejemplo brik 1 L"` |
| `brand` | `"Marca Ejemplo"` |
| `ean` | `"8400000000000"` |
| `sku` | `"1895370000"` |
| `price` | `0.99` |
| `regularPrice` | `1.19` |
| `discountPercentage` | `17` |
| `onPromotion` | `true` |
| `currency` | `"EUR"` |
| `unitPrice` / `unitPriceMeasure` | `0.99` / `"l"` |
| `stock` / `inStock` | `240` / `true` |
| `sellPackUnit` | `1` |
| `categoryId` | `"cat00000"` |
| `image` | `"https://static.carrefour.es/example/000000.jpg"` |
| `images` | list of extra image URLs (when details are fetched) |
| `ingredients` | `"Leche de vaca semidesnatada."` |
| `allergens` | `["Leche"]` |
| `nutrition` | nutrition panel values (when details are fetched) |
| `rating` / `reviewCount` | `4.5` / `12` |
| `reviews` / `reviewsFetched` | up to 20 most recent customer reviews, each `{"reviewId", "rating", "title", "text", "date", "reviewerName"}`, and how many were returned (with details on) |
| `url` | link to the product page on carrefour.es |
| `searchMode` / `source` | `"search"`, `"category"`, or `"product"`, how this record was found (a pasted search-result link in url mode also reports `"search"`) |
| `scrapedAt` | `"2026-01-01T00:00:00Z"` |

Any product returned with details also carries the label data printed on the pack where the store publishes it (`legalName`, `manufacturer`, `manufacturerAddress`, `netContent`, `packagingFormat`, `healthRegistrationNumber`, `alcoholByVolume` for drinks, `countryOfOrigin`, `storageInstructions`, `nutriScore`), an additional info block (`moreInfo`) and a raw offer block (`offer`) alongside the fields above; wine products additionally carry tasting notes and wine details (`tastingNote`, `wineInfo`). When `incrementalMode` is on, every returned record also carries `changeType` (`NEW`, `UPDATED`, `UNCHANGED`, `REAPPEARED`, or `EXPIRED`), `changedFields` (populated only for `UPDATED`), `firstSeenAt` and `lastSeenAt`.

### How to Use

1. Pick a **mode**: `search` (by keyword), `category` (a Carrefour category link OR a plain category name like `leche`), or `url` (paste product links, bare product ids, category links, promotions links and search-result links).
2. Fill in the field for that mode, then narrow the results with the price, rating, promotion or stock filters, and pick a sort order if you want one.
3. Turn **Fetch product details** on or off, and set **Max products** 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.

**Search by keyword:**

```json
{
  "mode": "search",
  "queries": ["leche"],
  "maxItems": 30
}
```

**Browse a category, promotions only:**

```json
{
  "mode": "category",
  "categories": ["https://www.carrefour.es/supermercado/lacteos-huevos/leche/cat20093/c"],
  "onPromotionOnly": true,
  "maxItems": 100
}
```

**Paste a product link and a category link together:**

```json
{
  "mode": "url",
  "urls": [
    "https://www.carrefour.es/supermercado/R-521007071/p",
    "https://www.carrefour.es/supermercado/lacteos-huevos/leche/cat20093/c"
  ]
}
```

#### URL mode: every link shape accepted

Every entry in `urls` is routed automatically, so you can mix shapes freely in one list.

| Shape | Example | Routed as |
| --- | --- | --- |
| Product link | `https://www.carrefour.es/supermercado/<any-slug>/R-521007071/p` | Product, full detail. The slug text next to the id is ignored: Carrefour resolves by the `R-<id>` segment, never by a slug text search (confirmed live, #2802), so a wrong or outdated slug still lands on the right product. |
| Marketplace / non-food product link | `https://www.carrefour.es/glorioso-blanco-2025-vino-blanco-espana-rioja-doca-75-cl-120/8410351213004/p` (barcode form) or `https://www.carrefour.es/extractor-portatil-de-leche-kikkaboo/VC4A-27082334/p` (catalogue-code form) | Product, full detail, including the seller (`sellerName`, `isMarketplace`). These pages have no `/supermercado/.../R-<id>/p` form, so the pasted link itself is read. |
| Bare product id | `521007071` or `R-521007071` (incl. marketplace ids like `VC4AECOMM-700248`) | Product, full detail |
| Category link | `https://www.carrefour.es/supermercado/.../cat20093/c` | Category, all result pages. Same ID-not-slug resolution as product links. |
| "Ofertas" promotions link | `https://www.carrefour.es/supermercado/bebidas-promocion/F-1086Z13rjo/c` | Category, all result pages (`/F-<token>/c` carries a real product grid, same as a `catNNNNN/c` page) |
| Search-result link | `https://www.carrefour.es/?q=leche` or `https://www.carrefour.es/#!q=leche` | The same search as typing `leche` into **Search keywords**, with your filters applied |
| Tracking-param variant | any of the above with `?utm_source=...`, `?gclid=...`, etc. appended | Unaffected; tracking params are ignored, not matched against |
| `&amp;`-escaped link | a link copied out of rendered HTML/email with `&amp;` instead of `&` | Decoded automatically before parsing |
| `m.carrefour.es` link | `https://m.carrefour.es/supermercado/R-521007071/p` | Normalized to `www.carrefour.es` and routed as above. No separate mobile site exists live today (probed 2026-10-06), but a stale bookmark/app-share link is still handled. |
| Non-food category | any category shape above under a non-food department (`Mascotas`, `Parafarmacia`, `Cuidado personal e Higiene`, ...) | Same routing as a grocery category |
| Anything else (e.g. a bare marketplace barcode or `VC4A-` code without its product link, or a `/tecno/` link) | n/a | Reported with a loud per-run-log warning naming the exact URL, never a silent empty result |

**Default behavior:** the `urls` field ships with one live-verified example of every shape above (product, bare id, barcode-form and catalogue-code marketplace product, category, promotions category, `?q=` and `#!q=` search links). If you run url mode and leave the field untouched, the actor now scrapes those examples instead of failing on an empty list; clear or replace the list to scrape your own links.

A category NAME in `categories` (or a bare name typed where a link is expected) is resolved the same way, against Carrefour's own category names, not a raw id. A name that doesn't match anything is reported in the run log; paste the category's own link instead.

**Daily monitoring of the same search:**

```json
{
  "mode": "search",
  "queries": ["leche"],
  "incrementalMode": true
}
```

#### Run it from your code

Python:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("abotapi/carrefour-es-scraper").call(run_input={"mode": "search", "queries": ["leche"]})
for product in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(product["name"], product["price"])
```

JavaScript:

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

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('abotapi/carrefour-es-scraper').call({ mode: 'search', queries: ['leche'] });
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 specific interrupted or previous run: paste its run id or dataset id, and this run skips products already collected there, returning only the remaining new products. An automatic checkpoint also protects a long run against a platform restart, with no input needed.
- **Incremental mode** (`incrementalMode`) is for scheduling the same search again and again, for example daily. The actor remembers the previous run of that search by itself, so no id needs to be pasted. The first run returns everything as `NEW`. Later runs return `NEW` and `UPDATED` (with `changedFields` listing what changed); unchanged products are suppressed and not billed unless `emitUnchanged` is on. Turn on `emitExpired` to also mark, once a run fully scans the tracked search (not capped by Max products and not a resume), products that vanished as `EXPIRED`; a product only comes back as `REAPPEARED` after it was previously marked `EXPIRED` this way, so with `emitExpired` left off (the default) `REAPPEARED` never occurs. Use `stateKey` to name a monitoring campaign or deliberately share state; leave it empty to let the actor derive one automatically from the search and detail settings.
- **Ignore fields when detecting changes** (`ignoreFieldsForChanges`, incremental mode only) excludes field names from the `UPDATED`/`changedFields` decision without ever removing them from the row itself: they're still returned, they just can't flip a product to `UPDATED` on their own. Two fields are excluded by default, measured on a live double-scrape of the same category ~11 minutes apart: `stock` (the live inventory count) and the nested `offer.units_in_stock` (the same number, duplicated inside the raw `offer` block) both moved on an otherwise-unchanged product, a counter that ticks within minutes rather than a real product change. Nothing else is excluded: `images`, the `offer` block's price keys (`offer.price`, `offer.app_price`, `offer.price_per_unit`) and `rating`/`reviewCount` were byte-identical across the same double-scrape, so a genuine price or rating change still reports `UPDATED`. If your own monitored products turn out noisy in some other way, add entries yourself, for example a plain name for a top-level field (`"stock"`, already excluded by default), `"parent.child"` for one field nested inside another (`"offer.price"`), `"parent.*"` for every field nested inside another, or `"field[]"` for a list field (`"images[]"`). If you do exclude a stock-type field, `inStock` (`true`/`false`) keeps tracking real stock-outs and restocks for you, since it is a separate field you'd have to exclude on its own.
- **A product page that could not be read.** If a scheduled incremental run cannot load a tracked product's page (a connection problem), the product is compared using its listing data plus the page details from the previous run, so it is not reported as `UPDATED` just because its details went missing. Such a row, when returned, is marked `detailCarriedForward: true` and is not billed for detail enrichment. A product the store has delisted (its page now redirects) is still reported as changed.
- **Reviews in incremental mode.** The nested `reviews` list (and `reviewsFetched`) is never compared between runs, because its length follows your **Max reviews per product** setting: changing that setting must not flag every product as `UPDATED`. A new customer review still shows up, as a change in `reviewCount` and usually `rating`.

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

Optionally pipe the scraped products 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 haven't authorized a connector yet.)
3. For **Notion**, also set `notionParentPageUrl` to the page where item pages 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.

### Input Parameters

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `mode` | string | `search` | `search`, `category`, or `url`. (`product` is a hidden legacy alias of `url` mode, kept only so a Task saved before 2026-07-17 keeps running; new tasks should use `url`.) |
| `queries` | array | none (prefill: `["leche"]`) | Keywords to search, search mode only. Each is searched separately. |
| `categories` | array | none (prefill: a category link and a plain name) | Carrefour category page links OR plain category names (e.g. `leche`, `Vinos`) to browse, category mode only. |
| `urls` | array | one example of every supported shape (same as the prefill) | Carrefour Spain product links, bare product ids, category links, promotions links and search-result links to scrape directly, url mode only; see the shapes table above. |
| `products` | array | none (prefill: one example link) | Legacy alias, only used when mode = product (a hidden field kept so a Task saved before 2026-07-17 keeps running). |
| `sortBy` | string | `relevance` | Order applied over the collected products: relevance, price low-high/high-low, unit price low-high/high-low, name, top rated first, or promotions first (the last two order the products the run collects, after their product pages are read). |
| `minRating` | string | `0` | Keep only products rated at least this many stars. Automatically fetches product details for filtered products. |
| `onPromotionOnly` | boolean | `false` | Keep only products currently on promotion. |
| `inStockOnly` | boolean | `false` | Keep only products currently in stock. |
| `minPrice` | integer | none | Keep only products priced at or above this amount, in EUR. Never filters out a pasted product link. |
| `maxPrice` | integer | none | Keep only products priced at or below this amount, in EUR. Never filters out a pasted product link. |
| `brands` | array | none | Keep only products of these brands (exact brand name, not case-sensitive). Never filters out a pasted product link. |
| `fetchDetails` | boolean | `true` | Fetch barcode, ingredients, allergens, nutrition, extra images, rating and customer reviews. Off returns listing-level fields only, except pasted product links, which always fetch full detail. |
| `maxReviews` | integer | `20` | With details on, the most recent customer reviews nested in each product's `reviews` list (never separate rows). `0` skips review reads; products without reviews cost no extra request. |
| `maxItems` | integer | `20` | Maximum products returned across the whole run. `0` means unlimited. |
| `maxPages` | integer | `0` | Maximum result pages walked per keyword, category or link. `0` means unlimited. |
| `resumeFromRunId` | string | none | Continue one specific interrupted run or dataset id, skipping products already collected there. |
| `incrementalMode` | boolean | `false` | Return only new and changed products on repeat runs against the same search. |
| `stateKey` | string | none | Name or share an incremental-mode monitoring campaign. Derived automatically when left empty. |
| `emitUnchanged` | boolean | `false` | Incremental mode only. Also return, and bill, products unchanged since the last run. |
| `emitExpired` | boolean | `false` | Incremental mode only. Also return, and bill, products no longer found, once a run has fully scanned the tracked search. |
| `ignoreFieldsForChanges` | array | `[]` (empty) | Incremental mode only. Field names/paths (`"stock"`, `"offer.price"`, `"offer.*"`, `"images[]"`) excluded from the `UPDATED`/`changedFields` decision; the fields themselves are still returned. |
| `proxy` | object | Spanish residential (`{"useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"], "apifyProxyCountry": "ES"}`) | Connection settings. |
| `mcpConnectors` | array | none | Optional: send a summary of each product to apps you authorized under Integrations. |
| `notionParentPageUrl` | string | none | Notion connector only: page under which item pages are created. |
| `maxNotifyListings` | integer | `50` | Cap on items written to each connector per run. |
| `searchQuery` | string | none | Hidden alternative name for `queries` (one keyword). |
| `searchQueries` | array | none | Hidden alternative name for `queries`. |
| `categoryUrl` | string | none | Hidden alternative name for `categories` (one link). |
| `categoryUrls` | array | none | Hidden alternative name for `categories`. |
| `startUrls` | array | none | Hidden alternative name for `urls`. |
| `productUrls` | array | none | Hidden alternative name for `urls`. |
| `maxResults` | integer | none | Hidden alternative name for `maxItems`. |
| `maxResultsPerScrape` | integer | none | Hidden alternative name for `maxItems`. |
| `promotionsOnly` | boolean | none | Hidden alternative name for `onPromotionOnly`. |
| `proxyConfiguration` | object | none | Hidden alternative name for `proxy`. |

#### Dataset views

The dataset has an overview view (the key fields at a glance), a full-detail view (every field including nutrition, label data and reviews) and, for incremental runs, a changes view (`changeType`, `changedFields`, timestamps).

### Output Example

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

```json
{
  "productId": "520661336",
  "name": "Leche semidesnatada marca ejemplo brik 1 L",
  "brand": "Marca Ejemplo",
  "ean": "8400000000000",
  "sku": "1895370000",
  "price": 0.99,
  "regularPrice": 1.19,
  "discountPercentage": 17,
  "onPromotion": true,
  "currency": "EUR",
  "unitPrice": 0.99,
  "unitPriceMeasure": "l",
  "stock": 240,
  "inStock": true,
  "ingredients": "Leche de vaca semidesnatada.",
  "allergens": ["Leche"],
  "nutrition": { "main_nutritional_values": [{ "nombre": "Proteínas (g)", "valor": "3 g" }] },
  "rating": 4.5,
  "reviewCount": 12,
  "reviewsFetched": 1,
  "reviews": [
    {
      "reviewId": "example-review-1",
      "rating": 5,
      "title": "Muy buena",
      "text": "Texto de ejemplo de una opinión.",
      "date": "2026-01-01T10:00:00.000+01:00",
      "reviewerName": "Cliente Ejemplo"
    }
  ],
  "image": "https://static.carrefour.es/example/000000.jpg",
  "images": ["https://static.carrefour.es/example/000000.jpg"],
  "url": "https://www.carrefour.es/supermercado/example/R-520661336/p",
  "searchMode": "category",
  "source": "category",
  "scrapedAt": "2026-01-01T00:00:00Z"
}
```

### Plan Requirement

Carrefour Spain's storefront is protected by an aggressive bot wall that refuses most connections, so this is **not** a "works with no action needed" actor. It is set up to use a Spanish residential connection by default, and that setting is the best available, but residential access does not guarantee a page: a refused connection is simply retried on a fresh one, and the actor can still end a run with no data when the site refuses every attempt. Datacenter connections are refused outright, so a plan without residential proxy access gets noticeably worse results.

If your Apify plan does not include residential proxy access (free plans), the log shows this once per run:

> 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. For residential access, upgrade at https://apify.com/pricing or supply your own proxy URLs.

and, when the backup connection then serves data, an info line: "Served over the backup connection (the default connection was unavailable)." For the most reliable results use a plan with residential proxy access ([pricing](https://apify.com/pricing)) or supply your own proxy URLs.

### FAQ

#### How much does it cost?

Pay per event. On the Free tier: $0.0025 per product returned, plus $0.001 per product where full detail was fetched and returned (detail is on by default, so about $3.50 per 1,000 products with details, $2.50 per 1,000 card-only), plus a one-time actor start of $0.02. Paid Apify plans get lower per-event rates (down to $0.0015 and $0.0006). Customer reviews have no separate charge (set **Max reviews per product** to `0` to skip reading them). The **Pricing** tab always shows the current rates. Use **Max products** to cap the cost of any run.

#### Is it legal to scrape Carrefour Spain?

This actor collects only publicly available product data. You are responsible for how you use it: follow Carrefour's terms and the laws that apply to you, and get legal advice if you plan commercial redistribution. Prices and product facts are generally not protected, but product photos and descriptions may be subject to third-party rights.

#### 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, and unchanged ones are not billed. Products only ever come back marked `REAPPEARED` if `emitExpired` was previously turned on and a full scan had already marked them `EXPIRED`; with `emitExpired` off, expect only `NEW` and `UPDATED`.

#### What's the difference between Resume and Incremental mode?

**Resume** (`resumeFromRunId`) continues one specific interrupted or earlier run, so you paste an id and get the remaining new products from that same crawl. **Incremental mode** is for scheduling the same search repeatedly and getting only what changed since last time, with the actor tracking state on its own. Use Resume for a one-off continuation; use Incremental mode for recurring monitoring.

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

The run ends FAILED only when **no usable page could be loaded at all**, because reporting that as an empty dataset would hide a genuine connection problem behind what looks like "no matches." The status message tells you which case you hit:

- **"The proxy connection was refused for this account, so no page could be read. Your Apify plan does not include proxy access; a paid plan is required..."**: every connection was refused at the proxy itself. Check your plan, or your own proxy URLs if you supplied any.
- **"No results: residential proxies are unavailable on this run (Apify free plans do not include them), and the backup connection could not load the requested Carrefour.es pages..."**: a free-plan run whose backup connection could not get past the storefront. Upgrade or supply your own proxy URLs.
- **"Could not load any results page (the requests failed), so nothing was collected. This is a connection problem, not an empty search. Please try again later."**: the storefront refused every connection this run tried. Retrying later usually works.

Missing residential access by itself does not fail the run (see Plan Requirement); a single product page that could not be read quietly falls back to listing-level fields, while pasted product links that cannot be read are dropped. The run also fails outright for an unreadable `resumeFromRunId`, for an empty input, or when Incremental mode is combined with Resume on a search that already has saved state. A run that legitimately found no matching products (a narrow keyword, tight filters, or an incremental run with nothing changed) completes normally with a status message explaining why the dataset is empty.

#### 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 grocery data?

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

<table>
<tr><td>🥦 <a href="https://apify.com/abotapi/mercadona-es-scraper"><b>Mercadona Scraper</b></a><br>Scrape Mercadona products by delivery-area postal code. Search by keyword or category, or...</td><td>🥦 <a href="https://apify.com/abotapi/woolworths-au-scraper"><b>Woolworths AU Scraper</b></a><br>Scrape Woolworths Australia (woolworths.com.au) grocery products and reviews. Search by...</td></tr>
<tr><td>🥦 <a href="https://apify.com/abotapi/coles-au-scraper"><b>Coles AU Scraper</b></a><br>Scrape Coles.com.au grocery products and customer reviews. Search by keyword, browse...</td><td>🥦 <a href="https://apify.com/abotapi/lidl-es-scraper"><b>Lidl.es Products, Weekly Offers, Prices &amp; Reviews Scraper</b></a><br>Scrape Lidl.es grocery and non-food products across weekly offers, tools, home, garden...</td></tr>
<tr><td>🥦 <a href="https://apify.com/abotapi/leclerc-fr-scraper"><b>E.Leclerc Scraper</b></a><br>Scrape E.Leclerc France (e.leclerc) grocery and retail products. Search by keyword or...</td><td>🥦 <a href="https://apify.com/abotapi/foodhero-surplus-grocery-scraper"><b>FoodHero Scraper</b></a><br>Scrape FoodHero surplus grocery deals across Canada by area, store or offer ID. Extract...</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/carrefour-es-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`):

Choose 'search' for keywords + filters, 'category' to browse a category by its Carrefour category link OR a plain category name (e.g. 'leche', 'Vinos'), or 'url' to scrape pasted Carrefour Spain links — product links, bare product ids, category links, promotions links and search-result links are all accepted. 'product' is a legacy alias kept only so an old saved Task that already used it keeps working; new tasks should use 'url' instead.

## `queries` (type: `array`):

Only used when mode = search — ignored in every other mode. One or more keywords to search, for example 'leche' (milk) or 'cerveza' (beer). Each keyword is searched separately.

## `categories` (type: `array`):

Only used when mode = category — ignored in every other mode. A category page link, OR a plain category name such as 'leche' or 'Vinos' (resolved against Carrefour Spain's own category names — an unmatched name is reported, never guessed; paste the category's own link if your name doesn't resolve). To get a link: open carrefour.es, browse to the category you want, and copy the address from your browser — a category address ends in /catNNNNN/c (or /F-<token>/c for an "Ofertas" promotions category), for example https://www.carrefour.es/supermercado/lacteos-huevos/leche/cat20093/c

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

Only used when mode = url — ignored in every other mode. Mix freely: product pages under /R-<id>/p , marketplace / non-food product pages under /<name>/<barcode>/p or /<name>/VC4A-<code>/p , bare product ids such as 521007071 , category pages under /catNNNNN/c , "Ofertas" promotions categories under /F-<token>/c , or a pasted search-result link (?q=... or #!q=...). Multiple entries supported.

## `products` (type: `array`):

Legacy alias, only used when mode = product — ignored in every other mode. Carrefour product page links (or bare product ids), for example https://www.carrefour.es/supermercado/leche-semidesnatada-la-cantara-brik-1-l/R-VC4AECOMM-700248/p . Multiple entries supported. New tasks should use 'url' mode + the 'urls' field instead; this field exists only so a Task saved before 2026-07-17 keeps validating and running.

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

How to order the collected products. Price sorts on a keyword search are done by the store's own search, so the run stops as soon as it has enough; the other orderings are applied over the products gathered in the run before they are returned. 'Top rated first' and 'Promotions first' need product-page data (rating, promotion), so they order the products the run collects after reading their product pages — the cheapest Max products window, not the whole catalogue.

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

Optional. Only keep products whose average rating is at least this many stars. Requires details (ratings are collected during 'Fetch product details'). Products with no rating are excluded when this is set.

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

Optional. Keep only products currently on promotion — a cut price (was-price shown) or a multi-buy / basket promotion such as '3x2', 'Comprando 12' or '2ª unidad -50%'. Works in every mode. A keyword search does not carry promotion data, so its product pages are read to check (billed as detail-enriched for the products returned, even with 'Fetch product details' off).

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

Optional. Keep only products currently in stock. Works in every mode. A keyword search does not carry stock data, so its product pages are read to check (billed as detail-enriched for the products returned, even with 'Fetch product details' off). A product whose stock the store does not publish is kept, and counted in the run log.

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

Optional. Only keep products priced at or above this amount. Narrows a keyword search (filtered by the store's own search, so fewer pages are read), a category, or a pasted category link — a pasted product link is always returned as asked for, regardless of this filter.

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

Optional. Only keep products priced at or below this amount. Narrows a keyword search (filtered by the store's own search, so fewer pages are read), a category, or a pasted category link — a pasted product link is always returned as asked for, regardless of this filter.

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

Optional. Keep only products of these brands (e.g. 'Carrefour', 'Central Lechera Asturiana'; not case-sensitive, exact brand name). A keyword search is narrowed by the store's own search, so fewer pages are read; a category or pasted category link is narrowed on the listing. A pasted product link is always returned as asked for, regardless of this filter.

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

Collect each product's full detail (barcode / EAN, ingredients, allergens, nutrition panel, extra images, description, category path, origin, manufacturer, net content, storage and usage instructions, Nutri-Score, marketplace seller) plus its aggregate rating and customer reviews (see Max reviews per product), and — for keyword-search results — the promotion, was-price, unit price and stock that the search listing does not carry. Turn off for a faster, lighter run that returns only the listing-level fields (name, brand, price, unit price, image; plus promotion and stock in a category).

## `maxReviews` (type: `integer`):

With Fetch product details on, the most recent customer reviews collected for each product that has any (star rating, title, review text, date and the reviewer's public display name), nested in the product's `reviews` list — never as separate rows. Default 20; 0 skips review reads entirely. Products without reviews cost no extra request. Reviews are included in the per-product price with no separate review charge.

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

Maximum number of products to return across the whole run. This is the run's cap. Use 0 for unlimited.

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

Maximum result pages walked per keyword / category / link. 0 = unlimited (walk all pages) — the run then stops only at Max products or when a page repeats no new products.

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

Apify Proxy is required for reliable results. The actor connects through a Spanish residential exit by default.

## `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 catalogue pull in another run. For recurring daily 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 search/category/URL and detail-mode 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 search/category/URL and detail 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 the tracked search — 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 to exclude from change detection in Incremental mode — those fields are still returned in every row, they just never cause a product to be classified UPDATED or appear in changedFields. Use a plain name for a top-level field (e.g. "stock"), "parent.child" for one field nested inside another (e.g. "offer.price"), "parent.\*" for every field nested inside another, or "field\[]" for a list field (e.g. "images\[]"). Empty by default — nothing is excluded unless you add it here. (The nested `reviews` list is never compared: its length depends on Max reviews per product, so a new review shows up as a change in reviewCount / rating instead.) If you exclude stock-type fields, "inStock" (true/false) still changes on a real stock-out/restock, so you keep a stable signal for that.

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

Optionally send results into the apps you already use, via Model Context Protocol (MCP) connectors. Authorize one under Apify, Settings, API & Integrations, then select it here. Notion gets a rich page-per-item export; other connectors get a best-effort write/digest. Leave empty to skip; never changes the dataset output. 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.

## `searchQuery` (type: `string`):

Alternative spelling accepted for compatibility with other Carrefour Spain scrapers; prefer 'queries'.

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

Alternative spelling accepted for compatibility with other Carrefour Spain scrapers; prefer 'queries'.

## `categoryUrl` (type: `string`):

Alternative spelling accepted for compatibility with other Carrefour Spain scrapers; prefer 'categories'.

## `categoryUrls` (type: `array`):

Alternative spelling accepted for compatibility with other Carrefour Spain scrapers; prefer 'categories'.

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

Alternative spelling accepted for compatibility with other Carrefour Spain scrapers; prefer 'urls'.

## `productUrls` (type: `array`):

Alternative spelling accepted for compatibility with other Carrefour Spain scrapers; prefer 'urls'.

## `maxResults` (type: `integer`):

Alternative spelling accepted for compatibility with other Carrefour Spain scrapers; prefer 'maxItems'.

## `maxResultsPerScrape` (type: `integer`):

Alternative spelling accepted for compatibility with other Carrefour Spain scrapers; prefer 'maxItems'.

## `promotionsOnly` (type: `boolean`):

Alternative spelling accepted for compatibility with other Carrefour Spain scrapers; prefer 'onPromotionOnly'.

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

Alternative spelling accepted for compatibility with other Carrefour Spain scrapers; prefer 'proxy'.

## Actor input object example

```json
{
  "mode": "search",
  "queries": [
    "leche"
  ],
  "categories": [
    "https://www.carrefour.es/supermercado/lacteos-huevos/leche/cat20093/c",
    "Vinos"
  ],
  "urls": [
    "https://www.carrefour.es/supermercado/R-521007071/p",
    "836507271",
    "https://www.carrefour.es/glorioso-blanco-2025-vino-blanco-espana-rioja-doca-75-cl-120/8410351213004/p",
    "https://www.carrefour.es/extractor-portatil-de-leche-kikkaboo/VC4A-27082334/p",
    "https://www.carrefour.es/supermercado/lacteos-huevos/leche/cat20093/c",
    "https://www.carrefour.es/supermercado/bebidas-promocion/F-1086Z13rjo/c",
    "https://www.carrefour.es/?q=leche",
    "https://www.carrefour.es/#!q=cerveza"
  ],
  "products": [
    "https://www.carrefour.es/supermercado/leche-semidesnatada-la-cantara-brik-1-l/R-VC4AECOMM-700248/p"
  ],
  "sortBy": "relevance",
  "minRating": "0",
  "onPromotionOnly": false,
  "inStockOnly": false,
  "fetchDetails": true,
  "maxReviews": 20,
  "maxItems": 20,
  "maxPages": 0,
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "ES"
  },
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "ignoreFieldsForChanges": [],
  "maxNotifyListings": 50
}
```

# Actor output Schema

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

No description

## `fullDetail` (type: `string`):

No description

## `changes` (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 = {
    "mode": "search",
    "queries": [
        "leche"
    ],
    "categories": [
        "https://www.carrefour.es/supermercado/lacteos-huevos/leche/cat20093/c",
        "Vinos"
    ],
    "urls": [
        "https://www.carrefour.es/supermercado/R-521007071/p",
        "836507271",
        "https://www.carrefour.es/glorioso-blanco-2025-vino-blanco-espana-rioja-doca-75-cl-120/8410351213004/p",
        "https://www.carrefour.es/extractor-portatil-de-leche-kikkaboo/VC4A-27082334/p",
        "https://www.carrefour.es/supermercado/lacteos-huevos/leche/cat20093/c",
        "https://www.carrefour.es/supermercado/bebidas-promocion/F-1086Z13rjo/c",
        "https://www.carrefour.es/?q=leche",
        "https://www.carrefour.es/#!q=cerveza"
    ],
    "products": [
        "https://www.carrefour.es/supermercado/leche-semidesnatada-la-cantara-brik-1-l/R-VC4AECOMM-700248/p"
    ],
    "maxReviews": 20,
    "proxy": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "ES"
    },
    "incrementalMode": false,
    "emitUnchanged": false,
    "emitExpired": false,
    "ignoreFieldsForChanges": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("abotapi/carrefour-es-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",
    "queries": ["leche"],
    "categories": [
        "https://www.carrefour.es/supermercado/lacteos-huevos/leche/cat20093/c",
        "Vinos",
    ],
    "urls": [
        "https://www.carrefour.es/supermercado/R-521007071/p",
        "836507271",
        "https://www.carrefour.es/glorioso-blanco-2025-vino-blanco-espana-rioja-doca-75-cl-120/8410351213004/p",
        "https://www.carrefour.es/extractor-portatil-de-leche-kikkaboo/VC4A-27082334/p",
        "https://www.carrefour.es/supermercado/lacteos-huevos/leche/cat20093/c",
        "https://www.carrefour.es/supermercado/bebidas-promocion/F-1086Z13rjo/c",
        "https://www.carrefour.es/?q=leche",
        "https://www.carrefour.es/#!q=cerveza",
    ],
    "products": ["https://www.carrefour.es/supermercado/leche-semidesnatada-la-cantara-brik-1-l/R-VC4AECOMM-700248/p"],
    "maxReviews": 20,
    "proxy": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "ES",
    },
    "incrementalMode": False,
    "emitUnchanged": False,
    "emitExpired": False,
    "ignoreFieldsForChanges": [],
}

# Run the Actor and wait for it to finish
run = client.actor("abotapi/carrefour-es-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",
  "queries": [
    "leche"
  ],
  "categories": [
    "https://www.carrefour.es/supermercado/lacteos-huevos/leche/cat20093/c",
    "Vinos"
  ],
  "urls": [
    "https://www.carrefour.es/supermercado/R-521007071/p",
    "836507271",
    "https://www.carrefour.es/glorioso-blanco-2025-vino-blanco-espana-rioja-doca-75-cl-120/8410351213004/p",
    "https://www.carrefour.es/extractor-portatil-de-leche-kikkaboo/VC4A-27082334/p",
    "https://www.carrefour.es/supermercado/lacteos-huevos/leche/cat20093/c",
    "https://www.carrefour.es/supermercado/bebidas-promocion/F-1086Z13rjo/c",
    "https://www.carrefour.es/?q=leche",
    "https://www.carrefour.es/#!q=cerveza"
  ],
  "products": [
    "https://www.carrefour.es/supermercado/leche-semidesnatada-la-cantara-brik-1-l/R-VC4AECOMM-700248/p"
  ],
  "maxReviews": 20,
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "ES"
  },
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "ignoreFieldsForChanges": []
}' |
apify call abotapi/carrefour-es-scraper --silent --output-dataset

```

## MCP server setup

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