OLX Listings Scraper avatar

OLX Listings Scraper

Pricing

from $1.75 / 1,000 results

Go to Apify Store
OLX Listings Scraper

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

Ibnu Adzim

Maintained by Community

Actor 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, ...]
— not as ids or flags on the rows themselves.

That has two consequences most scrapers miss:

  1. Slicing data[:limit] drops real listings. The overflow is not at the end; the ads are interleaved from position 0.
  2. 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.

GroupFields
IdentityofferId, title, url, description, status, offerType
Priceprice, priceCurrency, priceLabel, priceNegotiable, priceArranged, priceIsFree, previousPrice
PlacementisPromoted, isHighlighted, isTopAd, promotionOptions, resultPosition
CategorycategoryId, categoryName, categoryType, categoryPath (full branch)
LocationcityName, regionName, districtName, latitude, longitude, geoPrecisionRadius
SellersellerId, sellerName, sellerIsBusiness, sellerSince, sellerCompanyName, shopSubdomain
TimingcreatedTime, lastRefreshTime, pushupTime, validToTime
AttributesattributeCount, attributes (key / name / value, category-specific)
MediaphotoCount, 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

FieldNotes
countrypl, ua, kz or uz. Sets the host and the currency.
queriesKeywords, one search each. An empty string lists a whole category.
categoryNumeric id or OLX's own path (nieruchomosci). Checked against the published tree before any request.
regionIdNumeric. Read it off a row from an unfiltered run — see the warning below.
sortByrelevance, newest, priceAsc, priceDesc.
priceFrom / priceToIn the country's own currency.
includePromotedKeep the paid placements (default) or drop them.
maxPages, pageSize, maxItemspageSize 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_pages or max_items), so a short run is never ambiguous.

Method, measurements and the full trap list: CRAWLING_METHOD.md.