Mercado Libre Scraper & API — 17 Countries
Every price carries its own currency.
Mercado Libre is not one marketplace but seventeen, and they do not behave alike: the large ones return thousands of results per query and have catalog product pages, while the small ones return dozens and have no catalog at all. This Actor reads all seventeen with one parser, and puts a currency on every single row because six of them mix the local currency and US dollars on the same results page.
oswaldocarabano/mercadolibre-data-actor
{
"siteId": "MLV",
"searchQueries": ["iphone"],
"maxItems": 500,
"scrapeDetail": false
}- Version
- v0.1.14
- Memory
- 4096 MB
- Browser
- yes
- Proxy
- Residential, exiting in each marketplace's country, required
Short answer
The Mercado Libre Scraper Actor extracts listings and product pages from 17 Latin American marketplaces with no API key, no account and no quota. Every row carries its own `currency` field, because Uruguay, Paraguay, the Dominican Republic, Nicaragua, Guatemala and Panama show local prices and `US$` prices on the same results page, and summing them without reading each row produces a meaningless number. Venezuelan product pages additionally return `price`, `price_local` and `exchange_rate_implied`, derived from the page's structured data rather than the rendered price. Brazil and Cuba are refused by the Actor rather than silently accepted. Pricing is $0.0045 per listing on the Free plan, falling to $0.0011 on Gold and above, with product pages billed separately at $0.018 because opening one costs about 40 times what a listing row costs.
Key points
- Six of the seventeen marketplaces — Uruguay, Paraguay, the Dominican Republic, Nicaragua, Guatemala and Panama — show the local currency and US dollars on the same results page, which is why every row carries its own `currency` rather than the marketplace carrying one.
- Venezuelan product pages return a dual price: `US$ 580.80` alongside `Bs. 476,314`, plus `exchange_rate_implied` derived from the page's structured data rather than from the rendered price, which splits the cents into a separate element and shifts the rate by 0.14%.
- Three link shapes coexist on one results page — `articulo.…/MLA-…`, `/p/MLA…` and `/up/MLAU…` — so `item_id` is taken from the page's embedded search results array; a URL pattern silently drops the third shape, which can be 40% of a page.
- Measured on 13 September 2026, Mercado Libre serves paginated and sub-category pages only under the `_NoIndex_True` URL form that its own robots.txt disallows by name: in Argentina, 3 of 3 attempts on the plain path returned the anti-bot wall against 3 of 3 pages served on the disallowed form.
- A run that scraped nothing because every page was blocked fails instead of reporting success with zero rows, and every run reports `wall_hits`, `captcha_hits`, `retries` and two separate robots-policy counters.
- Stock and sales figures are returned as the text the site serves — `+25 vendidos` — because Mercado Libre's own API answers `RANGO_1_50` for the same data, and no exact integer is invented from a range.
- Brazil and Cuba are refused with a clear error rather than accepted: Brazil until there is a documented LGPD basis, Cuba because its proxy route is unverified.
What it does
Mercado Libre is not one marketplace but seventeen, and they do not behave alike: the large ones return thousands of results per query and have catalog product pages, while the small ones return dozens and have no catalog at all. This Actor reads all seventeen with one parser, and puts a currency on every single row because six of them mix the local currency and US dollars on the same results page.
{
"item_id": "MLV12345678",
"title": "Apple iPhone 13 128 Gb Medianoche",
"url": "https://articulo.mercadolibre.com.ve/MLV-12345678",
"price": 580.8,
"currency": "USD",
"currency_symbol": "US$",
"price_local": 476314,
"currency_local": "VES",
"exchange_rate_implied": 820.1,
"condition": "Nuevo",
"shipping_text": "Envío gratis",
"link_kind": "publication",
"is_catalog": false,
"position": 3,
"query": "iphone",
"site_id": "MLV",
"scraped_at": "2026-09-13T10:22:41Z",
"from_cache": false,
"data_age_hours": 0
}Why this one
A currency on the row, not on the marketplace
Most marketplace scrapers attach one currency per country, which is correct in eleven of these seventeen markets and wrong in six. In Uruguay, Paraguay, the Dominican Republic, Nicaragua, Guatemala and Panama the local currency and `US$` appear in the same results page, so a column of prices summed under one currency label is a number with no meaning. Every row here carries `currency` and `currency_symbol` as served.
The Venezuelan dual price, and where the rate comes from
A Venezuelan product page shows both a dollar price and a bolívar price. Reading them off the rendered page gives the wrong ratio, because the rendered price splits the cents into a separate element — a 0.14% shift. The rate here is derived from the page's structured data instead, and across the product pages measured on one day it came out identical on every single one. Nobody else in this category returns the pair at all.
Two marketplaces wearing one brand
An `iphone` search returns 6,372 results in Mexico and 26 in Panama. The large markets have catalog product pages and installments; the small ones have neither, and one page is the entire result set with nothing to paginate. The Actor detects this and requests the next page only when the previous one came back full, so a query in Panama costs exactly one page load rather than a sequence of empty ones.
The robots.txt problem, stated instead of resolved quietly
Mercado Libre's robots.txt allows page 1 of a search and disallows both `/*_Desde_` and `/*_NoIndex_True`. The site now serves pagination and sub-categories only under that second form. So there is no polite pagination available today, and the Actor does not pretend otherwise: pages fetched against the policy are counted in `pages_disallowed_by_robots_fetched`, pages skipped for it in `pages_truncated_by_robots_policy`, and the operator chooses with `respectRobots`.
Six responses, one of which is data
The anti-bot wall answers HTTP 200 with a well-formed page and zero listings, which is indistinguishable from an exhausted page to anything that checks the status code. The Actor separates a page with data, an exhausted page, the wall, a CAPTCHA, a page that had not finished rendering, and a page returning a plausible but nearly empty result set. Only the first is treated as data, and a wall retires the proxy session rather than being retried on it.
The wall is absorbed, not billed
A blocked page costs a full browser load of 2.4 to 4.5 MB and returns nothing. Those are never charged, and neither is a product page that failed to open — if the detail fetch fails you get the listing row and pay the listing rate. Charging for attempts would make the wall rate the customer's problem instead of the Actor's.
Use cases
- Track prices for a product category across several Latin American marketplaces with one comparable schema.
- Measure how a brand is priced in Venezuela in both dollars and bolívares, using the dual price rather than an external exchange rate.
- Build a competitor catalogue in one country, with seller, condition and shipping text as the marketplace serves them.
- Compare marketplace depth between countries before deciding where a product is worth listing.
- Feed a pricing model rows that each declare their own age through `from_cache`, `scraped_at` and `data_age_hours`.
Input
Every field has a default, and the defaults are deliberately small so a first run is cheap enough to inspect before you commit to a sweep. This table mirrors the Actor's own input schema field for field.
| Field | Default | What it does |
|---|---|---|
siteIdstring | "MLA" | Country marketplacepersonal dataOne of the 17 supported marketplaces, from MLA (Argentina) to MPA (Panamá). MLB (Brasil) and MCU (Cuba) are refused rather than accepted. |
searchQueriesstring[] | [] | Search queriesFree-text searches, e.g. `iphone`. Page 1 returns up to 48 listings. |
categoryUrlsstring[] | [] | Category URLsCategory listing URLs to scrape directly, when you already know the category rather than the search term. |
startUrlsobject[] | [] | Start URLsListing or product URLs. A product URL is scraped as a detail page and billed at the detail rate. |
maxItemsinteger | 100 | Max itemsHard cap on rows. The crawl stops as soon as the cap is reached, so pages already queued are not fetched. |
scrapeDetailboolean | false | Scrape product detail pagespersonal dataOpens every product page for rating, reviews, location, stock ranges, attributes, seller and the dual price. Multiplies browser page loads by about 40 and is billed as a separate event. |
allowPaginationboolean | true | Paginate beyond the first pagepersonal dataFollows pagination in the `_NoIndex_True` form the site actually serves, which its robots.txt disallows by name. Every such page is counted in `pages_disallowed_by_robots_fetched`. |
respectRobotsboolean | false | Respect robots.txt strictlySkips every disallowed path, which in practice now means page 1 per query and little else. robots.txt is downloaded at the start of the run; if it cannot be read, the run fails rather than claim compliance it could not verify. |
categorySlicingboolean | true | Slice queries by categorypersonal dataAlso fetches page 1 of each sub-category found on the results page. The only route past the ~2,000 item ceiling, and its coverage gain has not been measured. |
maxSlicesinteger | 20 | Max category slices per queryUpper bound on sub-category pages per query, so slicing cannot quietly multiply a run. |
maxConcurrencyinteger | 2 | Max concurrencyParallel browser pages. Each listing page is 2.4-4.5 MB, which is why the default is low. |
useCacheboolean | true | Use cacheReuses listings seen in the last 24 hours. Cached rows are charged normally and always declare `from_cache` and `data_age_hours`. |
Output and fill rates
A field being in the schema is not the same as it having a value. The percentages below were counted on real runs; the sample sizes are in Measurements. Anything not listed here is not promised.
| Field | Filled | Meaning |
|---|---|---|
item_idstring | 100% | Primary key, cache key and dedupe key, taken from the page's embedded search results array rather than parsed out of the URL. |
titlestring | 100% | With `url` and `thumbnail`. Left in the marketplace's own language, because `Envío gratis` is data rather than interface. |
pricenumber | 100% | With `currency` and `currency_symbol`, per row and never per marketplace. |
price_originalnumber | not measured | Only present when the page shows a struck-through price. |
seller_namestring | not measured | With `condition`, `shipping_text` and `installments_text`. Fill rates vary widely by marketplace: the small markets have no installments at all. |
link_kindstring | 100% | With `is_catalog`. One of `publication`, `catalog` or `user_product` — the three link shapes that coexist on one results page. |
positioninteger | 100% | With `query` and `source_url`, so every row says where on which page it came from. |
site_idstring | 100% | With `scraped_at`, `from_cache` and `data_age_hours`. Every row declares its own age. |
ratingnumber | not measured | With `reviews_count`. Detail pages only — absent from listing pages on every marketplace. |
sold_quantity_textstring | not measured | Detail pages only, returned as served: `+25 vendidos`. Mercado Libre's own API answers `RANGO_1_50` for the same data, so no exact integer is invented. |
attributesobject | not measured | With `brand`, `sku`, `availability`, `breadcrumb` and `warranty_text`. Detail pages only. |
price_localnumber | not measured | With `currency_local` and `exchange_rate_implied`. The Venezuelan dual price, derived from structured data rather than the rendered price. |
Every key is always present. A field that exists but is empty comes back as explicit null, so a parser never has to guess.
Datasets
Different record types go to different datasets, so the main table never carries columns that are blank on most rows.
defaultOne row per listing, in snake_case, with product text left in the marketplace's own language.billedERRORSFailed pages, kept separate so a parse failure never arrives mixed into the results.never billedRUN_STATS (key-value store)Queries, pages fetched, items pushed and from cache, wall and CAPTCHA hits, retries, sessions retired, both robots counters, detail pages fetched and errors.never billed
Pricing
Pay per delivered result. Charges are applied as each row is produced rather than in a lump at the end, so an aborted run bills only for what it actually gave you.
| Event | Price | Notes |
|---|---|---|
apify-actor-startActor start | $0.00001 | Charged per gigabyte of run memory, minimum one event. Effectively free. |
item-scrapedListing scraped | $0.0045 | One listing delivered from a results page, on the Free plan. $0.0019 on Bronze, $0.0014 on Silver and $0.0011 on Gold and above. Pages blocked by the anti-bot wall are never charged, because the wall costs a full browser load and the Actor absorbs it. |
item-detail-scrapedProduct page scraped | $0.018 | One listing enriched with its product page — rating, stock ranges, attributes, seller and the dual price — on the Free plan. $0.014 on Bronze, $0.0115 on Silver and $0.0095 on Gold and above. Priced separately because opening a product page costs about 40 times what a listing row costs, and only charged when the page actually opened. |
Measurements
Each figure is shown with the method that produced it. A benchmark without a method is a marketing claim wearing a number's clothes.
Marketplace depth
6,372 results in Mexico, 26 in Panama
The same `iphone` query measured across all 17 marketplaces. Argentina returned 5,078, Colombia 5,305, Venezuela 436 and Bolivia 27 — which is why pagination is requested only after a full page rather than by default.
Mixed currencies
6 of 17 marketplaces
Uruguay, Paraguay, the Dominican Republic, Nicaragua, Guatemala and Panama show local and US dollar prices in the same results page. Counted per marketplace across the same measurement pass.
Pagination against the wall
0 of 3 plain, 3 of 3 disallowed form
Measured in Argentina on 13 September 2026: `/iphone_Desde_49` returned the anti-bot wall on 3 of 3 attempts, `/iphone_Desde_49_NoIndex_True` served results on 3 of 3. The same split was measured in Mexico.
Derived exchange rate
identical on every product page measured
Venezuelan product pages on one day, comparing the rate derived from structured data against the rendered price. The rendered price splits the cents into a separate element and shifts the rate by 0.14%.
Query ceiling
about 2,000 items, roughly 39% of the total
Paginating an Argentine `iphone` search to the very end, against the result count the site itself reports for that query.
Listing page weight
2.4-4.5 MB per page
Measured across marketplaces. It is the reason the default concurrency is 2 rather than the platform default.
Catalog pages and installments
9 of 17 marketplaces
Argentina, Mexico, Colombia, Chile, Uruguay, Peru and Ecuador have catalog pages and installments; Venezuela, Bolivia, Paraguay, the Dominican Republic, Costa Rica, Nicaragua, Guatemala, Honduras, El Salvador and Panama have neither.
What it will not do
Stated plainly so you can judge fit before spending anything.
- Brazil (MLB) and Cuba (MCU) are refused. Brazil is out until there is a documented LGPD basis; Cuba is unreviewed and its proxy route is unverified.
- Roughly 2,000 items per query is a hard ceiling. Paginating an Argentine `iphone` search to the end reaches about 39% of what the site itself says it has.
- `categorySlicing` is the only route past that ceiling, and how much extra coverage it gives has not been measured and is not promised.
- Rating, reviews, location, stock ranges, attributes, seller id, description and the dual price appear on no marketplace's listing pages. They need `scrapeDetail`, which multiplies page loads by about 40.
- Stock and sales figures are ranges, not numbers: `+25 vendidos` is returned as text, and any derived number is named `_min` because a minimum is what it is.
- Buyer questions and reviews are out of scope entirely — that is text written by identifiable people.
- Seller phone numbers and email addresses are never returned, and seller profile pages under `/perfil/` are never touched.
- Coverage beyond page 1 varies by country and by run. In one pass Colombia and Chile returned the anti-bot wall for both URL forms.
- One snapshot is one moment: cached rows are at most 24 hours old and every row says how old it is.
Privacy
- No account, no session cookies, no OAuth and no API key: the Actor never signs in.
- CAPTCHAs are never solved, by any method, and a sustained CAPTCHA rate stops the run.
- Buyer questions and reviews are not scraped, because that is text written by identifiable people.
- No seller phone numbers and no email addresses, which some Actors in this category do return.
- Seller profile pages under `/perfil/` are disallowed by robots.txt and are never requested.
- Images are not rehosted; rows carry image URLs.
- Listings are cached for at most 24 hours and every row declares its age.
- Removal requests, quoting the `item_id`: privacy@actorstack.dev
See also the data removal process.
Frequently asked questions
Do I need a Mercado Libre API key or a developer account?
Why does every row have its own currency field?
Can I get the price in both dollars and bolívares for Venezuela?
Why can't I get ratings and stock from the listing pages?
Is pagination allowed by Mercado Libre's robots.txt?
How many results can one query return?
Am I charged for pages that get blocked?
Why does it refuse Brazil?
Does it return exact stock or sales numbers?
What happens if every page gets blocked?
Guides for this Actor
- How to scrape Mercado LibreA working method for extracting listings and product pages from any of the 17 Mercado Libre marketplaces, including the two decisions — pagination and detail pages — that decide what a run costs.
- API alternativeThe official Mercado Libre API requires OAuth and returns 403 to an anonymous request. A comparison of what the API gives an authorised caller, what scraping gives anyone, and which fields exist in only one of the two.
- The 17 marketplacesA reference table of every Mercado Libre site id, its country, its currency, the depth of a single query and whether it has catalog pages, mixed currencies and installments.
- Mixed-currency pagesIn Uruguay, Paraguay, the Dominican Republic, Nicaragua, Guatemala and Panama the local currency and US dollars appear in the same list of results — which makes a per-country currency column quietly wrong.
- Venezuela's dual priceVenezuelan product pages show a dollar price and a bolívar price at once. Deriving the implied rate from the page's structured data rather than the rendered price is the difference between a stable number and one that drifts.
- Pagination and robots.txtMeasured in September 2026: the plain paginated path returns the anti-bot wall and the `_NoIndex_True` form returns results — and robots.txt disallows the second by name. There is currently no polite pagination available.
- item_id and link shapesMercado Libre results mix `articulo.…/MLA-…`, `/p/MLA…` and `/up/MLAU…` links. Deriving the item id from the URL looks fine until the third shape appears, which can be nearly half a page.
- The anti-bot wallMercado Libre's anti-bot response is a well-formed page with a 200 status and zero listings. Six different responses have to be told apart, and only one of them is data.
- Stock and sales rangesThe site shows `+25 vendidos` and the official API answers `RANGO_1_50`. Any dataset with an exact sales integer in it invented that integer.
- Listing versus detailWhich fields only exist on product pages, what turning on `scrapeDetail` does to a run's page loads and its bill, and how to get the enriched fields for only the rows that need them.