Otodom.pl Scraper - Property Listings, Prices & Rentals avatar

Otodom.pl Scraper - Property Listings, Prices & Rentals

Pricing

from $0.80 / 1,000 results

Go to Apify Store
Otodom.pl Scraper - Property Listings, Prices & Rentals

Otodom.pl Scraper - Property Listings, Prices & Rentals

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.

Pricing

from $0.80 / 1,000 results

Rating

5.0

(1)

Developer

Abot API

Abot API

Maintained by Community

Actor stats

2

Bookmarked

32

Total users

5

Monthly active users

a day ago

Last modified

Share

Otodom Scraper: Polish Property Listings, Prices, GPS & Agent Contacts

Otodom Scraper turns Otodom into a clean data feed of Polish property listings. Get price, area, rooms, full address, GPS coordinates, description, agent contact, building year, ownership, market type and the full feature list for apartments, houses, plots, commercial units, garages, new developments and rooms, for sale or for rent. Build a search from transaction, property type, city and filters, or paste any Otodom search URL, then export to JSON, CSV or Excel, or pull the results straight into your app through the API.

Why This Scraper?

  • 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.
  • Incremental monitoring for scheduled runs. Turn on incremental mode for a recurring search and every row is flagged NEW, UPDATED, UNCHANGED, REAPPEARED or EXPIRED against what you collected last time.

Use Cases

  • 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.

Data You Get

Sample shape, values are illustrative placeholders, not from a live listing.

FieldExample
id"00000001"
url"https://www.otodom.pl/pl/oferta/sample-listing-ID00000"
title"Przytulne mieszkanie na Mokotowie, 2 pokoje"
transaction"sprzedaz"
estate"mieszkanie"
priceTotal / priceCurrency750000 / "PLN"
pricePerSquareMeter15000
areaInSquareMeters50.0
rooms2
floor / buildingFloors"3" / 5
buildYear2010
marketType"secondary"
ownership"full_ownership"
buildingType"block"
city / province / district"Warszawa" / "mazowieckie" / "Mokotów"
latitude / longitude52.0000 / 21.0000
agencyName / advertiserType"Sample Agency" / "agency"
phone"+48000000000"
features["meble", "balkon", "winda"]
imageUrl"https://img.otodom.pl/api/v1/offers/sample-id/image"
scrapedAt"2026-04-28T00:00:00.000Z"

How to Use

  1. Pick a mode: search (build a query from transaction, property type, cities and filters) or url (paste one or more Otodom search URLs).
  2. Fill in the fields for that mode, and turn Fetch detail pages on if you need GPS coordinates, the full description, agent phone, building year, ownership or the feature list.
  3. Set Max pages and Max listings to control run size and cost, then click Start.
  4. Download the dataset as JSON, CSV or Excel, or read it through the API.

Search mode, basic Warsaw apartments for sale:

{
"mode": "search",
"transaction": "sprzedaz",
"estate": "mieszkanie",
"locations": ["Warszawa"],
"maxPages": 3,
"maxListings": 50,
"fetchDetails": true
}

Search mode, rentals in three cities, filtered:

{
"mode": "search",
"transaction": "wynajem",
"estate": "mieszkanie",
"locations": ["Warszawa", "Krakow", "Wroclaw"],
"minPrice": 2000,
"maxPrice": 4500,
"minRooms": 2,
"maxRooms": 3,
"sortBy": "PRICE_ASC",
"maxPages": 2,
"maxListings": 60,
"fetchDetails": true
}

Search mode, new developments only (primary market):

{
"mode": "search",
"transaction": "sprzedaz",
"estate": "mieszkanie",
"locations": ["Warszawa"],
"marketType": "primary",
"maxPages": 2,
"maxListings": 30,
"fetchDetails": true
}

URL mode, paste any Otodom search URL:

{
"mode": "url",
"urls": [
"https://www.otodom.pl/pl/wyniki/sprzedaz/mieszkanie/mazowieckie/warszawa/warszawa/warszawa/mokotow"
],
"minPrice": 400000,
"maxRooms": 3,
"maxPages": 5,
"maxListings": 100,
"fetchDetails": true
}

URL mode is the recommended path for filters not exposed above (district, neighborhood, building type) or for cities outside the built-in lookup table. Open Otodom in your browser, refine your search, then copy the URL. The price, area and room range fields still apply to listings found this way.

Supported URL shapes:

ShapeExampleResult
Search (city, district, filters, sort, ?page=N)/pl/wyniki/sprzedaz/mieszkanie/mazowieckie/warszawa/warszawa/warszawaAll listings, walking pages from the page in the URL
Paginated search (?page=2, walks forward from that page)/pl/wyniki/sprzedaz/mieszkanie/mazowieckie/warszawa/warszawa/warszawa?page=2Listings from page 2 onward
Search with market and rooms query (the site redirects to a canonical path)/pl/wyniki/sprzedaz/mieszkanie/cala-polska?ownerTypeSingleSelect=ALL&roomsNumber=%5BTWO%5D&market=PRIMARYAll matching listings
Rent search with price and sort query/pl/wyniki/wynajem/mieszkanie/cala-polska?priceMin=1000&priceMax=3000&by=PRICE&direction=ASCAll listings
District with radius/pl/wyniki/sprzedaz/mieszkanie/malopolskie/krakow/krakow/krakow/stare-miasto?distanceRadius=5All listings
New-development search/pl/wyniki/sprzedaz/inwestycja/cala-polskaAll listings
Single listing (also the short /pl/oferta/ID... form)/pl/oferta/2-pok-46-38-m2-balkon-umeblowane-do-wejscia-0-prowizji-ID4CBFQOne record with full detail
Agency profile/pl/firmy/biura-nieruchomosci/hampton-home-nieruchomosci-ID3065052The first page of the agency's listings (about 6; the site does not serve further pages on this address, a warning shows the agency's total)

Not supported, skipped with an error line in the log (never a silent zero): new-development detail pages (/pl/inwestycja/...), agency directory lists (/pl/firmy/biura-nieruchomosci/lista/...), the mobile host m.otodom.pl, /en/ addresses and the old address style (/sprzedaz/mieszkanie/warszawa/). If no URL in the list is usable the run fails with an input error.

Behaviour change: if you switch to URL mode and leave urls untouched (or omit it from an API call), the actor now scrapes the eight example URLs in the table above (one per supported shape, capped by maxListings) instead of doing nothing. An explicitly empty list is still rejected. Each returned listing is billed as usual under the existing event; no pricing changed.

Locations by name or path: locations accepts a city or voivodeship name, or a location path such as malopolskie/krakow/krakow/krakow/stare-miasto. A name or path that cannot be resolved is skipped with an error in the log instead of silently searching all of Poland.

Run it from your code

Python:

from apify_client import ApifyClient
client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("abotapi/otodom-pl-scraper").call(run_input={
"mode": "search",
"transaction": "sprzedaz",
"estate": "mieszkanie",
"locations": ["Warszawa"],
"maxListings": 20,
})
for listing in client.dataset(run["defaultDatasetId"]).iterate_items():
print(listing["title"], listing["priceTotal"], listing["city"])

JavaScript:

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('abotapi/otodom-pl-scraper').call({
mode: 'search',
transaction: 'sprzedaz',
estate: 'mieszkanie',
locations: ['Warszawa'],
maxListings: 20,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();

Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the Integrations tab.

Resume and recurring updates

  • Resume (resumeFromRunId) continues one interrupted full-catalogue walk: paste its run or dataset ID and already-collected listings (matched by id) are skipped, so this run only saves the new ones.
  • Incremental mode (incrementalMode) is for scheduled runs of the same search. The actor remembers the search itself, no ID to paste, and classifies every row NEW, UPDATED (with changedFields), UNCHANGED (suppressed unless emitUnchanged is on), REAPPEARED, or EXPIRED (only after a run that scanned the whole search with no cap or resume, and only with emitExpired on). stateKey names or shares the stored baseline; leave it empty and one is derived automatically from your search settings. Every output field is compared except scrapedAt, the photo fields (images, imageUrl: image links can change without the ad changing), isPromoted (a paid-promotion flag that comes and goes), and anything you list in ignoreFieldsForChanges (ignored fields stay in the output but never trigger UPDATED or appear in changedFields). dateModified is Otodom's own modified-at stamp and moves when an advertiser edits or refreshes an ad; add it to ignoreFieldsForChanges if you only care about content changes. A pasted URL that starts at ?page=2 or later, or any run stopped by maxListings/maxPages, never emits EXPIRED. city and province are now filled from the detail page (they used to be empty); on the first incremental run after this change that fill is saved silently as a one-time backfill and is not returned or billed as UPDATED. With incremental mode off, output is exactly as before.
  • The two combine only when bootstrapping: pass resumeFromRunId on the very first incrementalMode run to seed its baseline from a prior non-incremental pull.

Output views and run summary

The dataset has three views, also linked from the run's Output tab: Listings (overview, the core columns), Listing details (detail: description, GPS, phone, condition, extras, buildYear, ownership, dateModified) and Changes (incremental mode) (changes: changeType, changedFields, firstSeenAt, lastSeenAt). A Run summary (key-value store record OUTPUT) lists, per search or URL, the pages loaded, listings found and status. Detail enrichment also adds images (up to 30 photo URLs), condition, extras, dateModified, agencyUrl and agencyLicenseNumber where Otodom shows them.

Pricing

Pay per result: $0.08 per run start plus $0.0015 per listing at the FREE tier (the Pricing tab shows the rates for your tier). Detail-page enrichment is included in the listing, and incremental mode bills only the rows it returns.

Send results into your apps (MCP connectors)

Optionally pipe the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. This is an extra delivery step after the scrape, the Apify dataset is never changed.

What gets written to the connector: a condensed, human-readable summary of each record, not the full JSON. Each item becomes one entry with a title and its key fields flattened to plain text. The complete record always stays in the Apify dataset.

  1. Authorize a connector once under Apify → Settings → Integrations (Notion, Linear, Airtable, or Apify).
  2. Select it in the "Pipe results into your apps" input field. (If the picker is empty, you have not authorized a connector yet.)
  3. For Notion, also set notionParentPageUrl to the page where items should be created.

The connection is mediated by Apify's MCP proxy, so this actor never sees your third-party credentials. Leave the field empty to skip.

Input Parameters

ParameterTypeDefaultDescription
modestringsearchsearch (use the filters below) or url (paste URLs).
transactionstringsprzedazsprzedaz (sale) or wynajem (rent). Used to build the search URL in search mode.
estatestringmieszkanieProperty type: mieszkanie, dom, dzialka, lokal, garaz, inwestycja, pokoj, or haleimagazyny (halls and warehouses).
locationsarraynone (prefill: ["Warszawa"])Polish city names. 80 cities and all 16 voivodeships are recognized by name; unrecognized names are skipped with an error in the log (and a location path such as malopolskie/krakow/krakow/krakow/stare-miasto is accepted). Leave empty for Poland-wide, or use URL mode for districts and smaller cities.
marketTypestringallall, primary (rynek pierwotny), or secondary (rynek wtórny).
minPriceintegernoneLower bound on total price (PLN).
maxPriceintegernoneUpper bound on total price (PLN).
minAreaintegernoneLower bound on area (m²).
maxAreaintegernoneUpper bound on area (m²).
minRoomsintegernoneMinimum number of rooms, 1 to 10.
maxRoomsintegernoneMaximum number of rooms, 1 to 10.
buildYearintegernoneOnly listings built in this year or later (1900-2030). Search mode only.
postedDatestringempty (any time)Only listings posted within the last 1, 3, 7 or 14 days. Search mode only.
districtsarraynoneCity districts such as Mokotów or Wola; one search runs per district. Applies to city locations only (ignored, with a log warning, for voivodeships or Poland-wide). Search mode only.
sortBystringDEFAULTDEFAULT, LATEST, PRICE_ASC, PRICE_DESC, AREA_ASC, or AREA_DESC. Search mode only.
urlsarrayeight sample URLs (one per supported shape; also the prefill)Full otodom.pl URLs (URL mode): searches, single listings, agency profiles. The price, area and room filters above still apply.
maxPagesinteger0Result pages to walk per search or per URL (about 36 listings per page). 0 walks every page.
maxListingsinteger20Stop after this many listings across all searches. 0 means no cap. The schema accepts at most 1,000,000.
fetchDetailsbooleantrueAlso fetch each listing's detail page for GPS coordinates, full description, agent phone, building year, ownership and the full feature list.
resumeFromRunIdstringnonePrevious run or dataset ID; already-collected listings (matched by id) are skipped.
incrementalModebooleanfalseRecurring monitoring of the same search: adds changeType, changedFields, firstSeenAt, and lastSeenAt to every row.
stateKeystringnone (auto-derived)Optional label for the incremental-mode baseline; leave empty to derive one from your search settings.
emitUnchangedbooleanfalseAlso output UNCHANGED rows in incremental mode (bills extra dataset rows).
emitExpiredbooleanfalseAlso output EXPIRED rows for delisted listings, only after a complete, uncapped, non-resumed scan in incremental mode.
ignoreFieldsForChangesstring[][]Incremental mode only: field names (dotted path for nested, e.g. agency.logo) left out of change detection; they still appear in the output. scrapedAt is always excluded.
proxyobjectResidential (PL)Connection settings. Residential is optional; datacenter or your own proxy URLs also work.
mcpConnectorsarraynoneOptional: send a summary of each record to apps you authorized under Integrations.
notionParentPageUrlstringnoneNotion connector only: page under which records are created.
maxNotifyListingsinteger50Cap on items written to each connector per run.

Output Example

Sample shape, values are illustrative placeholders, not from a live listing.

{
"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.0,
"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.0000,
"longitude": 21.0000,
"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"
}

Plan Requirement

The default connection setting is Apify's residential proxy scoped to Poland. It is optional: in our checks Otodom pages also loaded through Apify's standard datacenter proxy (clear a leftover Poland/residential selection in the connection field to use it), so residential is not required. Datacenter access was checked on single fetches only; heavy runs at volume may hit rate limits, in which case switch back to residential. Residential and datacenter proxy usage are billed to your own Apify account.

If your plan has no residential access, the actor says so in the log ("The selected connection is unavailable to this run. Apify free plans do not include residential proxies. Continuing on a backup connection; some pages may still be unavailable.") and continues on a backup connection. If no page can be read at all, the run ends as failed with one of these messages:

  • CONNECTION_REFUSED: the proxy connection was refused for this account (Apify plans without proxy access need a paid plan), or your own proxy URLs have invalid credentials.
  • NO_RESULTS: residential proxies are unavailable on this run and the backup connection could not load the requested pages.
  • COULD_NOT_LOAD: no results page could be loaded; a connection problem, not an empty search. Try again later.

You can also paste your own proxy URLs into the connection field; make sure they reach Poland for stable routing.

FAQ

How much does it cost?

You pay $0.08 per run start plus $0.0015 per record returned at the FREE tier, with detail-page enrichment included in that same record. The Pricing tab shows the rates for your tier. Use Max listings to cap the cost of any run.

This actor collects only publicly available property listing data. You are responsible for how you use it: follow Otodom's terms and the laws that apply to you, including data protection rules if you store or reuse agent or owner contact details, and get legal advice if you plan commercial redistribution.

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.

Why did my run fail instead of returning an empty dataset?

A run fails only in these cases: (1) the input has no usable URLs or locations left after unrecognized entries are rejected (an input error); (2) no results page can be loaded at all, because the proxy connection was refused for your account, because residential access is unavailable on a free plan and the backup connection could not load the page either, or because the requests failed for another connection reason; (3) resumeFromRunId points to a run or dataset your account cannot read; (4) incrementalMode and resumeFromRunId are combined on a search that already has saved incremental state; or (5) the connection settings cannot be set up at all (for example an invalid proxy configuration). Everything else, including filters that match nothing or a single page that could not be read while other pages load, finishes as a successful run with a warning in the log and an empty or partial dataset. If you get zero listings, check your city name and filters first, then try again.

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.

🔗 Want more real estate data?

Pair this actor with these related scrapers from the same team:

🏠 ImmobilienScout24.de Scraper
$0.9💰/1K for Gold discount. Extract property listings from immobilienscout24.de...
🏠 Immowelt.de Scraper
Scrape detailed property listings from Immowelt.de into clean structured data. Extract...
🏠 SeLoger Scraper
Scrape SeLoger.com properties for sale and rent. Search by location with price, room and...
🏠 Cian RU
Collect property listings from Cian.ru by search filters or direct URLs. Returns...
🏠 Domclick RU Property Scraper
Extract property listings from domclick.ru, one of Russia’s largest real estate portals...
🏠 591.com.tw Scraper
Extract property listings from 591房屋交易網 591.com.tw, Taiwan’s largest real estate...

👉 Browse all abotapi scrapers

💬 Support & custom scrapers

  • 🐞 Found a bug or a missing field? Open a ticket on the Issues tab. We usually reply within hours.
  • 🛠️ Need another site, extra fields or a private build? Email contact@abotapi.com or message Telegram @abotapi.
  • ⭐ Enjoying it? A quick review on the actor page helps other users find it.