Carrefour Spain Scraper - Products, Prices, Nutrition
Pricing
from $1.50 / 1,000 product results
Carrefour Spain Scraper - Products, Prices, Nutrition
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.
Pricing
from $1.50 / 1,000 product results
Rating
0.0
(0)
Developer
Abot API
Maintained by CommunityActor stats
1
Bookmarked
12
Total users
7
Monthly active users
3 days ago
Last modified
Categories
Share
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
- Pick a mode:
search(by keyword),category(a Carrefour category link OR a plain category name likeleche), orurl(paste product links, bare product ids, category links, promotions links and search-result links). - 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.
- Turn Fetch product details on or off, and set Max products and Max pages to control run size and cost.
- Click Start, then download the dataset as JSON, CSV or Excel, or read it through the API.
Search by keyword:
{"mode": "search","queries": ["leche"],"maxItems": 30}
Browse a category, promotions only:
{"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:
{"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 |
&-escaped link | a link copied out of rendered HTML/email with & 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:
{"mode": "search","queries": ["leche"],"incrementalMode": true}
Run it from your code
Python:
from apify_client import ApifyClientclient = 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:
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 asNEW. Later runs returnNEWandUPDATED(withchangedFieldslisting what changed); unchanged products are suppressed and not billed unlessemitUnchangedis on. Turn onemitExpiredto also mark, once a run fully scans the tracked search (not capped by Max products and not a resume), products that vanished asEXPIRED; a product only comes back asREAPPEAREDafter it was previously markedEXPIREDthis way, so withemitExpiredleft off (the default)REAPPEAREDnever occurs. UsestateKeyto 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 theUPDATED/changedFieldsdecision without ever removing them from the row itself: they're still returned, they just can't flip a product toUPDATEDon 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 nestedoffer.units_in_stock(the same number, duplicated inside the rawofferblock) both moved on an otherwise-unchanged product, a counter that ticks within minutes rather than a real product change. Nothing else is excluded:images, theofferblock's price keys (offer.price,offer.app_price,offer.price_per_unit) andrating/reviewCountwere byte-identical across the same double-scrape, so a genuine price or rating change still reportsUPDATED. 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
UPDATEDjust because its details went missing. Such a row, when returned, is markeddetailCarriedForward: trueand 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
reviewslist (andreviewsFetched) is never compared between runs, because its length follows your Max reviews per product setting: changing that setting must not flag every product asUPDATED. A new customer review still shows up, as a change inreviewCountand usuallyrating.
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.
- Authorize a connector once under Apify, Settings, Integrations (Notion, Linear, Airtable, or Apify).
- Select it in the "Pipe results into your apps" input field. (If the picker is empty, you haven't authorized a connector yet.)
- For Notion, also set
notionParentPageUrlto 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.
{"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) 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:
| π₯¦ Mercadona Scraper Scrape Mercadona products by delivery-area postal code. Search by keyword or category, or... | π₯¦ Woolworths AU Scraper Scrape Woolworths Australia (woolworths.com.au) grocery products and reviews. Search by... |
| π₯¦ Coles AU Scraper Scrape Coles.com.au grocery products and customer reviews. Search by keyword, browse... | π₯¦ Lidl.es Products, Weekly Offers, Prices & Reviews Scraper Scrape Lidl.es grocery and non-food products across weekly offers, tools, home, garden... |
| π₯¦ E.Leclerc Scraper Scrape E.Leclerc France (e.leclerc) grocery and retail products. Search by keyword or... | π₯¦ FoodHero Scraper Scrape FoodHero surplus grocery deals across Canada by area, store or offer ID. Extract... |
π Browse all abotapi scrapers
π¬ Support & custom scrapers
- π Found a bug or a missing field? Open a ticket on the Issues tab. We usually reply within hours.
- π οΈ Need another site, extra fields or a private build? Email contact@abotapi.com or message Telegram @abotapi.
- β Enjoying it? A quick review on the actor page helps other users find it.