Monitor listings across Holland2Stay, OurDomain, OurCampus, Xior and Magis. Get instant notifications and auto-book on Holland2Stay.
cp .env.example .env && mkdir -p data logs logs/caddy # Edit Caddyfile → replace your.domain.com with your domain # Edit .env → set WEB_PASSWORD docker compose up -d
Open https://your.domain.com, log in, then click Start monitor on the Dashboard.
Download the latest .dmg from Releases, drag to Applications, double-click. The browser opens automatically.
Download the latest .zip from Releases, extract and run flatradar.exe.
iOS: Download on the App Store — native SwiftUI client.
Android: Download Android App (.apk) — Kotlin + Compose client with FCM push.
pip install -r requirements.txt cp .env.example .env python web.py
Open http://127.0.0.1:8088.
FlatRadar monitors four housing platforms. The Platform badge (H2S / OD / OC / XR) on each listing shows where it came from.
| Platform | Coverage | Data Level | Auto-Booking |
|---|---|---|---|
| Holland2Stay (H2S) | 26 Dutch cities | Unit (room number, rent, area) | ✅ Full GraphQL flow |
| OurDomain (OD) | Amsterdam (Diemen + South-East) | Unit (#6045, sqm, floor, view) | ❌ Notify only |
| OurCampus (OC) | Amsterdam Diemen (1 building) | Unit (same as OurDomain) | ❌ Notify only |
| Xior (XR) | 30 buildings across 14 Dutch cities | Unit (M1.30.53, sqm, rent, deposit) | ❌ Notify only |
| Magis (MG) | 17 buildings across 5 cities — 9 in Eindhoven | Unit (rent, sqm, floor, energy label) | ❌ Notify only |
The web panel is your control center. Everything is accessible from the left sidebar.
At-a-glance view: total listings in database, new listings today, status changes today, last scrape time. Shows the newest listings and recent status changes (48h). Each listing row includes a source badge (H2S / OD / OC / XR).
Click Start monitor to begin scraping. The dot turns green when the monitor is running.
Browse all listings in the database. Filter by status, city, source (platform), type, max rent, min area, contract, energy, and more. Click any listing to open its detail page on the source platform.
A status may carry a small inferred badge, meaning we worked this status out ourselves — the platform did not report it. Platforms never announce that a unit is gone; they simply stop listing it. So a listing that disappears from the feed is marked Reserved after 30 minutes and Occupied after 2 hours (that is Holland2Stay's own payment deadline). The badge clears the moment the listing shows up again. Always confirm on the platform before acting — an inferred status is our best guess, not a fact.
Listings plotted on an interactive map. Green = available to book, orange = lottery, grey = not available. Markers show platform badges. Requires GOOGLE_MAPS_API_KEY.
The map only shows listings still seen recently — fourteen days by default, adjustable under Map age limit in Settings, or 0 to show everything. "Not available" is a terminal state, so those records stay in the database indefinitely; without this limit, units delisted months ago would remain pinned on the map.
Coordinates are resolved and cached automatically by the monitor — new listings usually appear on the map within half an hour. Admins can also trigger a pass by hand for debugging.
Month-by-month calendar showing move-in dates. Filter by city and platform.
Charts for 7/30/90-day ranges: new listings trend, status change trend, city & status distributions, rent distribution, platform distribution, listing drop time (NL time).
Go to Users → click a user → Notifications.
https://api.telegram.org/bot<TOKEN>/getUpdates to get your chat IDTwo ways; the first is the default.
1. Sent for you (default). Enter a recipient address — no SMTP server needed. You will receive a confirmation email and must click the link in it before delivery begins; that step exists so nobody can sign someone else's address up. The verification state is shown on the user's settings page, where the confirmation can also be re-sent.
2. Your own SMTP. Switch the email mode to custom, then fill in server details:
| Provider | SMTP Host | Port | Security |
|---|---|---|---|
| Gmail | smtp.gmail.com | 587 | STARTTLS |
| Outlook | smtp.office365.com | 587 | STARTTLS |
Use an App Password (not your regular password) if 2FA is enabled. Custom SMTP skips the address verification — delivery is your own server's business.
Enter the recipient's phone number (e.g. +31612345678) or Apple ID email. Only works when the monitor runs on macOS with Messages.app signed in.
Requires a Twilio account. Fill in Account SID, Auth Token, and the whatsapp:+... numbers.
If you use the FlatRadar iOS app, APNs push notifications are sent automatically. Notifications are bilingual — Chinese or English depending on your device language.
If you use the FlatRadar Android app, FCM push notifications are sent automatically. Same bilingual support as iOS.
The notifications switch in a user's settings governs every channel, device push included. Turn it off and the phone stops receiving push.
Granting push permission and signing in from the app turns that switch on for you — agreeing on the phone is agreeing, you do not have to go and click it again in the panel. Turning it off in the panel afterwards is what genuinely turns it off.
Go to Users → click a user → Auto Booking.
When an H2S listing becomes Available to book and matches your filters, the monitor will:
addNewBooking)placeOrder)idealCheckOut)It does NOT auto-pay. You receive the payment URL and complete payment manually.
OurDomain, OurCampus, Xior and Magis are notify-only, for two different reasons. For OurDomain and Xior the booking code is written and reCAPTCHA solving is wired up, but the flow has not been verified end to end, so it is not exposed to users. For OurCampus and Magis the booking flow has never been scouted at all.
Restrict which listings trigger notifications. Go to Users → click a user → Filters.
| Filter | Example | Applies to | How it works |
|---|---|---|---|
| Max rent | €1200 | All | Only listings ≤ €1200/month. Platforms quote different figures — see below |
| Min area | 25 m² | All | Only listings ≥ 25 m² |
| Min floor | 1 | All | Skip ground floor (0) |
| Cities | Eindhoven, Amsterdam | All | Multi-select — any match |
| Sources | H2S, OD | All | Multi-select — only notify for these platforms |
| Tenant | student only | Except Magis | Multi-select — any match |
| Type | Studio, 1 | Except Xior | Multi-select — any match |
| Occupancy | Single | Except Xior, Magis | Multi-select — any match |
| Furnishing | Furnished | Except OurCampus | Multi-select — any match |
| Contract | Indefinite | Holland2Stay only | Long-term only (skip short-stay) |
| Min energy label | B | Holland2Stay, Magis | Only listings rated at or above this label |
| Neighborhood | Centrum | Holland2Stay only | Multi-select; options follow the cities you picked |
| Offer type | Rent | Holland2Stay only | Multi-select — distinguishes rentals from other listing types |
All filters are AND together. Leave empty = no restriction.
The Applies to column matters: when a platform does not report a field, the filter is skipped for that platform entirely rather than excluding all of its listings. Setting "Min energy label: B" therefore does not mean everything you receive is rated B — Xior, OurDomain and OurCampus listings never went through that check. The panel labels each filter with the same scope.
The four platforms do not quote the same number. Read this before setting Max rent:
| Platform | Price shown in the panel and notifications |
|---|---|
| Holland2Stay | All-in — includes service costs and utility prepayment |
| Xior | All-in — base rent plus that building's registered monthly advance charges |
| OurDomain | Base rent; service costs charged separately |
| OurCampus | Base rent; service costs charged separately |
| Magis | All-in — base rent plus the service costs printed on each listing |
For OurDomain and OurCampus, service costs vary by floor plan and the upstream feed does not say which unit belongs to which plan, so an all-in figure cannot be derived. Those listings carry an asterisk beside the price; hovering shows that building's service-cost range, and the rent line in notifications carries the same note.
Go to Settings (admin only).
| Setting | Default | What it does |
|---|---|---|
| Check interval | 300s (5 min) | Normal polling interval outside peak hours |
| Peak max interval | 60s | Starting interval during peak (08:30–10:00 NL) |
| Peak min interval | 15s | Floor — adaptive polling never goes below this |
| Peak hours | 08:30–10:00, 13:30–15:00 | When to accelerate (NL time, weekdays only) |
| Jitter ratio | 0.20 | ±20% random variation to avoid detection |
| Heartbeat interval | 60 min | How often the admin gets a liveness notification; 0 disables it |
| Map age limit | 14 days | Map shows only listings still seen within this many days; 0 shows everything |
Enable or disable Holland2Stay, OurDomain, OurCampus and Xior independently. For each platform, select which cities or buildings to monitor. Xior buildings are grouped by city.
After saving, click Apply Now to hot-reload — no restart needed.
Click Start monitor on the Dashboard. If it doesn't start, check Log Viewer or docker compose logs h2s.
Start at the dashboard. A checklist appears there whenever the account cannot receive anything and names the missing step; if it is gone, the chain is intact. To check by hand:
If scraping is blocked by Cloudflare WAF:
HTTPS_PROXY in .env