Otodom.pl Scraper — Polish Real Estate Listings + GPS
Pricing
from $1.50 / 1,000 listings
Otodom.pl Scraper — Polish Real Estate Listings + GPS
Drop-in compatible with trev0n/otodom-scraper — cheaper, reliable, no residential proxy. Otodom.pl listings as JSON: price, price/m2, area, rooms, floor, market, full location + GPS, building type/year, heating, ownership, features, images, agency. Sale and rent; flats, houses, plots.
Pricing
from $1.50 / 1,000 listings
Rating
0.0
(0)
Developer
DIOPSIDE AI
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
a day ago
Last modified
Categories
Share
Drop-in compatible with trev0n/otodom-scraper — cheaper, reliable, and no residential proxy required. Same input field names, same output field names. Switching is a one-line change of the actor id.
Scrapes property listings from Otodom.pl, Poland's largest real estate portal: sale and rent, apartments, houses, plots, commercial units, warehouses and garages. Price, price/m², area, rooms, floor, market, full location hierarchy with GPS coordinates, building type/material/year, heating, ownership, condition, amenities, every photo, and the listing agency.
Why this one
The Otodom cluster's leading Actor fails about a third of its runs (33.4% of 1 220 runs in the last 30 days, Apify Store stats). Three things cause that class of failure on this site, and this Actor handles each one explicitly:
- Otodom canonicalises filters into the URL path. Ask for
market=SECONDARYor a single room count and Otodom answers HTTP 200 with a body containing__N_REDIRECT, not the results — the real listing lives at/wyniki/sprzedaz/mieszkanie,rynek-wtorny/.... Nothing at the HTTP layer notices. This Actor follows those redirects; a scraper that doesn't sees an empty result set and reports success. - Search cards carry a lossy slug. A seller who puts
!in the title gets a card slug with the punctuation stripped, while the canonical offer page spells itexclamation. Requesting the card's slug returns another silent 200 redirect. Unhandled, that means no coordinates and no description for every listing whose title contains punctuation — a systematic hole that never shows up as an error. We follow it: detail coverage is 12/12, not 11/12. - The Next.js
buildIdrotates on every Otodom deploy. A hardcoded one makes every data route return an empty body. We read the current one off the landing page at the start of each run, and re-read it if a request starts coming back empty mid-run.
On top of that:
- Filters are verified, not assumed. Otodom echoes the filters it actually applied in
filteringQueryParams. Every search response is checked against what was requested, and the run fails loudly rather than pushing unfiltered data. A CDN edge answering page 1 from cache is a real failure mode on sites like this, and the result — a dataset of the wrong properties — looks completely valid until you check the prices by hand. - No proxy at all, by default. Otodom's CloudFront/AWS WAF gate keys on the TLS fingerprint, not on IP reputation: plain
curl/httpxget a 403 from any IP, while a Chrome-fingerprinted client gets the real page from a direct connection. Measured over four runs each: direct 4/4 in ~3 s, Apify datacenter proxy 2/4 in ~40 s — Otodom has soured on much of that shared datacenter pool, so routing through it is slower and less reliable. The incumbent defaults to residential proxy in Poland; you pay for that bandwidth and get a worse result. We only escalate to residential if the direct connection is ever actually blocked. - Deep pagination actually works. 72 listings per request (Otodom's own maximum), verified to the last page of a 19 924-listing, 277-page Warsaw search.
maxItems: 0means everything. - Partial failures are reported, never hidden. A missing value is
null; a failed offer page costs that record its detail fields, not the run; and the run's status message names every target that returned nothing.
Input
Field names are trev0n/otodom-scraper's, so an input written for that Actor runs here unchanged.
| Field | Type | Default | Description |
|---|---|---|---|
startUrls | array | [] | Otodom search (/pl/wyniki/...) or offer (/pl/oferta/...) URLs. Filters in the URL are preserved. Overrides the three fields below. |
searchType | string | sprzedaz | sprzedaz (sale) or wynajem (rent) |
propertyType | string | mieszkanie | mieszkanie, dom, dzialka, lokal, haleimagazyny, garaz |
location | string | cala-polska | Path from an Otodom URL, e.g. mazowieckie/warszawa/warszawa/warszawa, malopolskie/krakow/krakow/krakow, mazowieckie, cala-polska |
priceMin / priceMax | integer | – | Price in PLN (monthly rent for rentals) |
areaMin / areaMax | integer | – | Floor area in m² |
roomsMin / roomsMax | integer | – | Room count range |
buildYear | integer | – | Minimum construction year |
market | string | ALL | ALL, PRIMARY (developer), SECONDARY (resale) |
postedDate | string | "" | 1, 3, 7, 14 — listings created in the last N days |
sortBy | string | DEFAULT | New. LATEST, PRICE_ASC/DESC, AREA_ASC/DESC, PRICE_PER_M_ASC/DESC |
maxItems | integer | 50 | Per start URL. 0 = every listing Otodom returns |
extractDetails | boolean | true | Visit each offer page for GPS, description, building facts, all images |
maxConcurrency | integer | 8 | Parallel requests |
maxRequestRetries | integer | 3 | Retries per request; each rotates to a fresh proxy session |
maxRequestsPerMinute | integer | 120 | Request rate ceiling |
proxyConfiguration | object | none | No proxy needed — see "Why this one" |
residentialFallback | boolean | true | New. Retry once on RESIDENTIAL/PL if the connection is ever blocked |
Example: apartments for sale in Warsaw, 2–3 rooms, 500–900k PLN
{"searchType": "sprzedaz","propertyType": "mieszkanie","location": "mazowieckie/warszawa/warszawa/warszawa","priceMin": 500000,"priceMax": 900000,"roomsMin": 2,"roomsMax": 3,"maxItems": 200,"extractDetails": true}
Example: poll for new Kraków rentals every morning
{"searchType": "wynajem","propertyType": "mieszkanie","location": "malopolskie/krakow/krakow/krakow","priceMax": 4000,"postedDate": "1","sortBy": "LATEST","maxItems": 0}
Example: whole-country sweep, listing data only
{"location": "cala-polska","maxItems": 0,"extractDetails": false,"maxConcurrency": 12}
Output
One record per listing.
{"id": "68439042","title": "2 pokoje na Bemowie","price": 750000,"priceCurrency": "PLN","priceFormatted": "750 000 PLN","pricePerM2": 14705.88,"rentPrice": 1000,"area": 51.0,"rooms": 2,"floor": "1","totalFloors": 4,"market": "SECONDARY","location": "Jelonki Południowe, Bemowo, Warszawa, mazowieckie","city": "Warszawa","province": "mazowieckie","district": "Bemowo","subdistrict": "Jelonki Południowe","street": "Rozłogi","latitude": 52.224556,"longitude": 20.900364,"description": "Na sprzedaż funkcjonalne 2-pokojowe mieszkanie...","buildingType": "block","buildingMaterial": "brick","buildYear": 1980,"heating": "urban","ownershipType": "full_ownership","condition": "ready_to_use","windowsType": "plastic","parkingType": "garage","elevator": "no","features": ["balcony", "usable_room"],"images": ["https://ireland.apollo.olxcdn.com/v1/files/.../image;s=1280x1024;q=80"],"imageCount": 5,"sellerType": "agency","agencyName": "Royalton Premium Real Estate","agencyUrl": "https://www.otodom.pl/pl/firmy/biura-nieruchomosci/royalton-premium-real-estate-ID11018171","dateCreated": "2026-09-20T00:50:29Z","dateModified": "2026-09-20T00:50:36Z","propertyUrl": "https://www.otodom.pl/pl/oferta/2-pokoje-na-bemowie-ID4Da7v","totalResults": 8505,"searchPage": 1,"position": 2,"hasDetails": true}
Fields the incumbent does not return: totalResults (how many listings matched, so you know what fraction you took), searchUrl / searchPage / position (reproduce any record), dateModified, pushedUpAt, deposit, freeFrom, status, referenceId, estate, transaction, advertType, terrainArea, developmentTitle/developmentUrl, shortDescription, tags, floorPlans, videoUrl, view3dUrl, isPromoted, isExclusiveOffer, isPrivateOwner, hidePrice, hasDetails, and characteristics — Otodom's raw attribute map, so anything it starts publishing reaches you before we ship a schema change.
Two deliberate differences from the incumbent
- Attribute values are Otodom's own enum keys (
block,brick,full_ownership,ready_to_use) rather than the Polish labels the website renders (blok,cegła). The keys are stable across Otodom's language switch and across UI copy changes; the labels are not. If you need the Polish label, map it once on your side. sellerPhoneis only ever an agency's or developer's business line. For a private seller, that number is an individual's personal mobile, and this Actor does not collect it —sellerPhone,agencyNameandagencyIdstaynullandsellerTypeisprivate. Everything published about a property itself is returned in full.
Switching from trev0n/otodom-scraper
Change the actor id. That's it — every input field name and output field name above is the same.
Then, optionally:
- Drop
proxyConfigurationfrom your input entirely. Residential bandwidth buys you nothing on this site, and the datacenter pool measured worse than no proxy at all. marketisnullin listing-only mode unless you set themarketfilter — Otodom genuinely does not state PRIMARY/SECONDARY on a search card, so we leave it null rather than guess. It is always populated whenextractDetailsis on.
Run sizes and cost
| Run | Requests | Data transferred | Measured |
|---|---|---|---|
| 20 listings, details on | 1 search + 20 offer pages | ~0.5 MB | 14 s |
| 50 listings, details on | 1 search + 50 offer pages | ~1 MB | ~30 s |
| 500 listings, details on | 7 searches + 500 offer pages | ~11 MB | ~5 min |
| 500 listings, details off | 7 searches | ~0.3 MB | ~20 s |
| 5 000 listings, details off | 70 searches | ~3 MB | ~3 min |
extractDetails: false is roughly 30x less traffic per listing. Turn it off for price monitoring and market sizing; leave it on when you need coordinates, descriptions or building attributes.
Use cases
- Market analysis — price and price/m² distributions by city, district and subdistrict, with
totalResultsgiving the true denominator. - Lead generation for agencies — new listings from private sellers (
isPrivateOwner: true,postedDate: "1"), which is where a listing agreement is still available. - Investment screening — filter on
pricePerM2,buildYear,marketandcondition, then map the results by GPS. - Rental yield modelling — pair a
sprzedazrun and awynajemrun over the same district and compare price per m² against rent per m². - Price-change monitoring — schedule daily with
sortBy: LATESTand diff onid+price+dateModified.
Notes
- Public listing data only. Otodom's
robots.txtAllows/pl/wyniki/and/pl/oferta/; this Actor uses the same JSON data routes the site's own pager calls and does not touch the/api/queryendpoint thatrobots.txtdisallows. - No login and no CAPTCHA solver.
- If the run produces nothing at all it fails with a status message naming the cause, rather than succeeding with an empty dataset.