Otodom Property Scraper (Poland)
Pricing
from $0.0007 / actor start
Otodom Property Scraper (Poland)
Scrapes apartments, houses, land and commercial property from Otodom — Poland's #1 real-estate portal, with 150,000+ apartments for sale. Filter by city, price, area and rooms; returns price, area, floor, full description, features, photos and agency. No pagination ceiling.
Pricing
from $0.0007 / actor start
Rating
0.0
(0)
Developer
Ibnu Adzim
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
14 hours ago
Last modified
Categories
Share
Scrapes apartments, houses, land and commercial property from Otodom — Poland's #1 real-estate portal (OLX Group), with over 150,000 apartments listed for sale nationwide.
Public data only. No login, no cookies, no browser.
What you get
Two record types share one dataset, told apart by recordType.
PROPERTY — one row per listing
Search rows carry price, price per m², area, rooms, floor, location, agency
and photos. With Fetch full property details on (the default), each row
also gets a propertyDetails object:
- the full description
featuresByCategory— every amenity grouped as Otodom groups them- floor plans and images
- exact location with the full administrative hierarchy
characteristics— building year, market (primary/secondary), ownership form, heating, condition- agency, owner and developer details
- additional costs (rent, utilities) where the advertiser supplied them
SEARCH_SUMMARY — one row per location searched
Upstream's match total and page count, how deep the run paged, the filters you requested, and Otodom's own echo of the filters it actually parsed.
Input
| Field | What it does |
|---|---|
| For sale / for rent | sprzedaz or wynajem |
| Property type | apartment, house, land, commercial premises, halls & warehouses, garage, room, new development |
| Locations | one search per entry: cala-polska, krakow, or a full path like mazowieckie/warszawa/warszawa/warszawa |
| Price / area | min–max ranges (PLN, m²) |
| Number of rooms | multi-select, unioned |
| Sort by | newest, price, area, price per m² — with direction |
| Offer URLs | scrape specific listings directly, skipping search |
| Results per request | 24 / 36 / 48 / 72 (default — fewest requests) |
| Max properties per location | 0 = unlimited, and here that really means everything |
| Fetch full property details | off = fast listing-only crawl |
Example
{"transaction": "sprzedaz","estate": "mieszkanie","locations": ["malopolskie/krakow/krakow/krakow"],"minPrice": 400000,"maxPrice": 900000,"rooms": ["TWO", "THREE"],"sortField": "LATEST","maxItems": 500,"includePropertyDetails": true}
What makes this one different
No pagination ceiling. Most property portals cap how deep you can page —
Immoweb stops at 9,990 rows, OnTheMarket at 1,020. Otodom does not: page 4,191
of 4,191 really does serve its final rows. maxItems: 0 genuinely means the
whole result set, so set a bound unless you mean it.
It reports what Otodom actually understood. Otodom silently ignores
filters it does not parse — roomsNumber=THREE is discarded (the right syntax
is [THREE]), and an unknown room value just widens your search without
saying so. Every run puts Otodom's own parse on the summary row:
"filtersRequested": {"priceMin": 400000, "priceMax": 900000, "roomsNumber": "[TWO,THREE]"},"upstreamFilterEcho": {"priceMin": 400000, "priceMax": 900000,"roomsNumber": ["TWO", "THREE"],"locations": [{"name": "Kraków", "fullName": "Kraków, małopolskie"}]}
Property types, room values, sort fields and page sizes are all validated before the first request. That matters because an unrecognised property type is not an error on Otodom — it answers HTTP 200 with all 150,000 apartments, which would look like a successful run.
Notes on reliability
- DataDome tag present, no challenge served. Otodom loads DataDome, but every cold request returned full data. The actor watches for genuine challenge markers and rotates IP + fingerprint if one ever appears.
- Residential proxy by default on cloud runs — the likeliest place for a challenge to show up.
- Runs unattended. No human-captured session, no expiring token.
- Unknown locations fail cleanly as
not_found. - Failures never vanish. An offer withdrawn mid-crawl degrades that one
row (
_detailError); every input maps to at least one output row.
Output envelope
Every record carries _input, _source and _scrapedAt. Upstream field names
pass through verbatim — no renaming. _source is S1-nextdata-search for
listing-only rows and S1-nextdata-search+S2-nextdata-ad once details are
attached.
Note location on a property row is upstream's own object; the location you
searched is locationQueried.
See CRAWLING_METHOD.md for the full reverse-engineering
trail, including the obiekt baseline-echo trap and the DataDome marker that
would have false-positived on every healthy page.