OLX Scraper - Poland, Romania, Ukraine, Bulgaria & Portugal
Pricing
from $1.00 / 1,000 run start fees
OLX Scraper - Poland, Romania, Ukraine, Bulgaria & Portugal
Search OLX in Poland, Romania, Bulgaria, Ukraine and Portugal and get every ad as a row: title, asking price as a number, currency, the seller's town, new or used, and whether the price is negotiable. For price research, resellers and market monitoring.
Pricing
from $1.00 / 1,000 run start fees
Rating
0.0
(0)
Developer
SR
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
13 days ago
Last modified
Categories
Share
OLX Scraper
OLX Scraper searches OLX and returns ads as rows: title, asking price as a number, the seller's town, whether the item is new or used, and whether the price is negotiable. Covers Poland, Romania, Bulgaria, Ukraine and Portugal from one actor.
The actor reads public search and category listings. You do not need an OLX account. Choose a search term in the local language or paste an OLX search URL with the filters you need.
Use it for second-hand price research, cross-border reselling, supply monitoring in a category, or any dataset of classified ads that you would otherwise copy out of the browser by hand. Results come out as JSON, CSV, Excel or through the Apify API, with the same field names in every country.
What OLX Scraper returns
- Five markets, one actor. Each OLX country runs its own domain and its own search path:
/oferty/in Poland,/oferte/in Romania,/ads/in Bulgaria and Portugal,/uk/list/in Ukraine. Pick a market and the right shape is used - Currency read off the price, not assumed. Bulgaria has moved to the euro, so
olx.bgnow prints139.99 €. Anything with BGN hard-coded is mislabelling every Bulgarian row today - Prices parsed per row, not per locale. Portugal writes
1.400 €meaning fourteen hundred; Bulgaria writes139.99 €meaning a hundred and forty. Same character, opposite meanings, same website. Both come out right price_typeinstead of a bare null. A free ad, a swap-only ad and an ad with no stated price are three different things, and none of them is a parse failurenegotiableas its own flag, because "6 800 zł do negocjacji" is an opening position rather than a price- Condition in the market's own wording where the seller set it, read from the card rather than translated
- Ad id, town and posting date on every row, so consecutive runs diff cleanly
Why OLX
OLX is the classifieds market across Central and Eastern Europe and Portugal. In Poland it is where used goods are actually bought and sold, the way Marktplaats is in the Netherlands or Kleinanzeigen is in Germany. For second-hand pricing in those countries there is no meaningful second source.
Because it is one platform under many domains, the same query can be run across five countries and compared directly. That is the part most tooling misses: the country-scoped actors on the Store cover one market each, so a cross-border comparison means running several and reconciling their formats yourself. The formats are the hard part, and they are not consistent even within OLX.
Input
| Field | Type | Required | Default | What it does |
|---|---|---|---|---|
query | string | one of the two | none (prefill rower) | What to search for, in the market's language |
market | select | no | pl | pl, ro, bg, ua or pt |
url | string | one of the two | – | An OLX search or category URL, from the desktop or the mobile site. Takes precedence |
price_min | integer | no | – | Lower bound in the market's currency |
price_max | integer | no | – | Upper bound in the market's currency |
limit | integer | no | 50 | Ads to return, 1 to 1000. A page carries about 50 |
retries | integer | no | 3 | Retry attempts per page, 1 to 6 |
A full input with every option, as used for a real run on the Portuguese site:
{"query": "bicicleta","market": "pt","price_min": 100,"price_max": 500,"limit": 120,"retries": 2}
That run returned 120 ads from three result pages, all priced between 100 and 500 €, 50 of them marked negotiable.
How to scrape OLX with OLX Scraper
- Pick the market.
plfor olx.pl,rofor olx.ro,bgfor olx.bg,uafor olx.ua,ptfor olx.pt. - Type the search term in that market's language.
rowerin Poland,bicicletăin Romania,колелоin Bulgaria,велосипедin Ukraine,bicicletain Portugal. An English term works only as far as sellers write English titles. - Or paste a URL. Open the OLX site, set the category and filters you want, and copy the address bar into
url. A link copied on your phone (m.olx.pl/...) works as well as the desktop address. Keepmarketon the same country. - Set
limit. 50 is one result page; 1000 is about twenty. - Run, then export. The dataset downloads as JSON, CSV or Excel, or comes back from the API in the same call when you use the synchronous run endpoint.
Output
{"ad_id": "1098286495","url": "https://www.olx.pl/d/oferta/rower-szosowy-kolarka-vintage-CID767-ID1ckiuj.html?search_reason=search%7Cpromoted","title": "Rower szosowy kolarka vintage","price": 900.0,"price_text": "900 zł","price_type": "asking price","negotiable": false,"currency": "PLN","location": "Nowy Glinnik","posted_label": "Odświeżono dnia 02 października 2026","condition": "Używane","is_promoted": false,"image": "https://ireland.apollo.olxcdn.com:443/v1/files/bw22j0yc5aav-PL/image;s=216x152;q=50","market": "pl","query": "rower","position": 1,"country": "PL","fetched_at": "2026-10-04T03:46:59.273757Z","source": "olx-scraper","listing_id": "1098286495","image_url": "https://ireland.apollo.olxcdn.com:443/v1/files/bw22j0yc5aav-PL/image;s=216x152;q=50"}
Two more real rows from the same day. A free ad in Poland, where price is 0 and price_type says why:
{"ad_id": "1102516739","title": "Rower oraz hulajnoga dla dziewczynki","price": 0.0,"price_text": "Za darmo","price_type": "free","negotiable": false,"currency": "PLN","location": "Chylice","posted_label": "03 października 2026","condition": "Używane","market": "pl","position": 4}
And a negotiable ad in Portugal, where the price text carries the flag:
{"ad_id": "673969412","title": "Bicicleta de estrada Bianchi ML3 Alloy Reparto Corse, ano 2000","price": 495.0,"price_text": "495 € Negociável","price_type": "asking price","negotiable": true,"currency": "EUR","location": "Estrela","posted_label": "01 de outubro de 2026","condition": "Usado","market": "pt","position": 2}
Both rows are shortened for reading; in the dataset they carry every field shown in the first example.
Output fields
| Field | Type | Meaning |
|---|---|---|
ad_id | string | OLX's id for the ad |
url | string | Link to the ad page |
title | string | Ad title as the seller wrote it |
price | number or null | Asking price; 0 for a free ad, null for swap or no price |
price_text | string or null | The price exactly as OLX shows it |
price_type | string | asking price, free, swap or not stated |
negotiable | boolean | The seller marked the price as negotiable |
currency | string | PLN, RON, EUR or UAH, read from the price |
location | string or null | Town (and district where given) |
posted_label | string or null | Posting or refresh date in the market's language |
condition | string or null | New or used, in the market's wording |
is_promoted | boolean | Promoted-ad flag |
image | string or null | Thumbnail URL |
market | string | The market you searched |
query | string or null | Your search term, or null for a URL run |
position | integer | Rank in the results, 1 upward across pages |
The existing row fields remain available. Each row also has source, listing_id, image_url, fetched_at and country. listing_id repeats ad_id; image_url repeats image. fetched_at is a UTC timestamp shared across the search, including later pages. country is the uppercase country code for market. These additions help combine results from different marketplaces without changing existing consumers.
The summary record reports returned ads, pages containing ads, price states, negotiability, location and condition coverage, the market and the currencies observed. If a later page cannot be read, already collected ads are retained and the errors record identifies the affected page. Missing results are never represented as invented ads.
Use cases
Second-hand pricing in a country you do not live in. Run your product term, filter to price_type: asking price, and take the median. condition lets you separate the new-in-box listings from the used ones, which usually sit in different price bands.
Cross-border arbitrage. The same query in pl, ro and ua returns three price distributions in three currencies. Traders live on that spread, and running it from one actor means the parsing is consistent across all three rather than three different scrapers' idea of a number.
Watching supply in a category. Schedule a query and count rows per run. posted_label shows how fresh the top of the results is, and diffing ad_id between runs tells you what appeared and what sold.
Finding dealers among private sellers. A seller posting many Nowe items in the same category is trading, not clearing out a garage. Group by location and condition to find them.
Lead lists. ad_id, title, location and url are enough to build a contact list for a category, with the ad page one fetch away.
OLX Scraper compared with the alternatives
| Option | What you get | Where it falls short |
|---|---|---|
| OLX partner API (developer portals per country) | Managing your own ads as a registered business partner | It is for posting and managing your listings, not for searching everyone else's. Access per country, per account |
| Your own script (the many GitHub OLX scrapers) | Full control | Each country has its own paths and price formats; scripts written for one market break on the next, and they stop working when the page layout changes |
| Scraping APIs and data vendors | Generic page fetching or bulk datasets | You still parse the page yourself, or you buy a dataset on someone else's schedule |
| Single-country OLX actors | One market, often with more fields for that market | A cross-border comparison means running several and reconciling their formats |
| This OLX Scraper | Search results from five OLX countries in one schema, prices as numbers, currency per row, pay per ad | Reads the results page only: no seller phone numbers or full descriptions |
Pricing
Two events. run_start costs $0.001 per run. listing costs $0.001 per ad written to the dataset, which is $1.00 per 1,000 ads. Only ads written to the dataset incur the listing event. Empty searches and unavailable pages incur no listing charge; the run-start event still applies. Free Apify users receive at most 10 ads per run. Paying users can request up to 1000 ads. All pricing is pay-per-event, with no per-compute-unit charges.
| Run | Charge |
|---|---|
Prefill (rower in Poland, 50 ads) | $0.001 + 50 × $0.001 = $0.051 |
| 1000 ads | $0.001 + 1000 × $0.001 = $1.001 |
| Search with no results | $0.001 |
Limits and gotchas
- The search path differs per country. If you paste a URL, paste it from that country's own site. A Polish path on the Romanian domain returns a 404 page that still renders the site chrome, so it looks like a working request with no results.
- Bulgaria prices in euro now.
currencyis read from the price text, so Bulgarian rows come back as EUR. If you have historical BGN data, the changeover is yours to reconcile. posted_labelis not a timestamp. OLX writes dates in the market's own language ("26 sierpnia 2026", "Обновено на 31 август 2026 г."), and it is returned verbatim rather than parsed through a month-name table per market that would silently be wrong somewhere.- Condition is optional for sellers. Coverage runs from about 97% in Romania down to roughly 23% in Bulgaria. The summary reports it per run so you know before you filter on it.
- A free ad is 0, a swap ad is null.
price_typedistinguishes them. Filter toasking pricebefore averaging anything. - Zero search results stay empty. Recommendations shown below an empty results grid are excluded. OLX can also broaden a query and return genuine suggested matches; check titles when an exact text match matters.
- Promoted ads repeat across pages. The walk deduplicates on
ad_id, so a deep run returns slightly fewer rows than pages times 50. That is correct, not loss.
FAQ
Which OLX countries are supported?
Poland, Romania, Bulgaria, Ukraine and Portugal, through the market field. OLX runs more domains than that; these five are the ones verified working.
Why is my Bulgarian result in euro? Because that is what OLX shows there now. The currency comes from the price on the page rather than a table in the code.
Why do some ads have no price?
Check price_type. Swap-only ads have no asking price at all, and a small number of ads state none. Free ads come back as 0 with price_type: free.
Can I paste a link from the OLX mobile site?
Yes. Addresses on m.olx.pl, m.olx.ro and the other mobile hosts of the five markets are accepted alongside olx.pl and www.olx.pl. OLX forwards them to the regular results page, and you get the same rows. Only the five supported OLX domains are fetched.
Can I filter by price?
Yes, price_min and price_max, in the market's own currency.
How many ads can I get in one run? Up to 1000, about 20 pages.
Does it return seller phone numbers? No. The results page does not carry them and this actor reads only the results page.
Is there an official OLX API for this? OLX runs partner APIs per country, but they are for businesses that post and manage their own ads. They do not offer a search across everyone's listings. OLX Scraper fills that gap from the public search pages.
Do I need an OLX account or login? No. The actor reads the same public result pages anyone can open in a browser.
Can I call OLX Scraper from my own code? Yes. Every Apify actor has an HTTP API and clients for Python and JavaScript. Start a run with your input and read the dataset back as JSON, or use the synchronous endpoint to get the rows in the same request.
Does it work for OLX Brazil or OLX India? Not at the moment. The five markets above are the ones this actor is built and verified for; other OLX countries are not covered.
Related Actors
- Marktplaats Scraper — the same job for the Dutch market
- DBA.dk Scraper — Danish classifieds
- Willhaben Scraper — Austrian classifieds across four verticals