# Portugal Supermarket Prices Scraper (`trovevault/portugal-supermarket-prices`) Actor

Scrape current product and promotional prices from Continente, Auchan, and Pingo Doce in one normalized dataset.

- **URL**: https://apify.com/trovevault/portugal-supermarket-prices.md
- **Developed by:** [Trove Vault](https://apify.com/trovevault) (community)
- **Categories:** E-commerce
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.85 / 1,000 products

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Portugal Supermarket Prices Scraper

Extract current product prices from Continente, Auchan, and Pingo Doce into one normalized dataset. Search by keyword, filter by a free-text brand, compare retailers, or leave both filters empty to collect each selected supermarket's complete public online catalog.

It reads actual prices displayed in public online catalogs rather than promotional leaflets. Results include product identity, brand, package size, regular and promotional prices, unit price, currency, image, and product URL.

### Why use this Portugal supermarket price scraper?

Portuguese grocery prices are spread across separate retailer websites with different product structures and promotion labels. This Actor converts those catalogs into one consistent format that can feed:

- price comparison apps and consumer tools;
- retail and competitor price monitoring;
- brand distribution and assortment research;
- promotion analysis;
- procurement and category-management workflows;
- recurring datasets, dashboards, and spreadsheets.

Supported retailers are **Continente, Auchan, and Pingo Doce**.

### How does the supermarket price scraper work?

1. Select the supermarkets.
2. Optionally enter a product keyword, a brand, or both.
3. Run the Actor.
4. Download the normalized dataset as JSON, CSV, Excel, XML, or another Apify-supported format.

```mermaid
flowchart LR
    A[Filters] --> B[Retailer catalogs] --> C[Normalized price dataset]
```

When Keyword and Brand are both empty, the Actor follows public pagination through the selected catalogs. Complete runs are slower and more expensive than filtered searches.

The Actor reads public online catalog pages without requiring a customer login, delivery address, or postal-code input.

### What input does the Actor accept?

| Field | Type | Description |
| --- | --- | --- |
| supermarkets | string array | Dropdown containing Continente, Auchan, and Pingo Doce. Select one or several. |
| keyword | string | Optional product search such as leite, café, arroz, cerveja, or detergente. |
| brand | string | Optional free-text brand filter such as Mimosa, Delta, Compal, or Fairy. |
| proxyConfiguration | object | Optional Apify Proxy configuration for access problems. |
| datasetId | string | Optional existing Apify dataset that receives a second copy of every result. |
| runId | string | Optional workflow or external job identifier copied to every result. |

#### Filtered multi-supermarket search

```json
{
  "supermarkets": ["continente", "auchan", "pingo_doce"],
  "keyword": "leite",
  "brand": "Mimosa",
  "runId": "weekly-milk-prices"
}
```

#### Complete Continente and Auchan catalogs

```json
{
  "supermarkets": ["continente", "auchan"]
}
```

### What supermarket price data does the Actor return?

Each dataset row represents one supermarket product and its currently displayed price.

```json
{
  "supermarket": "Continente",
  "productId": "4370473",
  "productName": "Leite UHT Meio Gordo sem Lactose Mimosa",
  "brand": "Mimosa",
  "category": "Laticínios e Ovos / Leite / Sem Lactose",
  "packageSize": "emb. 6 x 1 lt",
  "price": 7.74,
  "discountPrice": 6.84,
  "unitPrice": "1,14€/lt",
  "currency": "EUR",
  "url": "https://www.continente.pt/produto/...",
  "imageUrl": "https://www.continente.pt/dw/image/...",
  "runId": "weekly-milk-prices"
}
```

#### Output fields

| Field | Meaning |
| --- | --- |
| supermarket | Retailer whose online catalog displayed the product. |
| productId | Stable retailer identifier for matching the same item across repeated exports. |
| productName | Displayed product name. |
| brand | Displayed brand when available. |
| category | Displayed catalog category when available. |
| packageSize | Displayed package quantity, weight, or volume. |
| price | Regular displayed price. When there is no promotion, this is the current price. |
| discountPrice | Lower current promotional price, included only when the retailer explicitly displays a promotion. |
| unitPrice | Displayed comparison price per liter, kilogram, or unit. |
| currency | EUR. |
| url | Direct public product page URL. |
| imageUrl | Displayed product image URL when available. |
| runId | Optional identifier copied from the input. |

The absence of discountPrice means that the Actor did not find a genuine lower promotional price on that product card. It does not mean the product has never been promoted.

### How are regular and promotional prices represented?

Price interpretation matters when comparing supermarkets:

- A product without an explicit promotion has one price in the price field.
- A promoted product keeps the crossed-out or stated reference price in price and places the lower current amount in discountPrice.
- Unit price remains the retailer's displayed text because units and locale formatting vary.
- The Actor does not calculate historical changes.
- The Actor does not label ordinary prices as discounts.
- Shipping, basket discounts, loyalty-only benefits, coupons, and checkout-specific adjustments are not included unless they are already displayed on the public product card.

### How do I run the Actor through the Apify API?

Run the Actor through the Apify API:

```bash
curl -X POST \
  "https://api.apify.com/v2/acts/trovevault~portugal-supermarket-prices/runs" \
  -H "Authorization: Bearer YOUR_APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "supermarkets": ["continente", "auchan", "pingo_doce"],
    "keyword": "café",
    "brand": "Delta"
  }'
```

After the run finishes, export its default dataset in the Console or through the API. Keep tokens in server-side secret storage.

### How can I integrate the Actor into a price pipeline?

Use datasetId to append results to a longer-lived Apify dataset while still receiving the normal dataset for each run. Use runId to associate rows with a workflow, snapshot, client, or scheduled job.

For recurring price history, schedule the Actor and retain each run or append to a dedicated dataset. Product ID is stable within each retailer, while Supermarket plus Product ID forms the practical matching key across the combined output.

### How reliable is the supermarket price scraper?

Retailers are processed independently. If one fails, successful rows from the others remain available and `RUN_SUMMARY` identifies the failure. The run fails only when every selected retailer fails.

Retailer markup, promotion labels, rate limits, and online-store context can change. Start without a proxy; enable one only after repeated cloud blocking.

### What are the current limitations?

- Prices come from the retailer's public Portuguese online catalog, not every physical branch.
- Continente may apply the website's own default online delivery context when no customer location is supplied.
- The Actor intentionally has no postal-code input and does not claim store-level local prices.
- Product availability can change between collection and checkout.
- Some loyalty, coupon, app-only, multibuy, or basket promotions may not appear as a simple product discount.
- Category, brand, size, unit price, or image can be absent when the retailer does not expose them consistently.
- Product matching across different supermarkets is not inferred; identifiers are retailer-specific.
- Full-catalog runs depend on the products exposed through public pagination at run time.

### How do I troubleshoot a supermarket price run?

#### A brand search returns fewer products than expected

Try the brand name as Keyword and leave Brand empty. This broadens recall when a retailer omits the structured brand value.

#### A complete catalog run takes a long time

This is expected because the Actor follows every available page. Add a Keyword or Brand for a smaller recurring workflow.

#### One supermarket has no results

Check RUN\_SUMMARY, run that retailer alone, and verify that the same search works on its public site. Enable a proxy only after direct requests repeatedly fail.

#### A product has no discountPrice

The public card did not expose a lower promotional amount. The regular/current price remains in price.

### How should I use the Actor responsibly?

Use this Actor in accordance with applicable law, retailer terms, and Apify policies. Choose reasonable schedules, avoid unnecessary full-catalog runs, and process only data you are entitled to collect.

# Actor input Schema

## `supermarkets` (type: `array`):

Choose one or more Portuguese supermarket catalogs to scrape in the same run. Options: Continente, Auchan, and Pingo Doce. Default: all three; the Console prefill uses Continente for a faster first test.

## `keyword` (type: `string`):

Search product names and catalog text. Examples: leite, café, arroz, and detergente. If omitted, Brand can still filter results; if both fields are empty, the Actor scrapes the complete selected catalogs.

## `brand` (type: `string`):

Filter the displayed brand using case- and accent-insensitive free text. Examples: Mimosa, Delta, Compal, and Fairy. If omitted, products from every brand matching Keyword are returned.

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

Configure Apify Proxy only when a retailer repeatedly blocks direct cloud requests. If omitted, the Actor connects directly, which is the recommended and lowest-cost default.

## `datasetId` (type: `string`):

Enter an existing Apify dataset ID, not a dataset URL, to append a second copy of every product row. If omitted, results are written only to the run's default dataset.

## `runId` (type: `string`):

Add a non-secret workflow, snapshot, campaign, or external job identifier to every output row. Example: weekly-milk-prices. If omitted, runId is not added to product rows.

## Actor input object example

```json
{
  "supermarkets": [
    "continente"
  ],
  "keyword": "leite"
}
```

# Actor output Schema

## `dataset` (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 = {
    "supermarkets": [
        "continente"
    ],
    "keyword": "leite"
};

// Run the Actor and wait for it to finish
const run = await client.actor("trovevault/portugal-supermarket-prices").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 = {
    "supermarkets": ["continente"],
    "keyword": "leite",
}

# Run the Actor and wait for it to finish
run = client.actor("trovevault/portugal-supermarket-prices").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 '{
  "supermarkets": [
    "continente"
  ],
  "keyword": "leite"
}' |
apify call trovevault/portugal-supermarket-prices --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,trovevault/portugal-supermarket-prices"
        }
    }
}

```

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/50LfmHmKh5ftcXbaA/builds/Pd0thowDzw12WpmUs/openapi.json
