# Farmacias del Ahorro Product Prices Scraper (`automation-lab/farmacias-ahorro-product-prices`) Actor

Export public Farmacias del Ahorro products, SKUs, categories, images and current/old MXN catalog prices from keyword searches and canonical product URLs.

- **URL**: https://apify.com/automation-lab/farmacias-ahorro-product-prices.md
- **Developed by:** [Automation Lab](https://apify.com/automation-lab) (community)
- **Categories:** E-commerce
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.25 / 1,000 item processeds

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

## Farmacias del Ahorro Product Prices Scraper

Export **farmacias del ahorro products** for recurring Mexican retail price and assortment snapshots. Start with keyword searches, canonical product URLs, or both. Each dataset row contains a SKU, original Spanish title, assigned categories, image URL, source URL, current MXN catalog price, and an old price when the regular price is higher.

This Actor targets **fahorro.com**, not ahorro.com. It collects public online catalog observations, not branch inventory, prescription guidance, medical advice, or personalized offers.

### Who is this for?

- Retail analysts comparing public Mexican pharmacy catalog prices.
- Category teams checking product assortment by SKU.
- Data engineers building scheduled catalog snapshots in a spreadsheet or warehouse.

### Why use this Actor?

Get typed numeric prices rather than parsing formatted currency strings. Combine a known-SKU watchlist with keyword discovery in one run, deduplicated globally by SKU. Explicit product URLs are handled first so known products receive the shared result budget before broader searches.

The Actor uses the store's public catalog API without downloading images or launching a browser. Image links are included, not image files. It does not require a store account or proxy configuration.

### Getting started

1. Open the Input tab.
2. Enter a keyword such as `paracetamol`, or add a canonical product URL.
3. Choose `maxItems`, the global unique-product cap.
4. Run the Actor and inspect Products in Output.
5. Download the dataset as JSON, CSV, Excel, or another Apify-supported format.

A minimal search input:

```json
{"queries":["paracetamol"],"maxItems":10}
```

For a known product:

```json
{
  "startUrls":[{"url":"https://www.fahorro.com/tempra-500-mg-adultos-paracetamol-caja-10-tabletas.html"}],
  "maxItems":1
}
```

### Input parameters

| Field | Meaning |
| --- | --- |
| `queries` | Up to 50 nonempty search strings, each at most 200 characters. Whitespace is trimmed and duplicate strings removed. |
| `startUrls` | Up to 100 canonical HTTPS product URLs on fahorro.com or www.fahorro.com. Root-level product-name.html paths only; no query strings or fragments. |
| `maxItems` | Global unique-SKU cap, 1–10000; default 10. Zero and unlimited are unsupported. |

Provide at least one query or product URL. Empty input and unsupported fields fail with a clear error. URL inputs use exact URL-key lookup; category/search URLs are not accepted.

Search relevance is determined by the store, not a local exact-title, whole-word, or substring filter. Multiple searches are combined (ANY) in order. Their results may include related products. There is no guarantee of a minimum number per query. Explicit URLs consume the budget first; query terms do not filter those explicitly requested products.

### Extracted data

| Field | Type | Meaning |
| --- | --- | --- |
| `sku` | string | Store product identifier; global deduplication key. |
| `title` | string | Original catalog title. |
| `categories` | string\[] | Assigned category names, not ordered breadcrumbs. |
| `imageUrl` | string or null | Catalog thumbnail link. |
| `url` | string | Canonical store product URL. |
| `price` | number | Public primary final catalog price. |
| `oldPrice` | number or null | Regular price only when greater than current price. |
| `currency` | string | Always MXN. |
| `scrapedAt` | string | Observation timestamp in ISO format. |

Example observed product; prices and categories may change:

```json
{
  "sku":"7501095452116",
  "title":"Tempra 500 mg Adultos Paracetamol caja 10 tabletas",
  "categories":["Landings","Farmacia","Dolor y Fiebre","Te Queremos Protegido","Vive sin Dolor","Dolor y fiebre adulto"],
  "imageUrl":"https://production-media.fahorro.com/media/catalog/product/7/5/7501095452116_4.jpg?optimize=medium&bg-color=255,255,255&fit=bounds&height=&width=",
  "url":"https://www.fahorro.com/tempra-500-mg-adultos-paracetamol-caja-10-tabletas.html",
  "price":76,
  "oldPrice":null,
  "currency":"MXN",
  "scrapedAt":"2026-10-27T21:05:21.151Z"
}
```

### How much does it cost to extract Farmacias del Ahorro product prices?

Pay per event: a **$0.003 start fee per run**, plus a product event for each delivered unique product. The BRONZE product price is **$0.00208 per product**. Product event prices by Apify spend tier are FREE $0.002392, BRONZE $0.00208, SILVER $0.0016224, and GOLD/PLATINUM/DIAMOND $0.001248. Examples at BRONZE: 1 product costs $0.00508; 10 cost $0.0238; 100 cost $0.211. These examples exclude any separate platform charges that apply to your account.

Duplicates, nonexistent products, and valid empty searches have no product event charge. A valid empty search still incurs the start fee. Malformed input is rejected before the start charge. A failed run may retain already exported rows and their charges. Use the global product limit and Apify's maximum-charge setting to control collection.

### Limits and price interpretation

Prices describe the online public catalog at observation time. They are not checkout quotes, local stock, delivery promises, medical recommendations, or historical prices. A null `oldPrice` means the regular price was not greater than the current price; it does not establish that no promotion exists.

Search coverage follows the store's catalog and ranking. Products can move between pages during a run. Duplicate SKUs are removed, but this cannot guarantee a frozen complete catalog. The Actor stops at source exhaustion, the result cap, the charge cap, or the platform runtime limit.

### Failure behavior and troubleshooting

Transient network errors, HTTP 429 and server errors receive at most two retries with bounded backoff. Permanent HTTP/GraphQL errors, invalid prices and non-JSON responses fail rather than pretending that extraction succeeded. No paid proxy fallback is silently enabled.

If a query returns no results, try a common store keyword. Missing product URL keys return no row. If a formerly working URL stops matching, copy the canonical product URL from the store again. If the source API changes, the run fails with diagnostics; any existing rows are partial results, not proof of complete collection.

The `SUMMARY` key-value record reports saved products, request attempts, retries and response bytes for successful runs.

### Integrations

Schedule repeated runs in Apify, export each dataset to a spreadsheet or warehouse, and join observations on `sku`. Compare `price`, `oldPrice` and category membership downstream. The Actor itself does not calculate changes, keep historical state, send price alerts, or monitor continuously.

Use Apify webhooks, Make, Zapier or a custom API consumer to process successful runs. Keep full exports in storage; fetch only bounded slices for interactive analysis.

### API examples

Start a run with cURL, using your own Apify token:

```bash
curl -X POST 'https://api.apify.com/v2/acts/automation-lab~farmacias-ahorro-product-prices/runs' \
  -H "Authorization: Bearer $APIFY_TOKEN" -H 'Content-Type: application/json' \
  -d '{"queries":["paracetamol"],"maxItems":10}'
```

JavaScript:

```javascript
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({token: process.env.APIFY_TOKEN});
const run = await client.actor('automation-lab/farmacias-ahorro-product-prices').call({queries:['paracetamol'], maxItems:10});
const {items} = await client.dataset(run.defaultDatasetId).listItems({limit:10});
console.log(items);
```

Python:

```python
import os
from apify_client import ApifyClient
client = ApifyClient(os.environ['APIFY_TOKEN'])
run = client.actor('automation-lab/farmacias-ahorro-product-prices').call(run_input={'queries':['paracetamol'],'maxItems':10})
print(client.dataset(run['defaultDatasetId']).list_items(limit=10).items)
```

The start endpoint returns run metadata, not product rows. Follow that run to completion and then read its default dataset.

### MCP integration

Select this Actor explicitly:

```bash
claude mcp add --transport http apify \
  'https://mcp.apify.com?tools=automation-lab/farmacias-ahorro-product-prices'
```

Equivalent Claude Desktop, Cursor, and VS Code configuration (use your client's HTTP MCP configuration syntax):

```json
{"mcpServers":{"apify":{"url":"https://mcp.apify.com?tools=automation-lab/farmacias-ahorro-product-prices"}}}
```

Configure authentication through your client's supported Apify authorization flow. Example prompt: “Collect up to 10 paracetamol product prices from Farmacias del Ahorro and show SKU, title and MXN price.”

Discover actual tool names and argument schemas using scoped `tools/list`. Actor selection also adds run/data helper tools; it does not expose exactly one tool. Read-only discovery is not authorization to start or abort runs.

For an authorized invocation, start once and retain its run ID and storage IDs. `call-actor` waits at most 45 seconds (`waitSecs`, default 30). If pending, follow the same run with `get-actor-run`, using 2/4/8-second backoff capped at 10 seconds and a 120-second total deadline. Set client timeout above server wait plus transport margin. Never start a replacement just because polling timed out; report a pending/uncertain run instead.

After success, fetch `get-dataset-items` using the returned default dataset ID, explicit fields, limit and offset. Example consumer budget: 20 rows per page, at most 100 source rows and 64 KiB serialized UTF-8 content admitted to model context, whichever comes first. Enforce byte limits host-side, including oversized pages/rows, and disclose omissions. If your client cannot intercept responses, do not claim a hard byte guarantee. Advance source offsets, report truncation and next offset, and keep full exports outside model context. Failed runs may contain partial data. No numeric token-saving claim is made.

### Legality and privacy

Use public catalog data responsibly and follow applicable site terms and law. This Actor is independent of Farmacias del Ahorro and is not endorsed by the store. Do not use catalog prices as treatment advice or infer medical availability.

No AI model processes input or scraped products at runtime. Apify hosts datasets and logs under your account's storage retention settings; delete runs/datasets through Apify to remove stored exports. There is no cross-run cache. Use the Store issue tab to contact support about extraction problems.

Failed operations send sanitized diagnostic input, exceptions and Actor/build/run IDs to our private GlitchTip service for repair. Secret fields and URL query strings are removed; reports are retained for 30 days.

### FAQ

**Does it report branch stock or prescription requirements?** No. Those are outside the output contract.

**Does oldPrice show yesterday's price?** No. It is the current regular catalog price when higher than the current final price. Save separate runs for history.

**Can I combine URLs and keywords?** Yes. URLs are processed first and all results share one global SKU cap.

**Can I scrape ahorro.com?** No. Only fahorro.com product URLs are accepted.

### Support

Report extraction errors through this Actor's Apify Store issue tab. Include a run link, the public query or product URL and what you expected. Do not include API tokens or private medical information.

### Related workflows

For this store's SKU price snapshots this Actor is standalone. Use Apify's dataset exports and scheduling for recurring comparisons; no unrelated source is included in this Actor.

# Changelog

This Actor's version history is a separate document: https://apify.com/automation-lab/farmacias-ahorro-product-prices/changelog.md

# Actor input Schema

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

Up to 50 nonempty search strings (200 characters each). Whitespace is trimmed; duplicate strings removed. Searches use the store's relevance matching, not exact title or whole-word matching. Multiple searches are combined (ANY), in input order, after explicit URLs. No extra local keyword filter is applied.

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

Up to 100 canonical HTTPS fahorro.com or www.fahorro.com root-level product-name.html URLs, without queries or fragments. Exact URL-key lookup, not crawling categories. Missing products produce no row. Explicit URLs are processed before searches; duplicate SKUs are exported once globally.

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

Global unique-SKU output cap across both input routes and all searches. Default 10; zero/unlimited is not supported. Paging stops at this cap or source exhaustion; an Apify charge cap may stop output sooner. Earlier URLs and queries consume the budget first; no per-query minimum is guaranteed.

## Actor input object example

```json
{
  "queries": [
    "paracetamol"
  ],
  "maxItems": 10
}
```

# Actor output Schema

## `dataset` (type: `string`):

Unique catalog products and MXN price observations.

## `summary` (type: `string`):

Saved product and HTTP request counters for this run.

# 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 = {
    "queries": [
        "paracetamol"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("automation-lab/farmacias-ahorro-product-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 = { "queries": ["paracetamol"] }

# Run the Actor and wait for it to finish
run = client.actor("automation-lab/farmacias-ahorro-product-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 '{
  "queries": [
    "paracetamol"
  ]
}' |
apify call automation-lab/farmacias-ahorro-product-prices --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,automation-lab/farmacias-ahorro-product-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/47qcWfPjtS6OzqDkg/builds/F1kT5ls4JxwKTvnCb/openapi.json
