# MercadoLibre Fast Scraper (`dtrungtin/mercadolibre-fast-scraper`) Actor

search results — and, optionally, every product page they list — on all 18 Latin American country sites: Argentina, Brazil, Mexico, Chile, Colombia, Uruguay, Peru and the rest. Search by keyword or paste any search, category or product URL (filters and sorting included)

- **URL**: https://apify.com/dtrungtin/mercadolibre-fast-scraper.md
- **Developed by:** [Tin](https://apify.com/dtrungtin) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 results

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

## MercadoLibre Scraper — Node.js / Apify

Apify Actor that scrapes MercadoLibre / Mercado Livre search results — and,
optionally, every product page they list — on all 18 Latin American country
sites: Argentina, Brazil, Mexico, Chile, Colombia, Uruguay, Peru and the rest.
Search by keyword or paste any search, category or product URL (filters and
sorting included).

Search results alone give title, price, discount, installments, shipping,
seller, rating and units sold for 48 products per page, fast and cheap. Turn on
**product details** to also get stock, condition, brand, attributes,
description, all images, categories, review count and seller reputation.

### Setup

Requires Node.js 20.18.1+ (the Actor image uses Node 24 with Google Chrome and
Playwright 1.63.0).

```sh
npm install
npm test        # parser + crawler tests (offline, against saved pages)
```

Run locally with the Apify CLI (or write the same JSON to
`storage/key_value_stores/default/INPUT.json` and run `npm start`):

```sh
apify run --input='{
  "searchQueries": ["zapatillas running"],
  "country": "AR",
  "maxItems": 20
}'
```

MercadoLibre blocks IPs outside Latin America, so a local run needs a Latin
American residential proxy. Apify Proxy only works outside the platform if the
account has *Proxy external access*; without it the log says "No proxy
configured" and requests go out directly (and get MeLi's login wall). Locally
Chrome opens a visible window — it has to run headful (see below).

### Input

| field                   | type    | description |
|-------------------------|---------|-------------|
| `searchQueries`         | array   | Keywords, searched on the site of `country`. |
| `country`               | string  | `AR` (default), `BR`, `MX`, `CL`, `CO`, `UY`, `PE`, `EC`, `VE`, `BO`, `CR`, `DO`, `GT`, `HN`, `NI`, `PA`, `PY`, `SV`. |
| `startUrls`             | array   | Search / category result pages (`listado.mercadolibre.com.ar/...`, `lista.mercadolivre.com.br/...`) and product pages (`/p/MLA...`, `articulo.../MLA-...`, `/up/MLBU...`) on any country site. |
| `maxItems`              | integer | Limit of products across all inputs, at most 10,000 per run. Empty = 10,000. |
| `includeProductDetails` | boolean | Open every product page too. Default `false`. |
| `includeAds`            | boolean | Also output sponsored results (`isAd: true`). Default `false`. |
| `proxyConfig`           | object  | Default: Apify `RESIDENTIAL`. With no country set, each site gets proxies from its own country (mercadolibre.com.ar → AR, …); a country set here is used for every site. |

At least one search query or start URL is required. Search pages are followed
through MeLi's own "next" link until `maxItems` is reached or the results run
out. Products repeated across pages (MeLi reshuffles) are output once.

### Speed and cost

Measured on Apify with 4 GB memory (6 workers), residential proxies, 2026-10-03:

| mode | 1,000 products | proxy traffic | platform cost\* |
|---|---|---|---|
| search results only | ~3 min (6 items/s) | ~7 MB | ~$0.09 |
| with product details | ~25 min (0.6–0.7 items/s) | ~225 MB | ~$2.40 |

\*Residential proxy at $8/GB plus compute; proxy traffic is most of it. A
product page is ~200 KB over the wire, a search page (48 products) about the
same, plus ~250 KB once per browser session for MeLi's bot challenge.

Memory is limited to 2–8 GB (default 4 GB) in [.actor/actor.json](.actor/actor.json).
The worker count follows memory, ~1.5 per GB (2 GB → 3, 4 GB → 6, 8 GB → 12):
each worker drives a browser tab, and at 4 GB with 5 workers the run peaked at
1.8 GB and used 65 % of its CPU core — pages mostly wait on the proxy.

### Output

One dataset record per product, always the same 43 fields in the same order.
Example with product details (Argentina):

```json
{
  "url": "https://www.mercadolibre.com.ar/samsung-galaxy-a17-a175f-negro-67-4gwi-finfc-android-1-negro/p/MLA65114795",
  "itemId": "MLA4022163150",
  "productId": "MLA65114795",
  "title": "Samsung Galaxy A17 A175f negro 6.7 4G+Wi-Fi+NFC - Android 1 negro",
  "price": 353990,
  "originalPrice": 389000,
  "discountPercent": 9,
  "currency": "ARS",
  "installmentsCount": 6,
  "installmentAmount": 79641.85,
  "installmentsInterestFree": false,
  "shipping": "Llega gratis el martes",
  "freeShipping": true,
  "fullFulfillment": true,
  "internationalFrom": null,
  "seller": "ORBEXTECHSTORE",
  "sellerId": 3333630569,
  "officialStore": false,
  "sellerReputation": "5_green",
  "sellerPowerSellerStatus": "silver",
  "rating": 4.9,
  "reviewsCount": 14573,
  "soldQuantity": 1000,
  "availableQuantity": 6,
  "inStock": true,
  "condition": "new",
  "brand": "Samsung",
  "image": "https://http2.mlstatic.com/D_NQ_NP_751119-MLA105637632754_012026-O.webp",
  "images": [
    "https://http2.mlstatic.com/D_NQ_NP_751119-MLA105637632754_012026-O.webp",
    "https://http2.mlstatic.com/D_NQ_NP_819311-MLA105637692854_012026-O.webp"
  ],
  "categories": ["Celulares y Teléfonos", "Celulares y Smartphones", "Samsung"],
  "categoryId": "MLA1055",
  "domainId": "MLA-CELLPHONES",
  "attributes": [
    { "name": "Marca", "value": "Samsung" },
    { "name": "Modelo", "value": "A17" }
  ],
  "description": "Mayor rendimiento\nSu memoria RAM de 4 GB permite que tu smartphone funcione…",
  "listingType": "gold_special",
  "isAd": false,
  "position": 5,
  "country": "AR",
  "siteId": "MLA",
  "searchQuery": "celular samsung",
  "sourceUrl": "https://listado.mercadolibre.com.ar/celular-samsung",
  "detailsScraped": true,
  "scrapedAt": "2026-10-03T09:22:32.617Z"
}
```

Field notes:

- Without product details, these fields are `null` / empty: `sellerId`,
  `sellerReputation`, `sellerPowerSellerStatus`, `reviewsCount`,
  `availableQuantity`, `inStock`, `brand`, `categories`, `attributes`,
  `description`, `listingType`; `images` holds just the main image and
  `condition` is only set for used / refurbished items (search results don't
  label new ones).
- `itemId` is the seller's listing; `productId` the MercadoLibre catalog
  product it belongs to (many listings can share one). `url` is the page the
  search linked to — a catalog page (`/p/`), a listing (`articulo.`) or a user
  product (`/up/`).
- Prices are numbers in the site's currency (`currency`). `originalPrice` /
  `discountPercent` are only set when MeLi shows a discount.
- `soldQuantity` and `availableQuantity` are what MeLi shows, which is rounded
  (`+500 vendidos` → 500; large stocks are capped).
- `shipping` is the delivery promise as shown, in the site's language. It,
  `freeShipping` and prices can depend on where the proxy IP is.
- `position` is the rank among organic results (1 = first, continuing across
  pages); sponsored results have `position: null`. Sponsored results are taken
  from their product ids — the Actor never follows MeLi's ad-click links, so
  advertisers aren't billed for its visits.
- If a product page keeps failing, the product is still output with its
  search-result fields and `detailsScraped: false`. A product that no longer
  exists is skipped and its slot goes to the next result.

The run's outcome counters and proxy traffic are stored in the key-value store
as `summary`. Pages that load but can't be parsed (layout change, soft block)
are saved there as `debug-list-<n>` / `debug-item-<n>` (first 10 per run).

**Migrations.** The crawl state (queue, seen pages, `maxItems` budget,
counters) is saved to the key-value store as `CRAWLER_STATE` every minute and
when Apify moves the run to another server. On a move the crawler first stops
taking pages and waits for dataset pushes under way, so the resumed run
continues exactly where it stopped: no product is scraped — or charged — twice.

### Monetization

Works with pay-per-result as is. For pay-per-event, define the synthetic
`apify-default-dataset-item` event (every product) and optionally a
`product-details` event: the Actor charges it once per product whose page was
opened, so the much costlier details mode can be priced separately. A run's
spending limit is respected (`ACTOR_MAX_PAID_DATASET_ITEMS` caps `maxItems`;
under pay-per-event the crawl stops when the limit is reached).

### How it gets past MercadoLibre's bot protection

MeLi's search and product pages sit behind a bot challenge: a fresh visitor
first gets an interstitial whose script solves a proof-of-work and runs MeLi's
browser fingerprinting before reloading the page. Clients that don't run it
end on `/captcha/wall`; IPs outside Latin America get a login wall
(`/gz/account-verification`). MeLi's public API needs OAuth for everything.

So the Actor drives a real Google Chrome (Playwright), headful on the image's
X server — headless Chrome is refused outright — with residential proxies from
each site's own country. Each worker keeps one browser context (one sticky
proxy IP, one cookie jar) per country and reuses it for up to 80 pages, so the
challenge runs once per session. Only the challenge page may load scripts;
real pages load just their HTML document (images, CSS and JS blocked), which
is what keeps traffic at ~200 KB per page instead of ~2 MB. A blocked page
(captcha, login wall) is retried up to 4 times (search pages 8) on a fresh
session. In the test runs above, sessions were almost never blocked.

Everything is parsed from the JSON state MeLi embeds in every page
(`_n.ctx.r=…`), which is the same on all country sites, with the page's JSON-LD
as a fallback — no CSS selectors.

### Codebase

- [src/main.js](src/main.js) — Actor entry: input, proxies per country, dataset output, charging, summary.
- [src/meli/crawler.js](src/meli/crawler.js) — task queue, workers, session
  rotation, retries, pagination, `maxItems` budget, migration state.
- [src/meli/session.js](src/meli/session.js) — shared Chrome, one context per
  proxy session, challenge handling, resource blocking, page classification.
- [src/meli/parse.js](src/meli/parse.js) — search page and product page parsers, output record.
- [src/meli/sites.js](src/meli/sites.js) — country sites, URL helpers.
- `src/meli/*.test.js` — tests against real pages in
  [src/meli/fixtures/](src/meli/fixtures/) (gzipped, captured 2026-10-03
  through Apify residential proxies in AR/BR/CL/CO/MX).
- [.actor/](.actor/) — Actor manifest, Dockerfile and schemas: input, output
  (links to the products, the Overview table and the `summary` record) and
  dataset (type/description of every field plus the Overview and Product
  details views). Apify validates each pushed item against the dataset
  `fields`, so keep [dataset_schema.json](.actor/dataset_schema.json) in step
  with `RECORD_FIELDS` in parse.js when adding or changing a field.
- [legacy/akamai-debug/](legacy/akamai-debug/) — the earlier HTTP-only
  proof-of-work experiment this Actor replaced (not deployed).

Log lines look like:

```
[w3] sess=7129eglh4d6a item item_ok         status=200 chall=0 dur=3.3s kb=222 try=1 mercadolivre.com.br/headphone-bluetooth-max5c-ugreen-75h-anc-43db-cor- | BRL 162.96 Headphone Bluetooth Max5c Ugreen 75h Anc
```

`chall=1` means the page went through the bot challenge, `kb` is proxy
traffic. Outcomes: `list_ok`, `list_empty` (no results), `item_ok`, `captcha`,
`blocked` (login wall / access error), `challenge` (interstitial never
cleared), `ratelimit`, `not_found`, `http_error`, `transport_error`,
`unparsable`; `*_failed` counts pages that ran out of retries and
`item_card_only` the products output from their search result instead.

# Actor input Schema

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

Keywords to search for, as a shopper would type them (e.g. "zapatillas running"). Each term is searched on the MercadoLibre site of the country below.

## `country` (type: `string`):

MercadoLibre site the search terms are searched on. Start URLs always use their own site.

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

MercadoLibre search or category result pages (e.g. https://listado.mercadolibre.com.ar/zapatillas-running, with any filters and sorting applied) and/or product pages (/p/MLA... or articulo.../MLA-...). Any country site.

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

Limit of products to output across all search terms and start URLs, up to 10,000 per run. Leave empty for 10,000.

## `includeProductDetails` (type: `boolean`):

Also open every product page for stock, condition, seller id and reputation, review count, brand, attributes, description, all images and categories. Without it, results come from the search pages alone (title, price, discount, installments, shipping, seller, rating, sold quantity) — about 50x less traffic and much faster.

## `includeAds` (type: `boolean`):

Also output the sponsored ("Promocionado") results shown on search pages. They are marked with isAd = true.

## `proxyConfig` (type: `object`):

Residential proxies are required: MercadoLibre blocks datacenter IPs and IPs outside Latin America. Leave the country empty to use each site's own country (mercadolibre.com.ar → AR, mercadolivre.com.br → BR, ...); a country set here is used for every site.

## Actor input object example

```json
{
  "searchQueries": [
    "zapatillas running"
  ],
  "country": "AR",
  "maxItems": 50,
  "includeProductDetails": false,
  "includeAds": false,
  "proxyConfig": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

## `items` (type: `string`):

All scraped products with every field (url, ids, title, prices, installments, shipping, seller, rating, sold, stock, attributes, ...).

## `overview` (type: `string`):

The same products in the Overview table: image, title, price, discount, free shipping, seller, rating, sold, position and URL.

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

Item count, run time, sessions, proxy traffic and per-outcome counters (list_ok, item_ok, captcha, ...).

# 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": [
        "zapatillas running"
    ],
    "maxItems": 50,
    "proxyConfig": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("dtrungtin/mercadolibre-fast-scraper").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": ["zapatillas running"],
    "maxItems": 50,
    "proxyConfig": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("dtrungtin/mercadolibre-fast-scraper").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": [
    "zapatillas running"
  ],
  "maxItems": 50,
  "proxyConfig": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call dtrungtin/mercadolibre-fast-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,dtrungtin/mercadolibre-fast-scraper"
        }
    }
}
```

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/JrunCrB6xPhsURKGM/builds/5YdEayqWOU544qGdp/openapi.json
