Every November, MercadoLibre listings show a crossed-out "original price" next to a shiny discount. Sellers set that number themselves. The only way to know whether a deal is real is to compare today's price with what the product actually sold for over the last weeks.

I wanted that data for Latin America and found nothing: price-history sites exist for Amazon (camelcamelcamel) but not for MercadoLibre's 18 marketplaces. So I built it: a price tracker with shared history, and a price-drop alert on top of it.

The data problem

MercadoLibre closed its public search endpoint in 2024: GET /sites/MCO/search returns 403 even with an app token, and the website sits behind a JavaScript proof-of-work challenge plus Akamai. Every scraper in the Apify Store fights that with residential proxies and headless browsers.

What still works with a plain application token (client_credentials, no user login):

  • GET /products/search?site_id=MCO&domain_id=MCO-CELLPHONES&q=iphone 15 — catalog products. Without domain_id the relevance is useless (you get cables and books); with it you get real product cards.
  • GET /sites/MCO/domain_discovery/search?q=... — maps free text to a domain, so users don't need to know domain ids.
  • GET /products/{id}/items — every seller offer for that catalog product: price, seller, condition, free shipping, official store, "original price".

That's a cleaner source than any HTML scraper: canonical product ids, no anti-bot, no proxies.

Two gotchas worth knowing: /products/{id}/items sometimes answers a CloudFront 400 for products with no offers instead of the documented 404 (a retry fixes it), and /products/search rate-limits with 429 after a burst (backoff works).

Turning snapshots into history

A single run only gives today's prices. The value is in the time series, so every observation goes into Postgres (Neon):

  • ml_product — one row per catalog product with current best price, lowest/highest ever seen, and when.
  • ml_offer — one row per seller offer.
  • ml_product_price_point — a point only when the best price changed, so the table stays small.

Scheduled runs seed ~200 popular products per country per day (Colombia, Mexico, Argentina, Brazil) for about half a dollar a month of compute. After a few weeks, any popular product already has a curve when someone asks for it. The history is shared: every run by every user adds to the same series.

Actor 1: the price tracker

For {"country": "MCO", "queries": ["iphone 15 128gb"]} each row includes:

{ "productId": "MCO27172671", "name": "Apple iPhone 15 (128 GB) - Verde", "bestPrice": 1800000, "currency": "COP", "offersCount": 4, "lowestPriceSeen": 1800000, "lowestPriceSeenAt": "2026-11-12T01:02:47Z", "realDiscountPct": 0, "listedOriginalPrice": null, "history": [{"at": "2026-11-12T01:02:47Z", "bestPrice": 1800000, "offersCount": 4}], "offers": [{"itemId": "MCO2358894185", "sellerId": "113812212", "price": 1800000, "condition": "new", "freeShipping": true}] } 

realDiscountPct compares today's best price with the lowest price ever observed, not with the seller's "original price". That single number answers the Black Friday question.

Actor page: https://apify.com/pricelatam/mercadolibre-price-tracker

Actor 2: price-drop alerts on a watchlist

Most people don't want a dataset; they want to know when something they care about gets cheaper. The second Actor takes a list of catalog products (ids, /p/MCO... URLs or keywords), compares the current best price with the shared history (lastObservation, last7Days, last30Days or lowestEver) and returns only the products that dropped by more than your threshold:

{"country": "MCO", "products": ["MCO27172677", "MCO27172667"], "compareTo": "last7Days", "minDropPct": 5} 

Schedule it every few hours and attach Apify's email, Slack or webhook integration to the run, and you have a price alert without writing a line of code. Products seen for the first time come back as baseline and are not charged as alerts.

Actor page: https://apify.com/pricelatam/mercadolibre-price-drop-alerts

Running them

Both are pay-per-event Actors on Apify (tracker from $2.50 per 1,000 products; alerts $0.001 per product checked plus $0.004 per drop reported; no platform charges), so there is nothing to host:

from apify_client import ApifyClient client = ApifyClient("<APIFY_TOKEN>") run = client.actor("pricelatam/mercadolibre-price-tracker").call( run_input={"country": "MCO", "queries": ["iphone 15 128gb"]}) for row in client.dataset(run.default_dataset_id).iterate_items(): print(row["name"], row["bestPrice"], row["realDiscountPct"]) 

They also work as tools for AI agents through the Apify MCP server (https://mcp.apify.com/?tools=pricelatam/mercadolibre-price-tracker,pricelatam/mercadolibre-price-drop-alerts), so you can ask Claude or Cursor "has the iPhone 15 price dropped in Colombia this month?" and get the answer from real data.

What's next

A seller monitor (every listing of one seller, with history; useful for brands watching their resellers' prices) and catalog lookup by barcode. If you track prices in LatAm and need something specific, tell me in the comments or in the Actors' Issues tab; the history store is already there, the rest is a thin layer.