OLX Brazil Scraper API
Pricing
Pay per event
OLX Brazil Scraper API
Read olx.com.br search and category pages and get one row per listing: id, title, price in BRL, category, city and state, publish date, image URLs, per-category attributes and whether the seller is private or a business. $0.85 per 1,000 listings.
Pricing
Pay per event
Rating
0.0
(0)
Developer
Dami's Studio
Maintained by CommunityActor stats
0
Bookmarked
3
Total users
2
Monthly active users
2 days ago
Last modified
Categories
Share
Reads search and category result pages on olx.com.br and gives you one row per listing: id, title, price in BRL, category, city and state, publish date, image URLs, whether the seller is private or a business, and the listing URL.
It runs without a browser and never fetches a per-listing detail page, because it doesn't need to. About 50 listings come back per request. Seller phone numbers are not included, and the section below explains why.
What it returns
One row per listing. The percentages are real coverage measured on the shipped build over a
300-row run on ?q=iphone and a 120-row run on imoveis/venda/estado-rj, both on 2026-08-10:
| Field | Filled | Example |
|---|---|---|
listingId | 100% | 1517568906 |
title | 100% | iPhone 12 Pro Max 128GB | Conservado | Caixa Original |
price / priceLabel | 100% | 3000 / R$ 3.000 |
currency | 100% | BRL |
oldPrice / oldPriceLabel | 19% | 3200 / R$ 3.200, only when the seller cut the price |
category / categoryId / parentCategoryId | 100% | Celulares e Smartphones / 3060 / 3000 |
city / state / stateName | 100% | São Paulo / SP / São Paulo |
neighbourhood | 95–98% | Paraíso do Morumbi |
areaCode / locationLabel | 100% | 11 / São Paulo, Paraíso do Morumbi - DDD 11 |
publishedAt / publishedTimestamp | 100% | 2026-08-10T01:54:40.000Z / 1786413280 |
sellerType | 100% | private or business |
sellerName | 100% | Fatima — the public display name on the ad |
thumbnailUrl / imageUrls / imageCount | 100% | first image, all images, count |
hasVideo | always present | true on 0% of both runs |
isFeatured | always present | true on 25% (phones), 1% (property) |
isBumped | always present | true on 1% (phones), 0% (property) |
hasFreeShipping | always present | true on 100% (phones), 0% (property) |
attributes | 100% | { "Marca": "APPLE", "Modelo": "IPHONE 12 PRO MAX", "Condição": "Usado" } |
url | 100% | the listing page |
searchUrl, page, position, scrapedAt | 100% | provenance |
searchQuery | only when you passed a search term | iphone |
Attribute keys come straight from OLX and differ per category. On the phone run every row carried
Categoria, Condição, Cor, Marca, Modelo, plus Memória interna and Saúde da bateria on
98%. On the property run every row carried Categoria, Tipo, Quartos, Banheiros, plus
Condomínio on 99% and IPTU on 95%.
What it does not do
- No seller phone numbers. They are not in the payload this actor reads, and it does not go
looking for them anywhere else.
sellerTypeand the public display name are all you get. - No single-listing pages. Paste a search or category URL, not an ad URL. An ad URL returns an uncharged diagnostic row explaining that.
- No jobs.
vagas-de-empregois a separate OLX product that renders through a different component and carries no listing payload on the results page. - No more than about 5,000 listings per search. OLX serves 50 per page and repeats page 100
forever after that —
?o=101and?o=150return byte-for-byte the same 50 ads as?o=100. Narrow by state, category or price band to reach deeper stock. - No "highest price first" sort. OLX offers it in its own UI but does not expose it as a URL parameter that survives a plain request. Relevance, newest and lowest-price all work.
Input
{"searchQueries": ["notebook"],"category": "informatica","state": "SP","sortBy": "date","minPrice": 500,"maxPrice": 3000,"maxItems": 250}
Or paste URLs and keep every filter you already set in the OLX UI:
{"startUrls": ["https://www.olx.com.br/celulares/estado-sp?q=iphone&ps=1000&pe=3000"],"maxItems": 100}
Every input above was checked against live result counts before it was exposed: state cut an
iphone search from 103,531 to 19,372 listings, the ps/pe price band cut it to 46,186,
category + state and both sort orders all changed the returned set.
region narrows inside a state using the slug OLX shows in its own URLs, for example
sao-paulo-e-regiao. A region slug OLX does not recognise is ignored silently. You get the
whole state back with no error at all. This actor detects that and adds one uncharged REGION_NOT_APPLIED row
listing the region slugs that would have worked.
Empty input returns one labelled sample row and charges nothing.
How it works, and why it used to fail
OLX is a Next.js App Router site with no public JSON search endpoint. The listing data is in the
React Server Component payload, as a plain "ads": [ ... ] array with every listing already
denormalised — no detail-page fetch is needed for any field listed above.
The hard part was reaching it. olx.com.br is behind a Cloudflare firewall rule that refuses most datacenter addresses with a 5.8 KB "Sorry, you have been blocked" page, which is why earlier versions of this actor succeeded or failed apparently at random.
That rule only matches requests shaped like a top-level page navigation. Measured across 14 fresh Apify datacenter exit addresses, same minute, same TLS fingerprint:
| Request shape | Addresses served |
|---|---|
| plain page request | 1 / 14 |
+ x-requested-with: XMLHttpRequest | 8 / 14 |
+ RSC: 1 and a same-origin referer | 8 / 14 |
The actor sends both headers and takes the RSC form, whose response is the flight payload alone.
That's 260 KB instead of 730 KB, and about twice as fast. For the addresses still refused, it walks a
ladder of exit addresses until one is served and then pins it for the rest of the run; a pinned
address returned 25 pages out of 25 without a single refusal. Requests use impit with Chrome's
real TLS fingerprint, because plain Node is rejected at the handshake.
Measured on the Apify platform, 2026-08-10: 32 of 32 runs succeeded, including 10 deliberately run with the container's own address disabled, so that every request had to go through the exit ladder. Those 10 needed at most 3 addresses each. A 999-listing run took 58 seconds.
Billing
$0.85 per 1,000 listings, plus a $0.001 start fee per run. It's a flat rate, the same number on the free plan as on any paid one.
Blocked requests, empty results, bad URLs, the region warning and the sample row are never charged. You pay for listings actually returned, one charge per row.
FAQ
Can I scrape OLX Brazil without a Brazilian IP address? Yes. This actor runs from ordinary US datacenters and never uses a Brazilian address. OLX's firewall rule is about address reputation and request shape, not geography.
Why did my run return zero rows and a BLOCKED row?
Every exit address the run had available was refused. That's rare now, 0 of 32 test runs hit it,
but if it happens, re-run, or put your own proxy URLs in proxyConfiguration. Nothing was charged.
How many listings can I get for one search term? About 5,000: 50 per page, 100 pages, then OLX repeats page 100. Split by state, category or price band to go deeper. A 1,000-listing run took 58 seconds.
Does it return the seller's phone number? No, by design.
Can I monitor new listings?
Set sortBy to date and a small maxItems. Newest ads come first and every row carries
publishedAt, so you can diff against your last run.
What currency are prices in?
Brazilian reais. price is numeric, priceLabel keeps OLX's own formatting.
Does it work for property and cars, not just goods?
Yes. imoveis/venda, imoveis/aluguel and autos-e-pecas/carros-vans-e-utilitarios are all
result pages with the same payload; a property run returns Quartos, Banheiros, Condomínio and
IPTU in attributes.