Otodom.pl Scraper — Polish Real Estate Listings + GPS avatar

Otodom.pl Scraper — Polish Real Estate Listings + GPS

Pricing

from $1.50 / 1,000 listings

Go to Apify Store
Otodom.pl Scraper — Polish Real Estate Listings + GPS

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

DIOPSIDE AI

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

a day ago

Last modified

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:

  1. Otodom canonicalises filters into the URL path. Ask for market=SECONDARY or 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.
  2. 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 it exclamation. 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.
  3. The Next.js buildId rotates 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/httpx get 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: 0 means 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.

FieldTypeDefaultDescription
startUrlsarray[]Otodom search (/pl/wyniki/...) or offer (/pl/oferta/...) URLs. Filters in the URL are preserved. Overrides the three fields below.
searchTypestringsprzedazsprzedaz (sale) or wynajem (rent)
propertyTypestringmieszkaniemieszkanie, dom, dzialka, lokal, haleimagazyny, garaz
locationstringcala-polskaPath from an Otodom URL, e.g. mazowieckie/warszawa/warszawa/warszawa, malopolskie/krakow/krakow/krakow, mazowieckie, cala-polska
priceMin / priceMaxintegerPrice in PLN (monthly rent for rentals)
areaMin / areaMaxintegerFloor area in m²
roomsMin / roomsMaxintegerRoom count range
buildYearintegerMinimum construction year
marketstringALLALL, PRIMARY (developer), SECONDARY (resale)
postedDatestring""1, 3, 7, 14 — listings created in the last N days
sortBystringDEFAULTNew. LATEST, PRICE_ASC/DESC, AREA_ASC/DESC, PRICE_PER_M_ASC/DESC
maxItemsinteger50Per start URL. 0 = every listing Otodom returns
extractDetailsbooleantrueVisit each offer page for GPS, description, building facts, all images
maxConcurrencyinteger8Parallel requests
maxRequestRetriesinteger3Retries per request; each rotates to a fresh proxy session
maxRequestsPerMinuteinteger120Request rate ceiling
proxyConfigurationobjectnoneNo proxy needed — see "Why this one"
residentialFallbackbooleantrueNew. 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.
  • sellerPhone is 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, agencyName and agencyId stay null and sellerType is private. 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 proxyConfiguration from your input entirely. Residential bandwidth buys you nothing on this site, and the datacenter pool measured worse than no proxy at all.
  • market is null in listing-only mode unless you set the market filter — Otodom genuinely does not state PRIMARY/SECONDARY on a search card, so we leave it null rather than guess. It is always populated when extractDetails is on.

Run sizes and cost

RunRequestsData transferredMeasured
20 listings, details on1 search + 20 offer pages~0.5 MB14 s
50 listings, details on1 search + 50 offer pages~1 MB~30 s
500 listings, details on7 searches + 500 offer pages~11 MB~5 min
500 listings, details off7 searches~0.3 MB~20 s
5 000 listings, details off70 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 totalResults giving 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, market and condition, then map the results by GPS.
  • Rental yield modelling — pair a sprzedaz run and a wynajem run over the same district and compare price per m² against rent per m².
  • Price-change monitoring — schedule daily with sortBy: LATEST and diff on id + price + dateModified.

Notes

  • Public listing data only. Otodom's robots.txt Allows /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/query endpoint that robots.txt disallows.
  • 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.