<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Miguel Pinzon</title>
    <description>The latest articles on DEV Community by Miguel Pinzon (@tautmushroom0).</description>
    <link>https://dev.to/tautmushroom0</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4243435%2Fbe38439b-5470-4701-bc47-f9101592016f.jpg</url>
      <title>DEV Community: Miguel Pinzon</title>
      <link>https://dev.to/tautmushroom0</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tautmushroom0"/>
    <language>en</language>
    <item>
      <title>Is that Black Friday "discount" real? Tracking MercadoLibre prices with the official catalog API</title>
      <dc:creator>Miguel Pinzon</dc:creator>
      <pubDate>Fri, 13 Nov 2026 18:27:19 +0000</pubDate>
      <link>https://dev.to/tautmushroom0/is-that-black-friday-discount-real-tracking-mercadolibre-prices-with-the-official-catalog-api-a4d</link>
      <guid>https://dev.to/tautmushroom0/is-that-black-friday-discount-real-tracking-mercadolibre-prices-with-the-official-catalog-api-a4d</guid>
      <description>&lt;p&gt;Every November, MercadoLibre listings show a crossed-out "original price" next to a shiny discount.&lt;br&gt;
Sellers set that number themselves. The only way to know whether a deal is real is to compare today's&lt;br&gt;
price with what the product actually sold for over the last weeks.&lt;/p&gt;

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

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

&lt;p&gt;What still works with a plain application token (client_credentials, no user login):&lt;/p&gt;

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

&lt;p&gt;That's a cleaner source than any HTML scraper: canonical product ids, no anti-bot, no proxies.&lt;/p&gt;

&lt;p&gt;Two gotchas worth knowing: &lt;code&gt;/products/{id}/items&lt;/code&gt; sometimes answers a CloudFront 400 for products&lt;br&gt;
with no offers instead of the documented 404 (a retry fixes it), and &lt;code&gt;/products/search&lt;/code&gt; rate-limits&lt;br&gt;
with 429 after a burst (backoff works).&lt;/p&gt;
&lt;h3&gt;
  
  
  Turning snapshots into history
&lt;/h3&gt;

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

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

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

&lt;p&gt;For &lt;code&gt;{"country": "MCO", "queries": ["iphone 15 128gb"]}&lt;/code&gt; each row includes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;"productId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"MCO27172671"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Apple iPhone 15 (128 GB) - Verde"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"bestPrice"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1800000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"currency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"COP"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"offersCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"lowestPriceSeen"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1800000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"lowestPriceSeenAt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"2026-11-12T01:02:47Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"realDiscountPct"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"listedOriginalPrice"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"history"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"2026-11-12T01:02:47Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;"bestPrice"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1800000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;"offersCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"offers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"itemId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"MCO2358894185"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;"sellerId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"113812212"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1800000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;"condition"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"new"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;"freeShipping"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;realDiscountPct&lt;/code&gt; compares today's best price with the lowest price ever observed, not with the&lt;br&gt;
seller's "original price". That single number answers the Black Friday question.&lt;/p&gt;

&lt;p&gt;Actor page: &lt;a href="https://apify.com/pricelatam/mercadolibre-price-tracker" rel="noopener noreferrer"&gt;https://apify.com/pricelatam/mercadolibre-price-tracker&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Actor 2: price-drop alerts on a watchlist
&lt;/h3&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"country"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"MCO"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;"products"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"MCO27172677"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"MCO27172667"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;"compareTo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"last7Days"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;"minDropPct"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Actor page: &lt;a href="https://apify.com/pricelatam/mercadolibre-price-drop-alerts" rel="noopener noreferrer"&gt;https://apify.com/pricelatam/mercadolibre-price-drop-alerts&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Running them
&lt;/h3&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;apify_client&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;ApifyClient&lt;/span&gt;
&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ApifyClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;APIFY_TOKEN&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"pricelatam/mercadolibre-price-tracker"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;run_input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"country"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"MCO"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"queries"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"iphone 15 128gb"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;default_dataset_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;iterate_items&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"bestPrice"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"realDiscountPct"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;h3&gt;
  
  
  What's next
&lt;/h3&gt;

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

</description>
      <category>webscraping</category>
      <category>api</category>
      <category>ecommerce</category>
      <category>java</category>
    </item>
  </channel>
</rss>
