# Chedraui Mexico Supermarket Price Scraper (`superslowsloth/chedraui-products`) Actor

Chedraui Mexico supermarket price scraper: search chedraui.com.mx by keyword or walk whole departments (Supermercado and 15 more) and get price, list price, stock, brand, EAN, category and URL in MXN. For daily grocery price monitoring.

- **URL**: https://apify.com/superslowsloth/chedraui-products.md
- **Developed by:** [Superslow Sloth](https://apify.com/superslowsloth) (community)
- **Categories:** E-commerce, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.56 / 1,000 product scrapeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## Chedraui Mexico Supermarket Price Scraper

Read prices from [Chedraui](https://www.chedraui.com.mx) (one of Mexico's large
supermarket chains, on the VTEX commerce platform) by **keyword** or by
**department**: the selling price, the list price and the price before a
promotion in **MXN**, the discount percent, whether the product is in stock and
how many, the unit it is sold by, EAN barcode, brand, category, image and the
product URL, one flat row per **SKU**. Run it on a schedule and diff the dataset
to get a **Mexican supermarket price watch**: what a litre of milk, a kilo of
tomatoes or a television costs at Chedraui today against last week, and what
went out of stock.

No account, no API key, no login, no browser. One request returns 50 products.

### Input

| Field | Notes |
|---|---|
| `searchQueries` | Search queries, one per line (`leche`, `aceite`, `televisor`). Spanish and brand names work. A SKU found by two queries is returned once, under the first. |
| `categories` | Departments or categories to read, by name or id, one per line. The 16 departments are `1` Supermercado, `2` Vinos y licores, `3` Bebes, `4` Farmacia, `5` Moda y belleza, `6` Mascotas, `7` Hogar y jardin, `8` Tecnologia, `9` Catalogo extendido, `10` Jugueteria, `11` Cuidado e higiene personal, `12` Belleza, `13` Electrodomesticos y linea blanca, `14` Papeleria y oficina, `15` Deportes, `16` Automoviles y motos. Sub-categories work: `Supermercado/Lacteos y huevo`, or its id `105`. |
| `maxItems` | Rows to read per query, per category, or per query-and-category pair (default 100, up to 100,000). |
| `inStockOnly` | Skip SKUs Chedraui reports as unavailable (not returned, not charged). Off by default. |
| `proxyConfiguration` | Apify datacenter proxy by default. |

Give queries, categories, or both. At least one is required. With both, every
query is searched **inside** every category (`leche` in `Supermercado`), which
is how you keep a loose word from matching a toy.

#### Picking a category

A category is matched by id (`1`, `105`, `10501`; ids are unique across the
whole tree), by a path of ids (`1/105`), by name, or by a path of names
(`Supermercado/Lacteos y huevo`). Names ignore case and accents. The actor reads
Chedraui's **live category tree** (776 categories, three levels deep) to do it,
so a category added tomorrow works without a new release, and a name that
matches nothing or matches two categories fails the run **before anything is
charged**, with the list of what exists. A bare name that several categories
carry resolves to the shallowest one (`Mascotas` is department 6; there is
another under Catalogo extendido), and the id is always exact.

### Output

Prices are numbers in Mexican pesos; `currency` is always `MXN`.

| Field | Notes |
|---|---|
| `query`, `position` | The query that first found the SKU (null when it was found by walking a category), and where its **product** sat in the result list of the request that returned it (1 is the first result). Sibling SKUs share a position. |
| `product_id`, `sku_id` | Chedraui's product id and the SKU id (VTEX `itemId`). `sku_id` is unique per row in a run. |
| `name`, `sku_name` | The product name and the SKU's full name. |
| `ean`, `brand` | The SKU's barcode (leading zeros kept) and the brand. Null when Chedraui sends none. |
| `url`, `image_url` | The `https://www.chedraui.com.mx/<slug>/p` product page, and the first photo. |
| `category`, `category_path` | The most specific category and its breadcrumb (`["Supermercado", "Lacteos y huevo", "Leche"]`). |
| `price`, `list_price`, `price_without_discount` | The selling price now (`Price`), the reference price before the promotion (`ListPrice`) and `PriceWithoutDiscount`. **Null when Chedraui has no price for the SKU** (see below). |
| `on_sale`, `discount_percent` | `on_sale` is true when `list_price` is above `price`. `discount_percent` is worked out from the two prices (one decimal place) and is **null when the SKU is not on sale**, never 0. |
| `in_stock`, `stock_quantity` | Chedraui's availability flag and `AvailableQuantity` for the chosen seller's offer. **`stock_quantity` is 99999 for stock the seller does not count** (190 of 315 offers in a sample, nearly all groceries); it is passed through as sent. |
| `measurement_unit`, `unit_multiplier` | VTEX's `measurementUnit` (`un` for a unit, `kg` for produce and meat sold by weight) and `unitMultiplier`, as sent and not interpreted. |
| `seller_id`, `seller_name`, `sold_by_chedraui` | The seller whose offer the row shows. Id `1` is Chedraui itself; any other is a marketplace seller (televisions and furniture mostly). |

Example row (`leche`, position 1):

```json
{
  "query": "leche",
  "position": 1,
  "product_id": "3855683",
  "sku_id": "3855683",
  "name": "Leche Bové Leche con Café Orgánica 330ml",
  "sku_name": "Leche Bové Leche con Café Orgánica 330ml",
  "ean": "7501639313538",
  "brand": "Bové",
  "url": "https://www.chedraui.com.mx/leche-bove-leche-con-cafe-organica-330ml-3855683/p",
  "image_url": "https://chedrauimx.vteximg.com.br/arquivos/ids/78650497/7501639313538_00.jpg?v=639271041376600000",
  "category": "Leche",
  "category_path": ["Supermercado", "Lacteos y huevo", "Leche"],
  "currency": "MXN",
  "price": 30.0,
  "list_price": 30.0,
  "price_without_discount": 30.0,
  "on_sale": false,
  "discount_percent": null,
  "in_stock": true,
  "stock_quantity": 99999,
  "measurement_unit": "un",
  "unit_multiplier": 1.0,
  "seller_id": "1",
  "seller_name": "Chedraui",
  "sold_by_chedraui": true
}
```

#### Why one row per SKU

VTEX nests a product over its SKUs, and the SKU is what has a barcode, a price
and a stock level. On Chedraui that is almost always the same thing as a
product: none of 301 products sampled (groceries, televisions, phones) had a
second SKU. The row is still a SKU, so a product that does have several is not
reported under one price.

#### Honest nulls

A field Chedraui did not send is `null`, never `0` or `""`.

- **Unpriced SKUs.** Chedraui sends `Price: 0` and `ListPrice: 0` for a SKU it
  cannot currently sell (16 of 315 offers in a sample). The actor reports a null
  `price`, not a price of zero pesos, and still returns the row with
  `in_stock: false`, because a stock watch needs it. Set `inStockOnly` to skip
  them. A few SKUs keep their price while flagged unavailable (a phone with
  stock 0 still lists 23,499); both are reported as sent.
- **Several sellers.** A marketplace SKU lists Chedraui's own seller beside the
  marketplace one, unpriced. The row shows the seller Chedraui marks as the
  default (when none is marked, the first seller with a price), because the
  default is the one the site sells from.
- **Prices are the website's default, not a store's.** Chedraui's catalogue API
  carries no store or postcode, so these are the prices the site shows before a
  delivery location is chosen. A store's own shelf price can differ.

### Pricing

Pay per event:

- **$0.002** per run (`actor-start`), charged only after your input is
  validated and your categories are found, so a run that fails on bad input
  costs nothing.
- **$0.00056** per row written (`product-scraped`), i.e. $0.56 per 1,000 rows. A
  row that is not written is not charged.

### How it fetches

It calls the VTEX catalog JSON behind Chedraui's own search and category pages
(`www.chedraui.com.mx/api/catalog_system/pub/products/search?ft=<query>` and
`fq=C:/<department>/<category>/`, `_from=0&_to=49`), 50 products per request,
and the category tree at `/api/catalog_system/pub/category/tree/3`. Measured
2026-10-19: HTTP 200 or 206 and JSON from a plain home connection, an Apify
datacenter exit and a residential exit. HTTP 206 Partial Content is the normal
answer for a full page; the last page is 200. A refusal (403, 429, 202, 5xx), or
a 200 that is not the product array, is treated as the address being blocked,
and the actor rotates to a fresh one and retries. Chedraui occasionally answers
a good request with a 500 (`The operation was canceled.`, or a Solr queue
overflow); that is retried the same way.

Things worth knowing, all measured:

- **Depth ceiling: 2,550 products per search.** VTEX refuses `_from` above 2,500
  (HTTP 400) on this store as on any other, so one search can be read to 51
  pages of 50 however many products it matches. The way past it is the category
  tree: when a category holds more than that and `maxItems` asks for more, the
  actor reads its **sub-categories** instead (Supermercado's 13 hold 20,560
  products against its own 20,561, and Despensa's 10,401 split again). The
  split is logged. A search that is still over the ceiling with nothing left to
  split, such as a loose query like `leche` across the whole store, logs a
  warning so you can narrow it (add a brand or size, or pick a sub-category).
  At the default `maxItems` of 100 nothing is split: one page is enough.
- **The match count moves.** `leche` read 1,150 matches on one request and 1,151
  on the next; Supermercado read 20,559 to 20,562 within a minute. The count in
  the log is informational.
- **Page size.** VTEX serves at most 50 per page (`_to` above 50 is HTTP 400), so
  the actor never asks for more.
- **No matches.** A query with no results is a normal empty answer (`[]`), not an
  error. It is logged and the run continues with the next one. A page past the
  end of a smaller result set is also `[]`.
- **Heavy pages.** One page of 50 products is 0.8 MB of JSON, so a whole
  department of 20,000 products moves about 330 MB.
- **Not offered.** Sort orders, price filters and store selection. An empty
  query with no category would list the whole 66,000-product catalogue and is
  refused.

# Actor input Schema

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

What you would type into the search box on chedraui.com.mx, one query per line (for example leche, aceite or televisor). Spanish and brand names work. Each query is searched and read separately. A product that two queries both find is returned once, under the first. If you also give categories below, each query is searched inside each category. Leave empty to read whole categories.

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

Departments or categories to read, one per line, by name or id. The 16 departments are 1 Supermercado, 2 Vinos y licores, 3 Bebes, 4 Farmacia, 5 Moda y belleza, 6 Mascotas, 7 Hogar y jardin, 8 Tecnologia, 9 Catalogo extendido, 10 Jugueteria, 11 Cuidado e higiene personal, 12 Belleza, 13 Electrodomesticos y linea blanca, 14 Papeleria y oficina, 15 Deportes, 16 Automoviles y motos. Sub-categories work too: write Supermercado/Lacteos y huevo, or the id 105 (the actor reads the live category tree, so any id on chedraui.com.mx is valid). Case and accents do not matter. VTEX serves at most 2,550 products per category; a bigger one (Supermercado has about 20,500) is read through its sub-categories automatically when Max products asks for more.

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

Read at most this many products (one row per SKU, which on Chedraui is almost always one per product) for each query, each category, or each query-and-category pair. Chedraui serves 50 per page. A category with more than 2,550 products is split into its sub-categories to go past that, so set this above 2,550 to read a whole department.

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

Skip SKUs Chedraui reports as unavailable. They are read but not returned and not charged. Off by default, which returns them with in_stock false and, for most, a null price: a stock watch needs those rows, a price watch usually does not.

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

Use Apify's automatic datacenter proxy (the default). A refused request is retried from a fresh address.

## Actor input object example

```json
{
  "searchQueries": [
    "leche"
  ],
  "maxItems": 100,
  "inStockOnly": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `items` (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 = {
    "searchQueries": [
        "leche"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("superslowsloth/chedraui-products").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 = {
    "searchQueries": ["leche"],
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("superslowsloth/chedraui-products").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 '{
  "searchQueries": [
    "leche"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call superslowsloth/chedraui-products --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,superslowsloth/chedraui-products"
        }
    }
}
```

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/LTkuB2excWFPL1Ahe/builds/jdsCF8SJsjgpeqwSL/openapi.json
