# Liverpool Price & Promo Scraper (Mexico) (`superslowsloth/liverpool-products`) Actor

Liverpool scraper: search terms or category pages to one row per product with MXN price, regular price, promo price, discount percent, rating, seller, marketplace and sponsored flags, categories, colours, labels and image. For Mexican department-store price watching.

- **URL**: https://apify.com/superslowsloth/liverpool-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 $2.10 / 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

## Liverpool Scraper: Price & Promo Tracker for Mexico

This Liverpool scraper reads [Liverpool](https://www.liverpool.com.mx) (the
Mexican department store) by keyword, or reads a category page, and gives one
flat row per product: the price in **MXN**, the regular price, the promo price,
the discount in pesos and percent, the star rating, who sells it, whether it is
a marketplace or a sponsored listing, the categories, the colours, the labels,
the product link and the photo. Run it on a schedule and diff the dataset to get
a **Liverpool price tracker**: which televisions, perfumes or dresses were
reduced since last week, and by how much.

Palabras clave: scraper de Liverpool, precios Liverpool México, rastreador de
precios y ofertas de Liverpool, monitor de promociones de tienda departamental.

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

### Input

| Field | Notes |
|---|---|
| `searchTerms` | One or more search terms, one per line (`vestido`, `pantalla`, `perfume`). Liverpool corrects a misspelt term (`televison` is searched as `television`) and the corrected results are read; the log says so. A product found by two terms is returned once, under the first. |
| `startUrls` | Liverpool pages to read in full: a category (`https://www.liverpool.com.mx/tienda/pantallas/catst14457077`) or a search URL (`/tienda?s=perfume`, read as that search term). A category address ends in an id such as `catst14457077` or `cat5150041`; the name before it is ignored by the site. A top-level category such as `/tienda/mujer/catst4003072` is a landing page with no product list and is refused; use a sub-category. Filters chosen on the site (brand, price) are not part of the address and are not applied. Product pages are not accepted. Give terms, URLs or both. |
| `maxItems` | Products to read per term or page (default 100, up to 10,000). 56 per page. Liverpool stops a list at about 10,000 products whatever the match count. |
| `onlyOnSale` | Return only products that are reduced (`on_sale`). Products that are not reduced are read, not returned and not charged, and do not count toward `maxItems`. |
| `sort` | `relevance` (default), `price-asc`, `price-desc`, `newest`, `rating`: the choices of the site's own sort menu. See "Which sort to use" below. |
| `proxyConfiguration` | Apify datacenter proxy by default. |

Example input:

```json
{
  "searchTerms": ["vestido", "perfume"],
  "maxItems": 200,
  "onlyOnSale": true,
  "sort": "price-asc"
}
```

### Output

Prices are numbers in Mexican pesos; `currency` is `MXN` (every page measured).

| Field | Notes |
|---|---|
| `query`, `position` | The search term or category URL that first found the product, and its slot in Liverpool's list for that source and sort (1 is first, 56 slots per page). Sponsored products take slots, so the organic products around them are not numbered 1, 2, 3. |
| `product_id`, `url` | Liverpool's product id (`1183307572`; marketplace products have longer ids) and the product link. The link's name part is built from the product name; the site answers any name there, the id is what selects the product. |
| `name`, `brand` | As the page shows them (typos included). `brand` is null if the page sends none. |
| `currency` | Always `MXN`. |
| `price` | The lowest of `sale_price` and `promo_price`: the price the site's own price sort orders by. Null only if the page sent neither. |
| `sale_price`, `list_price`, `promo_price` | The three prices as the page sends them. `sale_price` is always sent. `list_price` is the regular price and is **null on about one product in seven**. `promo_price` is the price after a promotion and is **null when the product has none**; it is below `sale_price` for most products that have one. |
| `on_sale` | True when `price` is below the regular price (`list_price`, or `sale_price` when the page sends no list price). |
| `discount_amount`, `discount_percent` | Pesos and percent off the regular price, one decimal place for the percent, worked out from the prices (the page does not send them). **Null when the product is not on sale**, never 0. |
| `rating`, `rating_count` | The average stars on a 5-point scale, as sent, and how many ratings it is over. `rating` is **null when there are none** (the page sends 0, which is not a rating); `rating_count` is then 0. |
| `sellers` | Who sells it: `liverpool` for the store's own stock, a shop name for a marketplace seller. Several names appear when more than one sells the same product (one product listed 16). |
| `is_marketplace` | True when the page flags the product as a marketplace product. |
| `is_sponsored` | True when the row came from a sponsored slot, an advertisement a brand paid for. See "Sponsored products" below. |
| `product_type` | Liverpool's own product class, as sent: `Soft Line` or `Big Ticket` on every product measured. The grouping is Liverpool's and does not follow what you would guess (the television in the example row is `Soft Line`). |
| `category`, `categories` | The main category as sent (`Mujer`, `Electrónica`, or a campaign such as `Otras Categorias` or `Buen Fin`) and every category path the product is filed under (`Mujer > Ropa > Vestidos > Largo`). A product sits under several, including campaign and brand pages. |
| `colours`, `variant_count` | The colour names of the product's variants (`Negro`, `Sin color` where Liverpool names none) and how many variants the page lists. A product is one row; its colours are not separate rows. |
| `labels` | The labels on the card (`Exclusivo Liverpool`, `Regalo con compra`). Empty list when there are none. |
| `image_url` | The first photo of the first colour, on a `liverpool.com.mx` image host. |

Example row (real values, 2026-10-17):

```json
{
  "query": "television",
  "position": 3,
  "product_id": "1146759285",
  "name": "Pantalla Smart TV LED de 32 puadas Full HD 32LR650BPSA",
  "brand": "LG",
  "url": "https://www.liverpool.com.mx/tienda/pdp/pantalla-smart-tv-led-de-32-puadas-full-hd-32lr650bpsa/1146759285",
  "image_url": "https://sp629.liverpool.com.mx/i/1146759285.jpg",
  "currency": "MXN",
  "price": 3298.95,
  "sale_price": 7331.0,
  "list_price": 7999.0,
  "promo_price": 3298.95,
  "on_sale": true,
  "discount_amount": 4700.05,
  "discount_percent": 58.8,
  "rating": 4.7955,
  "rating_count": 176,
  "sellers": ["liverpool"],
  "is_marketplace": false,
  "is_sponsored": false,
  "product_type": "Soft Line",
  "category": "Otras Categorias",
  "categories": [
    "Otras Categorias > Top deals",
    "LG > Sistemas de Entretenimiento > Pantallas y televisores"
  ],
  "colours": ["Negro"],
  "variant_count": 1,
  "labels": []
}
```

#### Which price to read

Liverpool sends three prices and they are not always the same. In a sample of
896 products, `list_price` equalled `sale_price` for most, `sale_price` was
below `list_price` for 178, and `promo_price` was below `sale_price` for 502.
`price` is the lowest figure the page shows for the product and is the one to
track; keep `sale_price`, `list_price` and `promo_price` when you need to see
which kind of reduction it is. A product with no `list_price` and no
`promo_price` has `price` equal to `sale_price` and is not on sale.

#### Sponsored products

The first two pages of a search or category carry sponsored slots, every eighth
slot (6 or 7 of the 56). They are advertisements, so they can sit out of order
under a sort: the first row of a price-ascending list can be an expensive
sponsored product. A sponsored product that the page also lists organically is
returned once, as the organic row (`is_sponsored` false); a sponsored product
with no organic copy on its page is returned with `is_sponsored` true. Pages
from the third carry none. To drop them, filter on `is_sponsored`.

#### Honest nulls

A field the page did not send is `null`, never `0` or `""`. The list fields
(`sellers`, `categories`, `colours`, `labels`) are empty lists when there are
none.

- **No stock levels.** Liverpool's lists only show products it has in stock, but
  the rows carry no quantity.
- **`colours` is the site's name** and can be missing for a variant, so the list
  can be shorter than `variant_count`.
- **No delivery or store-pickup data.** It depends on the visitor's postcode.

### Which sort to use

`sort` takes the choices of the site's own menu. Two things are measured:

- **The price sorts follow the promo price** where a product has one:
  155 products over three pages of `television` had no pair out of order by
  `promo_price`, while the same list had 14 pairs out of order by `sale_price`.
  Sponsored products (above) are the exception.
- **Relevance repeats products on late pages; every other sort reads a list
  through.** A full walk of `television` (36 pages, 2,009 matches) in relevance
  order returned 1,996 organic rows of which only 1,681 were distinct: pages 28
  to 36 repeat products from pages 22 to 26. The same list under `price-asc`
  returned 1,963 rows of which 1,962 were distinct, and `newest`, `rating` and
  `price-desc` 1,958, 1,963 and 1,961. Repeats are returned once and charged
  once, but with relevance the end of a long list is simply not reached. To read
  a whole search, choose one of the other four sorts.
- **An explicit sort is a slightly smaller list than relevance** (`television`:
  1,976 products against 2,009), so the two are different lists, not one list in
  two orders.

The page says which sort it applied, and a run that did not get the sort it
asked for fails that source instead of returning the wrong order.

### Pricing

Pay per event:

- **$0.002** per run (`actor-start`), charged only after your input is
  validated, so a run that fails on bad input costs nothing.
- **$0.0021** per product row written (`product-scraped`), i.e. $2.10 per
  1,000 products. A row that is not written is not charged: rows filtered by
  `onlyOnSale`, and repeats of a product another term or page already returned,
  are free.

### How it fetches

It reads the product list that liverpool.com.mx's own search and category pages
embed in their page data. Measured 2026-10-17: HTTP 200 HTML (0.5 to 0.95 MB a
page) from a plain home connection and, in the build's probe, from an Apify
datacenter address on 20 of 20 requests, no key or cookie needed. A refusal
(403, 429, 5xx), a 200 that is not a Liverpool page, or a dropped connection is
treated as the address being blocked, and the actor rotates to a fresh one and
retries.

Things worth knowing, all measured:

- **Typos are corrected.** `televison` returns the results for `television`
  (2,008 products); the log names the correction and `query` stays the term you
  typed.
- **No match is not an error.** A term with no results (`zzqxjkwv`) is an empty
  list at no charge, and is logged.
- **Depth.** 56 slots per page, no page-size setting. A page past the last is an
  empty list and the actor stops there. Liverpool stops a list at about 10,000
  slots: for a search with 11,188 matches, pages from 180 repeat the last window,
  so at most 10,000 products are read per term or page.
- **Totals drift.** The same search reported 2,009 and 2,010 matches on two
  requests, and the last page holds the remainder (49 of 56 for `television`).
- **Category addresses.** `/tienda/<name>/<id>` where the id is `cat` plus
  digits (`cat5150041`) or `catst` plus digits (`catst14457077`). The name is
  ignored by the site (`/tienda/x/catst14457077` is the same list). An unknown id
  is a 404 and is reported; a top-level category (`/tienda/mujer/catst4003072`)
  is a landing page with no product list and is refused.
- **Filters are not supported.** Liverpool applies brand and price filters in the
  page after it loads; no address parameter for them was found, and a search term
  added to a category address (`?s=sony`) is ignored.
- **Totals under a search are for in-stock products** (the list is filtered to
  products the store can sell).

If Liverpool changes its page data, the run fails with a message instead of
returning empty rows.

# Actor input Schema

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

What you would type into the Liverpool search box, one term per line (for example vestido, pantalla or perfume). Each term is read separately. Liverpool corrects a misspelt term (televison is searched as television) and the corrected results are read. A product that two terms both find is returned once, under the first.

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

Liverpool pages to read in full, for example https://www.liverpool.com.mx/tienda/pantallas/catst14457077 or https://www.liverpool.com.mx/tienda?s=perfume. Use a category that lists products (its address ends in an id such as catst14457077); a top-level page such as /tienda/mujer/catst4003072 is a landing page with no product list and is refused. Filters set on the site (brand, price) are not carried by the address and are not applied. Product pages (/tienda/pdp/...) are not accepted. Leave empty to use search terms only.

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

Read at most this many products for each search term or page. Liverpool lists 56 per page, so 100 reads two pages. Liverpool caps every list at about 10,000 products, so up to 10,000 are accepted.

## `onlyOnSale` (type: `boolean`):

Return only products whose price is below their regular price (the on_sale field). Products that are not reduced are read but not returned and not charged, and do not count toward Max products.

## `sort` (type: `string`):

The order Liverpool lists the products in, the same choices as its own sort menu. 'Relevance' is its default, but it repeats products on late pages of a long list; the other four orders read a list through without repeats, so choose one of them to read a whole search. The price sorts follow the promo price where a product has one.

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

Use Apify's automatic datacenter proxy (the default). liverpool.com.mx answered an Apify datacenter address with HTTP 200 on 20 of 20 requests when this actor was built; the residential group was not needed. A refused request is retried from a fresh address.

## Actor input object example

```json
{
  "searchTerms": [
    "vestido"
  ],
  "startUrls": [],
  "maxItems": 100,
  "onlyOnSale": false,
  "sort": "relevance",
  "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 = {
    "searchTerms": [
        "vestido"
    ],
    "startUrls": [],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

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

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

```

## MCP server setup

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