Search flats and houses to rent or buy in Poland on Otodom.
Searches Otodom, Poland's largest property portal, in one of the 12 biggest cities: flats, houses or rooms to rent or for sale, by price, area, rooms (flats) and sort. Each listing has the price in PLN (and the monthly fees / czynsz), price per m², area, rooms, floor, street and district, private or agency, when it was listed, a photo and the link. Promoted listings are dropped by default.
- Site
- Otodom, Poland
- Version
- v2 of 2
- Runs, last 7 days
- No reports yet
- sha256
- 38f3edd1ee8a…
Run it with your agent
after installing WieldAsk in plain words. Your agent finds this recipe and calls it:
“Search flats and houses to rent or buy in Poland on Otodom.”
Read-only: it changes nothing on the site. No login needed.
{ "tool": "run", "arguments": { "recipe_id": "otodom.pl/search-properties", "params": { "city": "warszawa", "max_price": 4000, "rooms": 2 } } }
Requests
sent from your browser, in order- 1GET/pl/wyniki/{{f.tx}}/{{f.estate}}/{{f.loc}}?areaMax&areaMin&by&direction&page&priceMax&priceMinThe server-rendered results page /pl/wyniki/<wynajem|sprzedaz>/<mieszkanie[,N-pokoje]|dom|pokoj>/<location id> (Next.js; __NEXT_DATA__ props.pageProps.data.searchAds). The one-item foreach maps the city to Otodom's location id and builds the path and sort.
Parameters
from your agentReturns
to your agentWhere it can send
- www.otodom.pl
Anything else is blocked by the extension.
Reads from your session
Nothing. Your browser attaches the site's cookies by itself.
Used only in requests to the addresses on the left. Never sent to wield.run or your agent.
Checks
a run passes when- length(output.listings) > `15`
- output.total > `100`
- length(output.listings[?promoted]) == `0`
- length(output.listings[?price_pln > `4000`]) == `0`
- length(output.listings[?rooms != `null` && rooms != `2`]) == `0`
- length(output.listings[?starts_with(url, 'https://www.otodom.pl/pl/oferta/') && city == 'Warszawa']) > `15`
Raw recipe
Served as canonical JSON from /api/v1/recipes/otodom.pl/search-properties, so its sha256 matches the ETag and the index.
curl -s https://wield.run/api/v1/recipes/otodom.pl/search-properties | shasum -a 256 # 38f3edd1ee8aac25e855d62e375ee956ccbe8c557d0aa1976314760e029f0674
{ "description": "Searches Otodom, Poland's largest property portal, in one of the 12 biggest cities: flats, houses or rooms to rent or for sale, by price, area, rooms (flats) and sort. Each listing has the price in PLN (and the monthly fees / czynsz), price per m², area, rooms, floor, street and district, private or agency, when it was listed, a photo and the link. Promoted listings are dropped by default.", "effect": "read", "id": "otodom.pl/search-properties", "keywords": [ "otodom", "mieszkanie", "wynajem", "sprzedaż", "dom", "pokój", "nieruchomości", "czynsz", "warszawa", "kraków", "wrocław", "gdańsk", "poznań", "flat", "apartment", "rent", "buy", "poland", "property", "real estate" ], "output": { "schema": { "properties": { "city": { "type": "string" }, "listings": { "items": { "properties": { "agency": { "type": [ "string", "null" ] }, "area_m2": { "type": [ "number", "null" ] }, "bumped_at": { "description": "When the advertiser last paid to push it up", "type": [ "string", "null" ] }, "city": { "type": [ "string", "null" ] }, "development": { "description": "A new-build development (inwestycja)", "type": "boolean" }, "district": { "type": [ "string", "null" ] }, "fees_pln": { "description": "Monthly fees (czynsz) where given", "type": [ "number", "null" ] }, "floor": { "description": "0 ground floor, -1 cellar, 11 above the tenth, 99 attic", "type": [ "integer", "null" ] }, "id": { "type": "string" }, "image": { "type": [ "string", "null" ] }, "listed": { "type": [ "string", "null" ] }, "price_per_m2_pln": { "type": [ "number", "null" ] }, "price_pln": { "description": "Rent per month or sale price; null for new developments that show a price per m² only", "type": [ "number", "null" ] }, "private_owner": { "type": "boolean" }, "promoted": { "type": "boolean" }, "rooms": { "description": "11 = more than 10", "type": [ "integer", "null" ] }, "street": { "type": [ "string", "null" ] }, "title": { "type": "string" }, "url": { "type": "string" } }, "type": "object" }, "type": "array" }, "page": { "type": "integer" }, "total": { "type": [ "integer", "null" ] }, "transaction": { "type": "string" } }, "required": [ "city", "transaction", "listings" ], "type": "object" }, "value": "let $promo = params.include_promoted == `true`, $num = items(`{\"ONE\":1,\"TWO\":2,\"THREE\":3,\"FOUR\":4,\"FIVE\":5,\"SIX\":6,\"SEVEN\":7,\"EIGHT\":8,\"NINE\":9,\"TEN\":10,\"MORE\":11}`), $fl = items(`{\"CELLAR\":-1,\"GROUND\":0,\"FIRST\":1,\"SECOND\":2,\"THIRD\":3,\"FOURTH\":4,\"FIFTH\":5,\"SIXTH\":6,\"SEVENTH\":7,\"EIGHTH\":8,\"NINTH\":9,\"TENTH\":10,\"ABOVE_TENTH\":11,\"GARRET\":99}`) in {city: params.city, transaction: params.transaction || 'rent', total: steps.search[0].total || `0`, page: params.page || `1`, listings: map(&(let $a = @ in let $geo = $a.location.reverseGeocoding.locations || `[]` in {id: to_string($a.id), title: $a.title, price_pln: $a.hidePrice != `true` && $a.totalPrice.value || `null`, fees_pln: $a.rentPrice.value || `null`, price_per_m2_pln: $a.pricePerSquareMeter.value || $a.priceFromPerSquareMeter.value || `null`, area_m2: $a.areaInSquareMeters || `null`, rooms: ($num[?[0] == $a.roomsNumber] | [0][1]) || `null`, floor: ($fl[?[0] == $a.floorNumber] | [0][1]) || `null`, street: $a.location.address.street.name || `null`, district: ($geo[?locationLevel == 'district'] | [0].name) || `null`, city: ($geo[?locationLevel == 'city_or_village'] | [0].name) || `null`, private_owner: $a.isPrivateOwner == `true`, agency: $a.agency.name && trim($a.agency.name) || `null`, development: $a.estate == 'INVESTMENT', listed: $a.createdAtFirst || $a.dateCreated || `null`, bumped_at: $a.pushedUpAt || `null`, promoted: $a.isPromoted == `true`, image: $a.images[0].medium || `null`, url: join('', ['https://www.otodom.pl/pl/oferta/', $a.slug])}), steps.search[0].items || `[]`)[?$promo || !promoted]}" }, "params": { "additionalProperties": false, "properties": { "city": { "description": "warszawa, krakow, wroclaw, lodz, poznan, gdansk, gdynia, szczecin, katowice, lublin, bialystok or bydgoszcz", "enum": [ "warszawa", "krakow", "wroclaw", "lodz", "poznan", "gdansk", "gdynia", "szczecin", "katowice", "lublin", "bialystok", "bydgoszcz" ], "type": "string" }, "include_promoted": { "default": false, "description": "Keep promoted listings, marked", "type": "boolean" }, "max_area": { "description": "m²", "minimum": 0, "type": "integer" }, "max_price": { "description": "PLN (per month when renting)", "minimum": 0, "type": "integer" }, "min_area": { "description": "m²", "minimum": 0, "type": "integer" }, "min_price": { "description": "PLN (per month when renting)", "minimum": 0, "type": "integer" }, "page": { "default": 1, "description": "36 listings per page", "maximum": 50, "minimum": 1, "type": "integer" }, "property_type": { "default": "flat", "enum": [ "flat", "house", "room" ], "type": "string" }, "rooms": { "description": "Flats only: exactly this many rooms", "enum": [ 2, 3, 4 ], "type": "integer" }, "sort": { "default": "relevance", "enum": [ "relevance", "newest", "price_low", "price_high" ], "type": "string" }, "transaction": { "default": "rent", "enum": [ "rent", "buy" ], "type": "string" } }, "required": [ "city" ], "type": "object" }, "provenance": { "method": "browser-inspect", "notes": "Otodom's results pages are server-rendered by Next.js: __NEXT_DATA__ props.pageProps.data.searchAds has 36 items per page and pagination.totalItems. Location ids are voivodeship/powiat/gmina/city (mazowieckie/warszawa/warszawa/warszawa); the 12 here were each checked live (200 with that location and thousands of ads). The site's own location search is a lazily loaded GraphQL query, so the recipe uses this fixed list. A rooms filter is part of the path (mieszkanie,3-pokoje); the query form roomsNumber=[TWO] 307-redirects to it, and a 1-room path does not exist (it redirects without the filter). priceMin/priceMax/areaMin/areaMax, by/direction (LATEST, PRICE) and page are query parameters. isPromoted listings are paid placements (dropped by default); pushedUpAt is a paid bump of a genuine listing (kept, shown). Advertiser names of private persons are left out. Answers servers. v2 (2026-11-07 22:38): tos.notes only; requests and output unchanged.", "recorded_at": "2026-11-07T22:08:00+01:00", "recorded_by": "C4" }, "requires_login": false, "schema": "https://wield.run/schema/recipe/v1.json", "site": { "country": "PL", "id": "otodom.pl", "language": "pl", "name": "Otodom", "origin": "https://www.otodom.pl", "origins": [ "https://www.otodom.pl" ] }, "steps": [ { "description": "The server-rendered results page /pl/wyniki/<wynajem|sprzedaz>/<mieszkanie[,N-pokoje]|dom|pokoj>/<location id> (Next.js; __NEXT_DATA__ props.pageProps.data.searchAds). The one-item foreach maps the city to Otodom's location id and builds the path and sort.", "expect": { "content_type": "html", "status": [ 200 ] }, "extract": { "embedded_json": { "selector": "script#__NEXT_DATA__", "vars": { "items": "props.pageProps.data.searchAds.items", "total": "props.pageProps.data.searchAds.pagination.totalItems" } } }, "foreach": { "as": "f", "in": "[{tx: params.transaction == 'buy' && 'sprzedaz' || 'wynajem', estate: join('', [(params.property_type == 'house' && 'dom') || (params.property_type == 'room' && 'pokoj') || 'mieszkanie', (params.property_type != 'house' && params.property_type != 'room' && params.rooms) && join('', [',', to_string(params.rooms), '-pokoje']) || '']), loc: (let $c = params.city in items(`{\"warszawa\":\"mazowieckie/warszawa/warszawa/warszawa\",\"krakow\":\"malopolskie/krakow/krakow/krakow\",\"wroclaw\":\"dolnoslaskie/wroclaw/wroclaw/wroclaw\",\"lodz\":\"lodzkie/lodz/lodz/lodz\",\"poznan\":\"wielkopolskie/poznan/poznan/poznan\",\"gdansk\":\"pomorskie/gdansk/gdansk/gdansk\",\"gdynia\":\"pomorskie/gdynia/gdynia/gdynia\",\"szczecin\":\"zachodniopomorskie/szczecin/szczecin/szczecin\",\"katowice\":\"slaskie/katowice/katowice/katowice\",\"lublin\":\"lubelskie/lublin/lublin/lublin\",\"bialystok\":\"podlaskie/bialystok/bialystok/bialystok\",\"bydgoszcz\":\"kujawsko--pomorskie/bydgoszcz/bydgoszcz/bydgoszcz\"}`)[?[0] == $c] | [0][1]), by: (params.sort == 'newest' && 'LATEST') || ((params.sort == 'price_low' || params.sort == 'price_high') && 'PRICE') || `null`, dir: (params.sort == 'newest' && 'DESC') || (params.sort == 'price_low' && 'ASC') || (params.sort == 'price_high' && 'DESC') || `null`, page: (params.page || `1`) > `1` && to_string(params.page) || `null`}]", "max": 1 }, "id": "search", "parse": "html", "request": { "headers": { "Accept": "text/html" }, "method": "GET", "query": { "areaMax": "{{params.max_area}}", "areaMin": "{{params.min_area}}", "by": "{{f.by}}", "direction": "{{f.dir}}", "page": "{{f.page}}", "priceMax": "{{params.max_price}}", "priceMin": "{{params.min_price}}" }, "url": "/pl/wyniki/{{f.tx}}/{{f.estate}}/{{f.loc}}" } } ], "task": "Search flats and houses to rent or buy in Poland on Otodom", "tos": { "notes": "Otodom's robots.txt sets Content-Signal ai-input=no and ai-train=no. Wield runs it only when the user asks, in the user's own browser, one search page per call: no bulk collection, no contact with advertisers. Terms not reviewed yet for automated access. One person's search: 1 request for one results page, as a visitor gets it. robots.txt allows /pl/wyniki/. Contacting advertisers stays on otodom.pl.", "reviewed": false }, "verify": { "checks": [ "length(output.listings) > `15`", "output.total > `100`", "length(output.listings[?promoted]) == `0`", "length(output.listings[?price_pln > `4000`]) == `0`", "length(output.listings[?rooms != `null` && rooms != `2`]) == `0`", "length(output.listings[?starts_with(url, 'https://www.otodom.pl/pl/oferta/') && city == 'Warszawa']) > `15`" ], "example_params": { "city": "warszawa", "max_price": 4000, "rooms": 2 }, "max_ms": 6000 }, "version": 2 }