# Otodom Rental Yield Calculator: Buy-to-Let ROI Poland (`donzely/otodom-rental-yield-calculator`) Actor

Find the best apartments to buy and rent out in Poland. Scrapes Otodom.pl sale and rental listings, estimates rent per neighbourhood and calculates gross and net rental yield, payback period and price vs area median for every apartment for sale.

- **URL**: https://apify.com/donzely/otodom-rental-yield-calculator.md
- **Developed by:** [Grzegorz Pryjma](https://apify.com/donzely) (community)
- **Categories:** Real estate, Automation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$2.60 / 1,000 analyzed apartments

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Otodom Rental Yield Calculator: Buy-to-Let ROI Poland

**Find the best apartments to buy and rent out in Poland.** This Actor scrapes **Otodom.pl** apartments for sale and for rent in the same area, builds a neighbourhood-level rent model and calculates for every apartment for sale its **expected monthly rent, gross and net rental yield, payback period** and how its **price per m² compares with the area median**.

🇵🇱 *Kalkulator rentowności najmu z Otodom: dla każdego mieszkania na sprzedaż szacowany czynsz najmu, rentowność brutto i netto, okres zwrotu oraz cena za m² na tle mediany dzielnicy i osiedla.*

### What it does

1. Takes one or more locations typed like on Otodom ("Kraków", "Warszawa Mokotów", "Gdańsk Wrzeszcz"), Otodom location paths, or a full Otodom search URL with your filters. In small towns with few rentals, rent is estimated from the wider county and marked as low confidence.
2. Scrapes rental listings in the area and computes **median rent per m²** per neighbourhood, district and city, split by apartment size (under 30, 30–40, 40–55, 55–75, 75+ m²).
3. Scrapes apartments for sale (newest first) and for each one estimates the rent from the most specific group with enough data.
4. Calculates:
   - **gross yield** = annual rent / price
   - **net yield** after vacancy, maintenance, 8.5% flat rental tax (ryczałt), insurance, and purchase costs (2% PCC tax on the secondary market, notary and court fees, optional agency fee, renovation and furnishing)
   - **payback period** in years
   - **price vs area median** in %, so you see underpriced apartments immediately
5. Saves area statistics (rent per m², price per m², gross yield for every neighbourhood) to the key-value store record `AREA_STATS`.

### Who is it for

- Buy-to-let investors comparing districts and single apartments
- Real estate agents preparing investment offers for clients
- Flippers looking for listings priced below the neighbourhood median
- Analysts tracking rental yields in Warsaw, Kraków, Wrocław, Gdańsk, Poznań, Łódź and other Polish cities

### Input

| Field | Description |
|---|---|
| `locations` | City or district names, e.g. `Kraków`, `Warszawa Mokotów`, `Gdańsk Wrzeszcz`. Otodom location paths and full Otodom sale search URLs also work |
| `maxListingsPerLocation` | How many apartments for sale to analyze per location |
| `rentalPagesPerLocation` | Rental pages (72 listings each) used for the rent model |
| `maxPricePln`, `minAreaM2`, `maxAreaM2`, `market` | Filters |
| `minGrossYieldPct`, `minNetYieldPct` | Output only apartments above these yields |
| investment assumptions | Vacancy, maintenance, tax, insurance, PCC, notary, agency fee, renovation, furnishing |

Tip: open Otodom, choose a city or district, and copy the part of the URL after `/mieszkanie/`.

```json
{
  "locations": ["Warszawa Mokotów", "Kraków"],
  "maxListingsPerLocation": 200,
  "maxPricePln": 800000,
  "minNetYieldPct": 4.5
}
```

### Output

```json
{
  "title": "2 pokoje, balkon, metro",
  "url": "https://www.otodom.pl/pl/oferta/...",
  "district": "Mokotów",
  "neighbourhood": "Służewiec",
  "areaM2": 52.8,
  "rooms": 2,
  "market": "SECONDARY",
  "priceTotal": 1160000,
  "pricePerM2": 21954,
  "areaMedianPricePerM2": 21210,
  "priceVsAreaMedianPct": 3.5,
  "estimatedRentPln": 4312,
  "rentPerM2Estimate": 81.6,
  "rentEstimateBasis": "neighbourhood Warszawa/Mokotów/Służewiec, size 40-55",
  "rentSampleSize": 24,
  "rentConfidence": "high",
  "grossYieldPct": 4.46,
  "netYieldPct": 3.37,
  "paybackYears": 29.7,
  "annualNetIncomePln": 39900,
  "costs": { "pccTaxPln": 23200, "notaryAndCourtPln": 5000, "totalInvestmentPln": 1188200, "annualRentalTaxPln": 4030 }
}
```

The **Rental yield overview** table shows the key numbers side by side. Export to Excel, CSV or JSON, or connect through the API, Make, Zapier or n8n.

### Pricing

Pay per result: you pay only for analyzed apartments. There is no start fee. Rental listings used for the rent model are free.

### Accuracy and limits

- Rent is estimated from asking prices of current rental listings, not signed contracts.
- `rentConfidence` shows how many comparable rentals were behind the estimate: low under 10, medium 10–19, high 20 and more. Increase `rentalPagesPerLocation` for more precise neighbourhood estimates.
- Tax and cost defaults reflect typical 2026 values for private investors in Poland and are not tax advice.
- Only apartments (mieszkania) are supported.

### FAQ

**Can I analyze a whole city?** Yes. Use the city path, e.g. `dolnoslaskie/wroclaw/wroclaw/wroclaw`, and raise `rentalPagesPerLocation` so every district has enough rentals.

**Does it work for new developments?** Yes. Primary market listings skip the PCC tax. Set `market` to `PRIMARY` to see only them.

**Is scraping Otodom legal?** The Actor reads only publicly available listings without logging in. Use the data in line with Otodom's terms and local law.

# Actor input Schema

## `locations` (type: `array`):

City or district names as you would type them on Otodom (e.g. "Kraków", "Warszawa Mokotów", "Gdańsk Wrzeszcz"), Otodom location paths (e.g. "mazowieckie/warszawa/warszawa/warszawa/mokotow") or Otodom search URLs. Each location is analyzed separately.

## `maxListingsPerLocation` (type: `integer`):

How many apartments for sale to analyze per location (newest first).

## `rentalPagesPerLocation` (type: `integer`):

Rental result pages (72 listings each) used to estimate rent per m². More pages give more precise neighbourhood estimates.

## `maxPricePln` (type: `integer`):

Only apartments up to this price.

## `minAreaM2` (type: `integer`):

Minimum apartment size.

## `maxAreaM2` (type: `integer`):

Maximum apartment size.

## `market` (type: `string`):

Primary market = new apartments from developers (no PCC tax).

## `minGrossYieldPct` (type: `number`):

Output only apartments with at least this gross yield.

## `minNetYieldPct` (type: `number`):

Output only apartments with at least this net yield.

## `vacancyMonthsPerYear` (type: `number`):

Months per year without a tenant.

## `maintenancePctOfRent` (type: `number`):

Repairs and wear, as % of collected rent.

## `rentalTaxPct` (type: `number`):

Polish flat-rate tax on private rental (ryczałt) is 8.5% up to 100,000 PLN per year.

## `insurancePlnPerYear` (type: `integer`):

Apartment insurance.

## `pccPctSecondary` (type: `number`):

Civil law transactions tax for secondary market purchases. Set 0 for first-home exemption.

## `notaryAndCourtPln` (type: `integer`):

Notary deed, land register entries.

## `agencyFeePct` (type: `number`):

Buyer's commission for listings by agencies (not applied to private owners).

## `renovationPlnPerM2Secondary` (type: `integer`):

Refresh before renting out.

## `furnishingPln` (type: `integer`):

One-off furnishing cost.

## `minSampleSize` (type: `integer`):

Minimum number of listings in a neighbourhood and size group before falling back to a wider area.

## `proxyConfiguration` (type: `object`):

Optional Apify Proxy.

## Actor input object example

```json
{
  "locations": [
    "Warszawa Mokotów"
  ],
  "maxListingsPerLocation": 72,
  "rentalPagesPerLocation": 10,
  "market": "ALL",
  "vacancyMonthsPerYear": 1,
  "maintenancePctOfRent": 5,
  "rentalTaxPct": 8.5,
  "insurancePlnPerYear": 400,
  "pccPctSecondary": 2,
  "notaryAndCourtPln": 5000,
  "agencyFeePct": 0,
  "renovationPlnPerM2Secondary": 0,
  "furnishingPln": 0,
  "minSampleSize": 5,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `overview` (type: `string`):

No description

## `results` (type: `string`):

No description

## `areaStats` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "locations": [
        "Warszawa Mokotów"
    ],
    "maxListingsPerLocation": 72
};

// Run the Actor and wait for it to finish
const run = await client.actor("donzely/otodom-rental-yield-calculator").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "locations": ["Warszawa Mokotów"],
    "maxListingsPerLocation": 72,
}

# Run the Actor and wait for it to finish
run = client.actor("donzely/otodom-rental-yield-calculator").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "locations": [
    "Warszawa Mokotów"
  ],
  "maxListingsPerLocation": 72
}' |
apify call donzely/otodom-rental-yield-calculator --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,donzely/otodom-rental-yield-calculator"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/DhUdwpld5x1eckibF/builds/f2usfucoLFNphwL30/openapi.json
