OLX Listings Scraper
Pricing
from $1.75 / 1,000 results
OLX Listings Scraper
Search live OLX Group marketplace listings across 7 countries (Poland, Ukraine, Kazakhstan, Uzbekistan, Romania, Bulgaria, Portugal) — price, condition, seller, photos and location, routed to whichever surface each country's robots.txt actually allows.
Pricing
from $1.75 / 1,000 results
Rating
0.0
(0)
Developer
Ibnu Adzim
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
6 days ago
Last modified
Categories
Share
OLX Listings Scraper (PL, UA, KZ, UZ)
Classified listings from OLX — Poland, Ukraine, Kazakhstan and Uzbekistan — as structured rows: price with its currency, the full category branch, city and coordinates, seller, photos, and the category-specific attributes OLX attaches to every ad.
The thing that makes this different
About a quarter of every OLX result page is advertising, and OLX tells you which rows — but only as positions.
Ask for 40 listings and 52 come back. The extra twelve are paid placements,
injected on top of the page size you requested. OLX publishes them in
metadata.promoted as indexes into the results array —
[0, 1, 2, 13, 14, 15, 26, ...]That has two consequences most scrapers miss:
- Slicing
data[:limit]drops real listings. The overflow is not at the end; the ads are interleaved from position 0. - Those indexes stop being true the moment anything reorders the list. Dedupe, sort or filter first and the labels land on the wrong offers.
This Actor resolves the positions to offer ids on the raw page, before
anything else touches it. Every row carries isPromoted, and each search
reports promotedReturned and promotedShare — measured at 0.231 on a
typical page. Set includePromoted: false to drop the ads; the count is
still reported, so removing them does not also hide that they were there.
And the total you are shown is not the total you can have. OLX reports
total_elements: 1000 on every query however large the result set — that is
the pageable cap, not a count — while visible_total_count says 159,397
matched. Both are emitted, named for what they are: pageableOffers and
matchingOffers. The second one is a live estimate and visibly moves between
identical calls, so it is flagged as one.
Countries, and why only four
OLX runs one platform behind a dozen national domains, and their robots.txt
files do not agree. Poland opts the search endpoint in by name
(Allow: /api/v1/offers/); Romania, Bulgaria and Portugal carry
Disallow: /api/ with no such exception; Brazil, Bosnia and India answer
robots.txt itself with a challenge page.
Only the four that permit it are offered here. Asking for any other country is refused at input validation, with the reason. The full per-domain table is in CRAWLING_METHOD.md.
What you get
One SEARCH_SUMMARY row per keyword, then one OFFER row per listing.
| Group | Fields |
|---|---|
| Identity | offerId, title, url, description, status, offerType |
| Price | price, priceCurrency, priceLabel, priceNegotiable, priceArranged, priceIsFree, previousPrice |
| Placement | isPromoted, isHighlighted, isTopAd, promotionOptions, resultPosition |
| Category | categoryId, categoryName, categoryType, categoryPath (full branch) |
| Location | cityName, regionName, districtName, latitude, longitude, geoPrecisionRadius |
| Seller | sellerId, sellerName, sellerIsBusiness, sellerSince, sellerCompanyName, shopSubdomain |
| Timing | createdTime, lastRefreshTime, pushupTime, validToTime |
| Attributes | attributeCount, attributes (key / name / value, category-specific) |
| Media | photoCount, photos |
| Provenance | _input, _source, _scrapedAt, country, sourceHost |
categoryPath walks OLX's own tree to the root, so a phone comes back as
["Elektronika", "Telefony", "Smartfony i telefony komórkowe", "iPhone"]
rather than a bare id.
attributes are category-specific and cannot be fixed columns — a phone has
state, phonemodel, builtinmemory_phones; a flat has floor area and room
count. They are kept as a counted list so that a consumer flattening to CSV
at least sees what it dropped.
Input
| Field | Notes |
|---|---|
country | pl, ua, kz or uz. Sets the host and the currency. |
queries | Keywords, one search each. An empty string lists a whole category. |
category | Numeric id or OLX's own path (nieruchomosci). Checked against the published tree before any request. |
regionId | Numeric. Read it off a row from an unfiltered run — see the warning below. |
sortBy | relevance, newest, priceAsc, priceDesc. |
priceFrom / priceTo | In the country's own currency. |
includePromoted | Keep the paid placements (default) or drop them. |
maxPages, pageSize, maxItems | pageSize maxes at 50; OLX refuses 100. |
A note on regionId. An unknown category id is refused with HTTP 400, but
an unknown region id comes back as HTTP 200 with zero rows —
indistinguishable from a search that genuinely matched nothing. There is no
published region list to validate against, so take the id from a real row
rather than guessing at one.
A note on sortBy. OLX refuses the plausible value relevance with
HTTP 400 and silently ignores outright nonsense like banana:desc. Its own
error is therefore not a validity test, so sort values are checked here
before the request.
Limits, stated plainly
- ~1,000 organic listings per search, whatever the reported match count.
OLX stops paging at offset 1040. To go deeper, narrow the search — by
category, region or price — rather than raising
maxPages. - No anti-bot layer was found. The endpoint answers cold on six TLS profiles with no cookie or warm-up, so a proxy is optional and off by default.
- Every run reports why it stopped (
stoppedReason:no_new_ids,offset_ceiling,max_pagesormax_items), so a short run is never ambiguous.
Method, measurements and the full trap list: CRAWLING_METHOD.md.