🏠 immoscout
A typed, unofficial Python client for ImmobilienScout24 — search real-estate listings, fetch expose details, and plug it all into AI agents.
Instead of a raw JSON blob, you get clean typed objects (Listing, Expose) — and a
built-in MCP server so assistants like Claude can
search real estate for you natively.
📖 Documentation · pip install immoscout
⚠️ Unofficial and not affiliated with ImmobilienScout24. For personal use, and gentle with request volume — see the DISCLAIMER.
✨ Features
- 🚫 No captchas to solve — talks to ImmobilienScout24's mobile API, which returns clean JSON directly. No headless browser, no captcha-solving, no HTML scraping — where the website makes even human users pass captchas, the mobile endpoints just answer.
- 🔍 Typed results —
search()returnsSearchResultwith parsedListingobjects (price, rooms, m², address, URL) — not a nested dict you have to reverse-engineer. - 🎯 Validated filters —
SearchFilterwith price/rooms/space ranges and real-estate types. - 🗺️ Place names, not codes —
region="München"is auto-resolved to the right geocode path. - 📄 Auto-pagination & counts —
search_all()walks every page;count()returns totals only. - 🛡️ Robust HTTP — request timeouts, retries with backoff, and explicit rate-limit errors.
- 🤖 Agent-native — an MCP server (
immoscout-mcp) exposes search as tools for Claude & co. - ⌨️ CLI —
immoscout search …andimmoscout expose …. - 🧩 Nothing hidden — every object keeps the untouched API payload on
.raw.
🚀 Quick Start
from immoscout import ImmoscoutClient
client = ImmoscoutClient()
result = client.search(region="Berlin", price_max=1200, rooms_min=2) # place name auto-resolved
print(f"{result.total_results} results")
for listing in result.listings[:5]:
print(listing.title)
print(f" {listing.price:.0f} {listing.currency} · {listing.rooms:g} rooms · {listing.living_space:g} m²")
print(f" {listing.address.line}")
print(f" {listing.url}")
Finding a region
You can pass a place name directly (region="München") and it's resolved for you. To
see the options, or to store the exact path, look them up:
client.suggest_regions("münchen")[0].region # -> "/de/bayern/muenchen"
immoscout regions münchen
# /de/bayern/muenchen city München
# /de/bayern/muenchen-kreis district München (Kreis)
🎯 Filters & pagination
from immoscout import ImmoscoutClient, RealEstateType, SearchFilter
client = ImmoscoutClient()
query = SearchFilter(
region="/de/bayern/muenchen",
real_estate_type=RealEstateType.APARTMENT_RENT,
price_max=1500,
rooms_min=2,
living_space_min=50,
)
# Walk every result page (one request per page) — cap it to stay gentle:
for listing in client.search_all(query, max_pages=3):
print(listing.price, listing.title)
📄 Expose details
expose = client.get_expose("169446368")
print(expose.title, expose.price, expose.rooms, expose.living_space)
print(expose.address)
print(expose.description)
print(expose.attributes) # {"Wohnungstyp": "Etagenwohnung", "Etage": "2 von 4", ...}
⌨️ CLI
immoscout regions münchen # find a region path
immoscout search --region /de/berlin/berlin --price-max 1200 --rooms-min 2
immoscout search --region /de/berlin/berlin --json # machine-readable
immoscout expose 169446368
🤖 Use it from AI agents (MCP)
immoscout ships a Model Context Protocol server, so
any MCP client (Claude Desktop, Claude Code, Cursor, …) can search real estate natively.
pip install "immoscout[mcp]"
immoscout-mcp # runs the server (stdio)
Register it in your MCP client — e.g. Claude Desktop's mcpServers:
{
"mcpServers": {
"immoscout": { "command": "immoscout-mcp" }
}
}
The agent then has these tools:
search_listings— region, real-estate type, price/rooms/space bounds, max results.count_listings— how many match, without fetching them.get_expose— full details for a listing ID.suggest_regions— resolve a place name to region paths (so the agent can search any city).
Now you can ask "find me 2-room apartments in Berlin under 1200 € and summarize the three cheapest" and the agent does the lookup itself.
🧱 API overview
| Object | What it is |
|---|---|
ImmoscoutClient(timeout, max_retries, backoff) |
HTTP client — search(), search_all(), count(), get_expose(), suggest_regions() |
SearchFilter(...) |
Validated query; .to_params() compiles to API params |
RealEstateType |
Enum: apartment/house rent & buy, flat-share room, garage |
SearchResult |
total_results, number_of_pages, listings, raw |
Listing |
Parsed result: price, rooms, living_space, address, url, raw |
Expose |
Detail view: price, attributes, description, raw |
ImmoscoutError |
Base; RequestError, NotFoundError, RateLimitError |
🛠️ Development
pip install -e ".[dev]"
ruff check .
pytest # unit tests are fully mocked — no network needed
CI runs ruff + pytest across Python 3.10–3.13.
⚖️ Responsible use
ImmobilienScout24 actively rate-limits and blocks automated traffic. Keep the built-in timeouts/retries, add delays, and don't scrape at scale. This project is for personal and educational use — see the DISCLAIMER.
📄 License
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file immoscout-0.2.0.tar.gz.
File metadata
- Download URL: immoscout-0.2.0.tar.gz
- Upload date:
- Size: 18.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
00205ee00dccae7c14b14c722ebfbd8bfbe3ab84e73b276420dff9f788bd685e
|
|
| MD5 |
5428906c77cbb61473298c57122519fd
|
|
| BLAKE2b-256 |
ccc31963aad025c2a89631be199cb0ba5dd142b0fa37cb1eb4c3151a243e7bf3
|
File details
Details for the file immoscout-0.2.0-py3-none-any.whl.
File metadata
- Download URL: immoscout-0.2.0-py3-none-any.whl
- Upload date:
- Size: 15.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b3469a0881243741baff339525f6d289c970dc8626b00df575ec998e3cf4acf2
|
|
| MD5 |
06141a8f04357146fa2b58f0cef418d1
|
|
| BLAKE2b-256 |
44b3f478406e4529afa3c360298a863cf594022d9ff7a3a17b95e3f24a5127f5
|