Otodom Scraper & New-Listing Monitor - Poland Real Estate
Pricing
from $1.46 / 1,000 property returneds
Otodom Scraper & New-Listing Monitor - Poland Real Estate
Turn monitoring on and every later run returns only the listings it has not returned before, so you are never charged for the same property twice. Every Otodom.pl listing as clean JSON: price with its meaning, price per m2, rooms, area, floor, city and district, coordinates, agency. Unofficial.
Pricing
from $1.46 / 1,000 property returneds
Rating
0.0
(0)
Developer
NeverEmpty
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
20 hours ago
Last modified
Categories
Share
Otodom Scraper - Real Estate Data for Poland, Price & Area
Pull property listings from Otodom.pl — Poland's largest property portal — as clean JSON: the price as a number, what that number means, the area in m², rooms, floor, the city and district, the features and the agency.
Unofficial. This Actor is not affiliated with, endorsed by, or sponsored by Otodom. All product names are trademarks of their respective owners.
No API key. Reads the same public pages a visitor sees.
Run it tomorrow and you only get what is new
Schedule this Actor with monitoringMode: true and each run returns only the listings it has not returned before. Not "here is everything again, please de-duplicate it yourself" — the Actor remembers what it already gave you, so a daily feed costs you the handful of new homes, not the whole board every morning. You are never charged for the same property twice.
Listings your filters removed are remembered too, so widening a filter later does not resurrect them as false "new" listings. Listings that did not fit under your limit are not remembered, so they are still waiting on the next run instead of being silently lost.
Why this one is different
The monthly service charge is not sold to you as rent
This is the trap in Otodom's data. Its listing objects carry a field literally called rentPrice — and 62 of the 104 sale listings measured have one. It is not a rent. It is the monthly administrative charge (czynsz): median 731 PLN, against a sale price whose median is 586,200 PLN.
Passing it through as "rent" would put a 731 next to a 586,200 in the same column, so this Actor splits them:
price+priceType—salefor a total,monthly-rentfor a rent, taken from the listing's own transaction typemonthlyAdminCharge— the czynsz, in its own column, on sale and rent listings alikepricePerSqm— Otodom's own figure, on the listings that publish it
A property whose price is hidden comes back with price: null and isPriceHidden: true — never a zero.
Rooms and floors are words, and they are only turned into numbers where that is safe
Otodom writes roomsNumber: "FOUR" and floorNumber: "THIRD". Both come back twice: the raw word (roomsRaw, floorRaw) and a number (rooms, floor).
ABOVE_TENTH is left as a word with floor: null — "above the tenth" is not a floor number, and inventing 11 would be a guess. Any value not seen in the sample is treated the same way: the word is kept, the number stays null.
No agent names, no phone numbers, no private sellers' names
Otodom's page data carries the assigned agent's first name, last name and mobile number on 33 of the 141 listings measured, and 31 listings are posted by private individuals under their own names.
This Actor returns the agency name only. Agent names, agent mobiles and private sellers' names are not collected, and email addresses and Polish phone numbers are removed from the description text as well. A private listing comes back as advertiserType: "private" with no name attached. Property data, not a contact list.
One request, 30 to 37 complete properties
Otodom ships the full listing — price, area, rooms, floor, location, features, agency — inside the search page itself. This Actor reads all of them from that single page and never opens individual property pages.
Promoted listings repeat across pages; those repeats are dropped before delivery, so you are never charged twice for the same property (4 of the 141 read were repeats).
Filters run here, not on Otodom
Every filter — keyword, price, price per m², rooms, floor, area, agency, advertiser type, date — is applied to rows that were already fetched. Nothing extra is requested from Otodom and nothing extra is charged. A property is never dropped for a value Otodom did not publish: an unknown price is not a low one, an unreadable date is not an old one.
An empty search is an answer, not a silent fallback
A search Otodom has nothing for returns one no-results row saying so, and it is not charged. A page whose embedded data is missing — or which states a total above zero while yielding no properties — is reported as unreadable, so a redesign on Otodom's side shows up as an error rather than as a quiet, permanent "there is nothing for sale". Filters that removed everything are a third kind of row (no-filter-match) which states how many properties were actually read.
Input
Either paste a search URL, or build one from three fields.
| Field | Type | Default | Description |
|---|---|---|---|
searchUrl | string | — | e.g. https://www.otodom.pl/pl/wyniki/sprzedaz/dom/mazowieckie. Query parameters are stripped |
transaction | string | sprzedaz | sprzedaz (for sale) or wynajem (to rent) |
estate | string | mieszkanie | mieszkanie, dom, dzialka, lokal, haleimagazyny, garaz, pokoj |
location | string | cala-polska | The whole country, a region (mazowieckie), or a city as its full path (mazowieckie/warszawa/warszawa/warszawa) |
maxListings | integer | 20 | 1-1000. One property = one row = one charged event |
maxPages | integer | 0 | Hard cap on requests; 0 = decide from maxListings |
includeDescription | boolean | true | The teaser text. The keyword filter searches it either way |
useProxy | boolean | false | Off by default — Otodom answers Apify's network directly. Switches on by itself if blocked |
keywords / keywordMatch / excludeKeywords | array / string / array | — | Match on title, description, location, features and agency |
agencyNames | array | — | Keep only these agencies (partial match) |
minPrice / maxPrice | integer | 0 | PLN. A property with no published price is kept |
maxPricePerSqm | integer | 0 | PLN per m² |
requirePrice | boolean | false | Drop properties whose price is hidden |
minRooms / maxRooms | integer | -1 | -1 = off |
minFloor / maxFloor | integer | -1 | 0 is the ground floor. -1 = off |
minAreaSqm / maxAreaSqm | integer | 0 | 0 = off |
advertiserType | string | any | agency or private |
exclusiveOnly | boolean | false | Listings an agency holds exclusively |
listedWithinDays | integer | 0 | 0 = no limit. An unreadable date is kept |
monitoringMode | boolean | false | Return only properties not returned on a previous run |
resetMonitoringState | boolean | false | One-shot: forget what was already returned |
{ "transaction": "sprzedaz", "estate": "mieszkanie", "location": "warszawa", "maxListings": 100,"minRooms": 2, "maxPrice": 900000, "maxPricePerSqm": 16000, "advertiserType": "private" }
Monitoring mode turns this into a daily feed
Schedule it and set monitoringMode: true: each run returns only the properties it has not returned before. Properties removed by your filters are remembered too, so changing a filter later does not resurrect them as false "new" listings — and properties that simply did not fit under maxListings are not remembered, so they are still waiting for you on the next run rather than being silently lost.
Measured on 2026-09-04: 149,738 flats for sale across Poland.
Output
One row per property:
{"source": "otodom.pl","status": "ok","listingId": "68004190","title": "Balkon na lato. Wakacje każdego dnia.","url": "https://www.otodom.pl/pl/oferta/balkon-na-lato-wakacje-kazdego-dnia-ID4BkZM","transactionRaw": "SELL","transactionType": "sale","estateType": "FLAT","price": 675318,"priceCurrency": "PLN","priceType": "sale","isPriceHidden": false,"monthlyAdminCharge": null,"monthlyAdminChargeCurrency": null,"pricePerSqm": 9800,"pricePerSqmCurrency": "PLN","areaSqm": 68.91,"roomsRaw": "FOUR","rooms": 4,"floorRaw": "THIRD","floor": 3,"city": "Bielsko-Biała","province": "śląskie","street": "ul. Muszlowa","locationFullName": "Stare Bielsko, Bielsko-Biała, śląskie","features": ["AIR_CONDITIONING", "TERRACE", "STORAGE_ROOM", "TOP_FLOOR", "PARKING_SPOT", "SECURE_BUILDING"],"advertiserType": "agency","agencyName": "Eko Develop","agencyUrl": "https://www.otodom.pl/pl/firma/eko-develop-ID433594","isExclusiveOffer": false,"isPromoted": false,"imagesCount": 15,"listedAt": "2026-05-14T12:30:32Z","updatedAt": "2026-06-21T19:34:16+02:00","shortDescription": "…","scrapedAt": "2026-09-04T22:40:05.658Z"}
Measured across 141 live listings on 2026-09-04: title, url, transaction type, property type, city, province and the first-listed date on 141 of 141; area on 137; rooms on 137; floor on 133; a published price on 129.
Rows that are never charged
status | when |
|---|---|
no-results | Otodom has no listings for this search. That is its answer, not a failure |
no-such-search | Otodom has no page at that address - a mistyped location or property type. Not a claim that there is nothing there: warszawa on its own is a 404, while its full path holds 7,113 flats to rent |
no-filter-match | Listings were read, but your filters removed all of them. The row says how many were read |
no-new-listings | Monitoring mode: nothing new since the previous run |
unreadable | The page could not be read, its embedded data was missing, or it claimed a total above zero while yielding nothing |
Pricing
Pay per property returned. Rows that report an empty search, a filter that matched nothing, or a page that could not be read are not charged. A property that appears twice across pages is dropped before delivery, so it is never charged twice.
Notes
robots.txtwas read in full on 2026-09-04. TheUser-agent: *section disallows/ajax/,/api/query(except?crawl=true), advertising-slot paths,/uk/*and/*?*map=1. This Actor requests only the public search paths plus?page=N.- Only public pages are read. No login, no API key.