How to get Otodom listings in Python
By the end of this guide you have a CSV of Kraków flats for rent from Otodom with rent in PLN, rooms, size and agency, from one script you can run again tomorrow. Everything below was run on 16 October 2026; the output table is what it returned.
Why scraping Otodom yourself breaks
- Otodom has no public API for reading listings. The OLX Group API it offers is for CRM vendors publishing their clients' listings, and the old XML export is no longer supported.
- Searches need Otodom's region path ("malopolskie/krakow/krakow/krakow"); a bare city name finds nothing.
- Agencies and private owners are mixed in the same results; telling them apart means reading a flag in the page data.
None of that is impossible, but each point is code you then maintain. The rest of this guide uses Listingrail, which does the reading and hands back clean JSON. The Otodom API page has the official options with sources.
1. Get a key
One request with your email returns a key with 100 free listings. It is shown once, so keep it in an environment variable.
curl -X POST https://listingrail.com/v1/signup \
-H "content-type: application/json" \
-d '{"email": "you@example.com"}'
export LISTINGRAIL_KEY=re_...2. Search and save to CSV
Install requests (pip install requests), then run:
import csv
import os
import requests
r = requests.post(
"https://listingrail.com/v1/listings/search",
headers={"Authorization": f"Bearer {os.environ['LISTINGRAIL_KEY']}"},
json={"source":"otodom","location":"Kraków","type":"rent","limit":50},
timeout=300,
)
r.raise_for_status()
listings = r.json()["listings"]
fields = ["price", "currency", "rooms", "area", "address", "url"]
with open("otodom-krak-w.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
writer.writerow(fields)
for home in listings:
writer.writerow([home[k] for k in fields])
print(len(listings), "listings saved to otodom-krak-w.csv")The request body, on its own:
{
"source": "otodom",
"location": "Kraków",
"type": "rent",
"limit": 50
}Each listing returned costs one credit. A search that finds nothing, or fails at Otodom, costs nothing. A request can take up to a minute or two, because Otodom is read at that moment.
3. What comes back
The first three rows of the run on 16 October 2026:
Every listing has the same keys on all 17 portals, so the code for Otodom reads the others too. Fields Otodom does not show come back as null, and the portal's own record sits in raw. With pandas:
import pandas as pd
df = pd.DataFrame(listings)
df["agent"] = df["agent"].str.get("company")
print(df[["price", "rooms", "area", "address", "agent"]].head())4. Get new listings and price cuts every day
Running the script on a schedule works, but then you compare the files yourself. A monitor stores the search, checks it once a day, and sends only what changed: new listings and price cuts. The free key holds one monitor.
requests.post(
"https://listingrail.com/v1/monitors",
headers={"Authorization": f"Bearer {os.environ['LISTINGRAIL_KEY']}"},
json={
"source": "otodom",
"location": "Kraków",
"filters": {"type":"rent"},
"webhook_url": "https://example.com/new-listings",
},
timeout=30,
)Changes can also be read with GET /v1/monitors/{id}/changes or downloaded as CSV. Plans and limits are on the monitors page.
Otodom quirks worth knowing
- Pass a large Polish city ("Kraków", "Warszawa", "Wrocław"), a region path from an Otodom link, or "cala-polska" for the whole country. Searches are for flats.
- price is the rent in PLN with price_period "month". Some ads add a second monthly amount, which is in raw.rent_price.
- rooms is filled and beds is null: Otodom counts rooms. area is in square metres.
- agent.company is the agency, or null for a private owner (raw.is_private_owner). listed_at is when the ad was first created and can be years old.
Questions
Does Otodom have an API?
Only the OLX Group partner API for CRM software, which publishes listings. There is no API for searching them.
Why does my city return nothing?
Use one of the large cities, or copy the region path from an Otodom search link (e.g. "mazowieckie/warszawa").
Does it cover flats for sale?
Yes: use "type": "sale".