Otodom.pl Scraper icon

Otodom.pl Scraper

Scrape Otodom.pl properties for sale and rent, including apartments, houses, plots, commercial spaces, garages, rooms and new developments. Build…

Start using this actor on Apify ↗Need a custom scraper?Contact us ↗

Otodom.pl Scraper at a glance

Otodom.pl Scraper is a ready-to-run Apify actor from abotapi for collecting real estate data. Its documented output includes id, url, title, transaction and type, which you can export as JSON, CSV or Excel or read through the Apify API. It costs $1.50 per 1,000 results on the Apify Free plan and from $0.80 on paid plans, with smaller per-run or optional feature charges listed on Apify.

Runs on
Apify cloud: console, API, schedules and integrations
Data type
Real Estate, Automation, Developer Tools
Region
Europe
Pricing
$1.50 per 1,000 results (Free plan), from $0.80 on paid plans
Output
Documented below · JSON, CSV, Excel
Inputs
28 options
Run success
96.7% of completed public runs in the last 30 days

Scrape Otodom.pl properties for sale and rent, including apartments, houses, plots, commercial spaces, garages, rooms and new developments. Build searches by city, transaction, property type and filters, or paste Otodom URLs directly.

What Otodom.pl Scraper can do

  • Two ways to search. Build a query from transaction, property type, cities and filters, or paste any Otodom search URL refined in your browser. No need to construct search URLs by hand.
  • 80 cities plus all 16 voivodeships recognized by name. Warszawa, Kraków, Wrocław, Gdańsk, Łódź, Poznań, Szczecin, Sopot and more, built in. For a city or district that is not recognized, drop a URL into URL mode instead.
  • 30+ fields per listing. SERP fields (price, area, rooms, agency, photo) plus optional detail-page enrichment (city and province, GPS coordinates, full description, agent phone, building year, ownership, full feature list).
  • Detail depth on demand. Switch fetchDetails on or off per run: SERP-only fields are always included, detail-page fields are added when you need them.
  • Forward pagination with cost caps. Walks every result page per search by default, with per-run and per-search limits so a multi-city search returns balanced results instead of exhausting the budget on the first city.
  • Resume across runs. Continue an interrupted full-catalogue walk from a previous run or dataset ID without re-scraping listings you already collected.

Choose what to collect

These inputs let you configure Otodom.pl Scraper - Property Listings, Prices & Rentals. Open the actor on Apify to enter your values and review the current options.

Supported input fields
InputWhat it controls
modestring · requiredSearch mode = pick transaction + property type + cities + filters below. URL mode = paste any Otodom search URL refined in a browser (recommended for filters not exposed below, like district/neighborhood).

Choices: search, url

transactionstringSale (sprzedaż) or rent (wynajem). Used to build the canonical Otodom URL.

Choices: sprzedaz, wynajem

estatestringOtodom property type segment. Apartment (mieszkanie), house (dom), plot (działka), commercial (lokal), garage (garaż), investment / new development (inwestycja), room (pokój), halls and warehouses (hale i magazyny).

Choices: mieszkanie, dom, dzialka, lokal, garaz, inwestycja, pokoj, haleimagazyny

locationsarrayPolish city names. Recognized: Warszawa, Kraków, Wrocław, Gdańsk, Łódź, Poznań, Szczecin, Bydgoszcz, Lublin, Białystok, Katowice, Gdynia, Sopot, Gliwice, Zabrze, Rzeszów, Olsztyn, Toruń, Kielce, Radom, Częstochowa, Bytom, Tychy, Opole, Sosnowiec, plus all 16 voivodeship names. For unknown / smaller cities, leave empty (Poland-wide search) or use URL mode. The cartesian product of property type × city is searched.
marketTypestringFilter by primary (rynek pierwotny, new from developer) or secondary (rynek wtórny, resale). Default = both.

Choices: all, primary, secondary

minPriceintegerLower bound on totalPrice (PLN). Optional.
maxPriceintegerUpper bound on totalPrice (PLN). Optional.
Show 21 more inputs
More input fields
InputWhat it controls
minAreaintegerLower bound on areaInSquareMeters. Optional.
maxAreaintegerUpper bound on areaInSquareMeters. Optional.
minRoomsintegerMinimum number of rooms (1-10).
maxRoomsintegerMaximum number of rooms (1-10).
buildYearintegerOnly listings built in this year or later (1900-2030). Search mode only; listings without a build year on Otodom are excluded by the site.
postedDatestringOnly listings posted within the last 1, 3, 7 or 14 days. Empty = any time. Search mode only.

Choices: , 1, 3, 7, 14

districtsarrayCity districts to search, e.g. Mokotów, Wola (one search per district; Search mode only). Applies to city locations such as Warszawa or Kraków, ignored for voivodeships. Use the district name as written on Otodom.
sortBystringDefault = newest. Otodom inserts promoted listings ahead of organic order regardless of sort, so sort is best-effort.

Choices: DEFAULT, LATEST, PRICE_ASC, PRICE_DESC, AREA_ASC, AREA_DESC

urlsarrayOne or more full otodom.pl URLs: a search (city, district, filters, sort?page=N), a single listing page (/pl/oferta/...), or an agency profile (/pl/firmy/biura-nieruchomosci/-ID...). Tip: open otodom.pl in your browser, copy the URL from the address bar. Leaving this field untouched scrapes the eight sample URLs listed here. Unsupported pages (new-development detail pages, agency directory lists, m.otodom.pl, /en/ and old-style addresses) are skipped with an error in the log. Search-mode-only fields above (transaction, property type, cities, market type, sort order) are ignored; the price/area/rooms filters above still apply to listings found via search and agency URLs. Pagination starts at the page in the URL and walks forward.
maxPagesintegerHow many SERP pages to walk per city × property type combination (or per pasted URL). Each page returns ~36 listings. Leave empty to walk every result page.
maxListingsintegerStop after this many listings across all searches. 0 = no cap. Values above 1,000,000 are rejected.
fetchDetailsbooleanWhen ON, also fetch each listing's detail page for GPS coordinates, full description, agent phone, building year, ownership type, full feature list. SERP cards already carry id, title, price, area, rooms, agency and the cover image. City, province, latitude/longitude, description and contact details come from the detail page, so they are empty when this is off. Default ON. Each delivered listing whose detail page is read is charged as one detail-page event on top of the per-result charge; listings whose detail page is refused, and listings incremental mode suppresses as unchanged, are not charged.
resumeFromRunIdstringPaste a previous run ID or dataset ID to continue a full-catalogue walk across separate runs. Listings already collected there (matched by id) are skipped, so this run only saves the new ones. Leave empty for a normal fresh run. For ongoing scheduled monitoring of this search, use incrementalMode below instead.
incrementalModebooleanTurn on when you run this same search on a schedule (e.g. daily) and only want to know what's new or changed since last time. The actor remembers this search's listings itself, no run ID needed. Every field of a listing is compared (price, area, rooms, title, description, address, contact and agency data, features, image count and the rest) except scrapedAt, images, imageUrl, isPromoted and any field you list in the ignore setting at the end of this section; ignored fields stay in the output but never trigger UPDATED. dateModified is the site's own modified-at stamp (it moves when an ad is edited or refreshed); list it to ignore refresh-only changes. Adds changeType (NEW/UPDATED/UNCHANGED/REAPPEARED/EXPIRED), changedFields, firstSeenAt, and lastSeenAt to every row. Off by default so existing scheduled runs are unaffected. See stateKey, emitUnchanged, and emitExpired below.
stateKeystringOptional label for the incrementalMode baseline. Leave empty to auto-derive one from your search settings (mode/transaction/estate/locations/urls/marketType/price/area/rooms ranges/sort), a different filter setup automatically gets its own baseline, never sharing one by accident. Set this only if you want multiple differently-configured runs to intentionally share one baseline.
emitUnchangedbooleanWhen incrementalMode is on, also output listings that are UNCHANGED since the last run (changeType=UNCHANGED, changedFields=[]). Off by default: unchanged listings are silently skipped, so you only see and pay for what's new or different. Turning this on bills extra dataset-item rows for listings that haven't changed.
emitExpiredbooleanWhen incrementalMode is on, also output a row for previously-tracked listings that are no longer found (changeType=EXPIRED), so you can detect delistings. Only emitted after a run that scans the full tracked search with no page/item cap and no resumeFromRunId, a partial run never marks a still-live listing as expired. Off by default. Turning this on bills extra dataset-item rows.
ignoreFieldsForChangesarrayIncremental mode only. Field names to leave out when deciding whether a listing changed (they still appear in the output). Use a dotted path for a nested field, for example agency.name. Fields excluded by default: scrapedAt, images, imageUrl, isPromoted. Example: dateModified, agency.name. Leave empty to compare every other field.
mcpConnectorsarrayOptionally send the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. Authorize a connector once under Apify → Settings → Integrations, then select it here. The connector receives a condensed, human-readable summary per item (title + key fields), not the full JSON, the complete record stays in the dataset. Leave empty to skip. Supported: Notion (https://mcp.notion.com/mcp), Linear (https://mcp.linear.app/sse), Airtable (https://mcp.airtable.com/mcp), Apify (https://mcp.apify.com).
notionParentPageUrlstringURL (or id) of the Notion page under which item pages are created. Required to enable the Notion export; ignored by other connectors.
maxNotifyListingsintegerCap on items written to each connector per run. Does not affect the dataset.

Fields in the output

The documented dataset includes the fields below. Availability can depend on the source page and selected mode.

idID
urlURL (link)
titleTitle
transactionTransaction
estateType
priceTotalPrice
priceCurrencyCurrency
pricePerSquareMeterPrice / m²
rentPriceRent
areaInSquareMetersArea (m²)
roomsRooms
floorFloor
Show 27 more fields
buildYearBuild year
marketTypeMarket
ownershipOwnership
buildingTypeBuilding
cityCity
provinceProvince
districtDistrict
streetStreet
latitudeLat
longitudeLng
agencyNameAgency
advertiserTypeLister
phonePhone
isPromotedPromoted
isExclusiveExclusive
imageCountImages
imageUrlPhoto (image)
scrapedAtScraped
changeTypeChange (text)
detailFetcheddetailFetched
descriptionDescription
conditionCondition
extrasExtras
dateModifiedModified
changedFieldsChanged fields
firstSeenAtFirst seen
lastSeenAtLast seen

Example output

An example from this actor’s documentation. Values are illustrative; this is not a fresh live result.

{
  "id": "00000001",
  "url": "https://www.otodom.pl/pl/oferta/sample-listing-ID00000",
  "title": "Przytulne mieszkanie na Mokotowie, 2 pokoje",
  "transaction": "sprzedaz",
  "estate": "mieszkanie",
  "priceTotal": 750000,
  "priceCurrency": "PLN",
  "pricePerSquareMeter": 15000,
  "rentPrice": null,
  "rentAdditional": null,
  "deposit": null,
  "areaInSquareMeters": 50,
  "rooms": 2,
  "floor": "3",
  "buildingFloors": 5,
  "buildYear": 2010,
  "marketType": "secondary",
  "ownership": "full_ownership",
  "buildingType": "block",
  "buildingMaterial": "concrete",
  "heating": "urban",
  "condition": "ready_to_use",
  "extras": [
    "balcony",
    "garden"
  ],
  "dateModified": "2026-10-14T21:48:21Z",
  "windowsType": "plastic",
  "city": "Warszawa",
  "province": "mazowieckie",
  "district": "Mokotów",
  "street": "ul. Sample",
  "postalCode": "00-000",
  "latitude": 52,
  "longitude": 21,
  "agencyName": "Sample Agency",
  "advertiserType": "agency",
  "contactName": "Agent Name",
  "phone": "+48000000000",
  "referenceId": "REF-0001",
  "description": "Sprzedam przytulne mieszkanie w spokojnej okolicy, blisko metra i parku.",
  "features": [
    "meble",
    "balkon",
    "winda"
  ],
  "isPromoted": false,
  "isExclusive": false,
  "isPrivateOwner": false,
  "imageCount": 12,
  "imageUrl": "https://img.otodom.pl/api/v1/offers/sample-id/image",
  "dateCreated": "2026-01-01T00:00:00.000Z",
  "investmentEstimatedDelivery": null,
  "scrapedAt": "2026-04-28T00:00:00.000Z"
}

Ways to use this data

  • Real estate market research: track asking prices, price per square meter and inventory across cities, districts and property types over time.
  • Property investment analysis: pull GPS-tagged listings with build year, ownership type and agent contact to compare deals across multiple cities.
  • Lead generation for agents and brokers: filter by market type and advertiser type to find private-seller and secondary-market listings worth a call.
  • Rental market tracking: compare rent, deposit and additional fees across cities, room counts and property types.
  • Proptech and data products: feed structured Polish listings into a search app, price index or valuation model.

Before you run

Start with a small input and check the resulting records against your expected fields. Source content and actor options can change; the current Apify listing is the reference for availability and pricing.

Questions about this actor

What data does Otodom.pl Scraper return?

The documented output for Otodom.pl Scraper includes id, url, title, transaction, type, price, currency and price / m². Availability of each field depends on the source page and the selected mode.

How much does Otodom.pl Scraper cost?

It costs $1.50 per 1,000 results on the Apify Free plan and from $0.80 on paid plans, with smaller per-run or optional feature charges listed on Apify. The Apify listing shows the current rate for every plan.

Can I get only new or changed listings on a schedule?

Yes. Schedule the actor from the Schedules tab and turn on Incremental mode. Each run then returns only new, updated, reappeared, and (if you also turn on Emit expired listings) delisted records for that same search; unchanged ones are not returned or billed unless you turn that on too.

Why did my city search return listings from all over Poland?

locations recognizes 80 built-in cities and the 16 voivodeships by name. An unrecognized or misspelled name is skipped with an error in the log (if every entry is unrecognized the run fails with an input error). Check the spelling, or switch to URL mode and paste a search URL for that city or district.

Can I use it with AI agents or MCP?

Yes. Call it from any Apify integration or MCP client, and use the connector field to push results into Notion, Linear or Airtable.

Ready to collect with Otodom.pl Scraper?

Run it on Apify with your own input and export the results as JSON, CSV or Excel.