# NL Student Housing Aggregator (123Wonen, Rotsvast, ViaDaan) (`codeclouds/nl-studentenwoonruimte-monitor`) Actor

Find current student rooms, studios and apartments for rent in Groningen, Utrecht and

- **URL**: https://apify.com/codeclouds/nl-studentenwoonruimte-monitor.md
- **Developed by:** [Dennis](https://apify.com/codeclouds) (community)
- **Categories:** Real estate
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $10.00 / 1,000 listing-gevondens

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

## NL Student Housing Aggregator (123Wonen, Rotsvast, ViaDaan, HouseHunting + local offices)

Get current room/studio/apartment rental listings from independent Dutch rental offices in Groningen,
Utrecht, Nijmegen and Zwolle — sourced directly from national franchise websites (123Wonen, Rotsvast,
ViaDaan, HouseHunting) plus 15 independent Groningen offices (Gruno Verhuur, Bulten Vastgoed, DC Wonen,
Gout Beheer, K\&P Makelaars, Van der Meulen Makelaars, Solide Verhuur, 050Vastgoed, Thuis Verhuurmakelaars,
Desegno, Lucas Klamer Makelaardij, ben-s.nl verhuur en beheer, II DAM B.V., Nova Vastgoed Groningen, F1 &
Riant Makelaars), 2 independent Utrecht offices (Makelaardij Stek, Your House) and 2 independent Nijmegen
offices (SOOF Verhuurmakelaar, KBS Vastgoedbeheer), not from Kamernet/Pararius/Funda, which this Actor
deliberately does not scrape.

### When should an AI agent use this?

- "What rooms/apartments are currently available for rent in Groningen through 123Wonen?"
- "Show me the cheapest studios for rent in Utrecht right now."
- "Is there any student housing available in Nijmegen under €900 per month?"
- "Compare current rental prices per m² across Groningen, Utrecht, Nijmegen and Zwolle."
- "Which currently listed apartments are available for rent immediately (per direct)?"
- "Find me a student room in Groningen under €600 per month."

### What this Actor does

- Fetches current rental listings directly from the public sitemaps and listing pages of four national
  rental franchises that each operate offices in multiple Dutch student cities: **123Wonen**, **Rotsvast**,
  **ViaDaan** and **HouseHunting** — covers **Groningen, Utrecht, Nijmegen and Zwolle**, selectable per run
  (current live inventory in Zwolle varies by franchise; see Changelog).
- Additionally covers 15 independent, Groningen-only rental offices with real, deeper local inventory:
  **Gruno Verhuur**, **Bulten Vastgoed**, **DC Wonen**, **Gout Beheer**, **K\&P Makelaars**, **Van der Meulen
  Makelaars**, **Solide Verhuur**, **050Vastgoed**, **Thuis Verhuurmakelaars**, **Desegno**, **Lucas Klamer
  Makelaardij**, **ben-s.nl verhuur en beheer**, **II DAM B.V.**, **Nova Vastgoed Groningen** and **F1 & Riant
  Makelaars**, plus 2 independent, Utrecht-only offices (**Makelaardij Stek**, **Your House**) and 2
  independent, Nijmegen-only offices (**SOOF Verhuurmakelaar**, **KBS Vastgoedbeheer**) — each a separate,
  custom-built website with no shared structure, so each gets its own adapter (see Changelog). 4 of these
  (Gout Beheer, K\&P Makelaars, Van der Meulen, Solide Verhuur) require rendering the page in a headless
  browser to read the listings, so runs including them are slower per listing than the sitemap-based sources.
- Normalizes wildly different underlying data (every source runs its own, unrelated website/CMS) into one
  consistent output schema: address, price, surface area, bedrooms, availability, furnished status.
- Skips listings already marked as rented ("Verhuurd") on every source that keeps showing them after the
  fact — not useful for someone looking for a current vacancy.
- Explicitly does **not** scrape Kamernet, Pararius, Funda or Huurwoningen.nl (the large listing platforms) —
  those are used by independent offices only as one of several places to list, and have their own
  restrictions on automated access. This Actor goes directly to the offices' own public listings instead.
  For the same reason, some researched candidate offices were deliberately rejected rather than added — e.g.
  an agency whose own listing widget turned out to be powered by Pararius's own broker software (its listing
  data was served straight from a Pararius CDN), which would have meant re-scraping Pararius through a
  side door instead of adding genuinely independent inventory (see Changelog).
- Skips large institutional/waitlist housing providers (e.g. SSH&, Idealis, Lefier) and purpose-built student
  accommodation operators (e.g. Xior, Holland2Stay) — those operate a closed waitlist/lottery model, not a
  free-market listing.

### Input

| Field | Type | Description |
|---|---|---|
| `steden` | string\[] | Which cities to check: `groningen`, `utrecht`, `nijmegen`, `zwolle`. Default: all 4. |
| `bronnen` | string\[] | Which sources to check: `123wonen`, `rotsvast`, `viadaan`, `househunting` (national, any city); `gruno`, `bulten`, `dcwonen`, `goutbeheer`, `kpmakelaars`, `vandermeulen`, `solideverhuur`, `vijftigvastgoed`, `uwthuis`, `desegno`, `lucasklamer`, `bens`, `iidam`, `novavastgoed`, `riantmakelaars` (independent Groningen-only offices, ignored for other cities — `goutbeheer`/`kpmakelaars`/`vandermeulen`/`solideverhuur` are slower, browser-rendered sources); `stek`, `yourhouse` (independent Utrecht-only offices, ignored for other cities); `soof`, `kbs` (independent Nijmegen-only offices, ignored for other cities). Default: all 23. |
| `maxPerBron` | integer | Optional cap on listings fetched per source per city — useful for a cheap test run. Leave empty for no limit. |
| `nieuwSinds` | string (`YYYY-MM-DD`) | Optional: only include listings this Actor first saw on or after this date. Sources don't publish a "posted on" date, so this Actor tracks each listing's first-seen date itself, persisted across runs. The very first run (no history yet) always shows everything regardless of this setting. |
| `maxPrijs` | integer | Optional: only include listings priced at or below this amount (€/month). Listings with no stated price are excluded when this is set — never a guess. |
| `minOppervlakte` | integer | Optional: only include listings sized at or above this many m². Listings with no stated size are excluded when this is set, same reasoning as `maxPrijs`. |
| `typen` | string\[] | Optional: only include these property types: `kamer`, `studio`, `appartement`, `onbekend`. Leave empty for all types. |

### Output

One flat JSON record per listing found:

```json
{
  "stad": "groningen",
  "kantoor": "Gruno Verhuur",
  "bron": "gruno",
  "bron_url": "https://www.grunoverhuur.nl/",
  "titel": "studentenkamer aan de Paterswoldseweg",
  "type": "kamer",
  "adres": "Paterswoldseweg 96",
  "postcode": "9727 BG",
  "plaats": "Groningen",
  "prijs_pm": 700,
  "oppervlakte_m2": 15,
  "aantal_kamers": null,
  "beschikbaar_vanaf": null,
  "gemeubileerd": null,
  "url": "https://www.grunoverhuur.nl/woningaanbod/huur/groningen/paterswoldseweg/96",
  "opgehaald_op": "2026-07-20T16:31:02.486Z",
  "eerst_gezien_op": "2026-07-20"
}
```

Note: for the 4 national franchises (123Wonen/Rotsvast/ViaDaan/HouseHunting), `postcode` is consistently
`null` — none of them publish the exact postal code or house number on the public listing page (a common
lead-generation practice, so interested renters have to make contact). The independent Groningen offices
generally **do**
publish the full postcode. `beschikbaar_vanaf`, `gemeubileerd` and `aantal_kamers` are `null` whenever the
source page doesn't state them clearly — this Actor never guesses; a `null` means "not stated", not
"no"/"unfurnished"/"zero". A price is also `null` (never `0`) when a source shows no real price.

`opgehaald_op` is always the timestamp of the current run — it does **not** indicate how long a listing has
been available. `eerst_gezien_op` (date only) is the first date this Actor itself observed this exact
listing URL, tracked in a persistent store across runs — use `nieuwSinds` in the input to filter on it.

### Use cases

- Students/renters checking current availability across multiple independent offices without visiting each
  one's website separately.
- Market research on rental price/m² trends across student cities.
- Feeding a housing search agent or notification workflow with fresh listings.

### Legal

Sourced from each source's own public sitemap and listing pages. robots.txt was checked for all 23 sources
and none disallow the listing/object paths used here. Only publicly listed rental information is collected
(address street, price, size, availability) — no personal data, no agent/employee names or photos. Kamernet,
Pararius, Funda and Huurwoningen.nl are deliberately excluded (large platforms, own access restrictions) —
Pararius was used only during research to identify which independent offices exist, never as a scrape target
for this Actor.

### FAQ

**Q: Why these 23 sources, and not every independent office per city?**
A: 123Wonen/Rotsvast/ViaDaan/HouseHunting are the franchises confirmed to run on a single national website
covering multiple cities, so one source reliably covers all 4 cities at once (current live inventory in
Zwolle varies by franchise — see Changelog). The 15 Groningen offices, 2 Utrecht offices and 2 Nijmegen offices
are the highest-confidence independent offices identified so far for those cities out of a much longer
candidate list (built from an earlier Pararius-based longlist per city, revisited and technically verified
office by office) — one Tier-1 candidate (Groningse Panden Beheer), several Groningen Tier-2 candidates, and
several Utrecht/Nijmegen/Zwolle candidates researched across this Actor's expansion rounds were investigated
in depth but turned out to have no publicly browsable listings, a wrong business model (short-stay,
referral-only to Kamernet/Pararius/Funda, sales-dominated with near-zero current rental inventory, or a
Pararius-Office-powered backend that would have meant re-scraping Pararius through a side door), or a
technical block that a normal browser header doesn't solve — not included; see Changelog for the full
accounting. Dozens of other small independent offices still exist per city, each on its own unique,
custom-built website with no shared structure — adding more is possible but requires a dedicated adapter per
office, an ongoing expansion.

**Q: Why is `postcode` empty for some sources but not others?**
A: The 4 national franchises deliberately don't publish the exact postal code/house number (a lead-gen
choice by those offices). The independent Groningen, Utrecht and Nijmegen offices generally do publish it —
this Actor reports exactly what each source makes public, nothing more.

**Q: Does this include Kamernet/Pararius/Funda listings?**
A: No, and it never will by design — those are large platforms with their own access restrictions, and this
Actor exists specifically to surface listings from independent offices instead.

**Q: Why do some runs return few or zero ViaDaan/Gout Beheer results?**
A: Both are small offices — they may simply have no current listings for the requested city right now. Gout
Beheer's currently listed objects have all been observed marked "Verhuurd" (already rented) at time of
writing — this Actor correctly skips those rather than reporting stale vacancies, so 0 results on a given day
is expected behavior, not a bug.

**Q: Why do Rotsvast/ViaDaan/HouseHunting sometimes return 0 Zwolle results while 123Wonen returns several?**
A: All 4 franchises' scraping mechanism works identically regardless of city — but at the time Zwolle
coverage was added, only 123Wonen had current Zwolle listings in its sitemap; the other 3 mechanically
support Zwolle (no code change needed if/when they add local inventory there) but had none live yet. Worth
re-checking periodically rather than a sign anything is broken.

**Q: How do I get only new listings since my last check?**
A: Set `nieuwSinds` to a date (`YYYY-MM-DD`). None of the underlying sources publish a "posted on" date, so
this Actor tracks each listing URL's first-seen date itself in a persistent store shared across runs, and
filters on that. The very first run you do always shows everything (there's no history yet) and records
today as the first-seen date for every listing found — from the next run onwards, `nieuwSinds` lets you skip
listings you've already seen before that date.

**Q: How do I narrow results by price, size or type?**
A: Use `maxPrijs`, `minOppervlakte` and/or `typen`. All three filter on the already-normalized fields, so
they work the same way across every source. A listing missing the filtered field (e.g. no stated price) is
excluded rather than guessed at when that filter is active.

**Q: Why do some runs take noticeably longer than others?**
A: 5 sources (Gout Beheer, K\&P Makelaars, Van der Meulen Makelaars, Solide Verhuur, ViaDaan) require rendering
pages in a headless browser to read their listings — 4 because the listing data itself is client-side
rendered, and ViaDaan because reading its full national listing (not just the first page) requires
repeatedly triggering its "load more" component. Excluding those 5 from `bronnen` gives a faster run using
only the sitemap-based sources.

***

*Zoekwoorden: studentenkamer huren Groningen, kamer te huur, studio te huur, appartement huren Groningen,
Utrecht, Nijmegen, Zwolle, 123Wonen aanbod, Rotsvast huurwoningen, ViaDaan aanbod, HouseHunting Groningen,
Gruno Verhuur, Bulten Vastgoed, DC Wonen, Gout Beheer, K\&P Makelaars, Van der Meulen Makelaars, Solide
Verhuur, Makelaardij Stek, Your House Utrecht, SOOF Verhuurmakelaar Nijmegen, KBS Vastgoedbeheer.*

### Keywords

netherlands, student-housing, rental, huurwoning, studentenkamer, groningen, utrecht, nijmegen, zwolle,
123wonen, rotsvast, viadaan, gruno-verhuur, real-estate, rooms-for-rent

### Changelog

#### 0.12.0

- Added **KBS Vastgoedbeheer**, a second independent Nijmegen office (also covers Arnhem/Mook/Malden, filtered
  to Nijmegen-only listings by URL suffix). Different underlying platform than the other new sources this
  round — a GenerateBlocks-WordPress theme with explicitly labeled fields ("Status", "Type", "Oppervlak"),
  the most cleanly structured source in the whole Actor so far. Two format quirks handled: prices use a
  period as the decimal separator ("€617.00" means 617, not 61,700 — the existing price parser assumes Dutch
  comma-decimal formatting, so this needed a dedicated parser) and "Kamer nummer `<N>`" is a room ID within a
  shared house, not a bedroom count (deliberately left out of `aantal_kamers`). No sitemap for its listings
  (only static pages), so discovery paginates through its `/aanbod/` index instead — the only source in this
  Actor that needs more than one index page.
- Investigated and rejected **Numaga Vastgoed** (Nijmegen): despite ranking as the strongest not-yet-built
  candidate from the earlier research, its site turned out to have no publicly browsable listing pages at
  all — just a registration/contact funnel, the same pattern that got Domica rejected in 0.10.0.

#### 0.11.0

- Added **Your House**, a second independent Utrecht office (same shared vastgoedsoftware vendor as Stek,
  a different theme variant — `h1.object_title`/`.object_details` instead of `h1.obj_address`/
  `.object_characteristics`). Confirmed 19 live Utrecht listings on first run, more than doubling
  independent-office volume for that city.
- Added **SOOF Verhuurmakelaar**, the first independent, Nijmegen-only rental office (same vendor again, a
  third theme variant with per-feature structured elements — `.object__header` +
  `.objectheader__content-features`).
- Both new offices were found via two pre-existing but never-acted-on research documents
  (`docs/research-inbox/processed/{utrecht,nijmegen}-studentenwoonruimte-onderzoek.md`, from an earlier
  4-city longlist exercise) rather than fresh research — a reminder to check for that first before starting
  from scratch on a new city.
- Fixed a cross-cutting `beschikbaar_vanaf` gap affecting the whole shared-vendor source family (Gruno
  Verhuur, 050Vastgoed, Thuis Verhuurmakelaars, Desegno, Stek, and now Your House/SOOF): these sites label
  the move-in date "Aanvaarding" rather than "Beschikbaar (vanaf)", which the date parser didn't recognize —
  e.g. "Aanvaarding Direct" always came out `null` even though the equivalent "Beschikbaarheid Per direct"
  phrasing already worked for 123Wonen. `beschikbaar_vanaf` is now populated correctly for this phrasing too.
- Removed a duplicated sitemap-discovery helper: `runSitemapKantoor` used to hardcode `stad: 'groningen'`
  (a leftover from when every user of it was a Groningen office); it now takes an explicit `stad` parameter,
  so Stek/Your House/SOOF share the same helper as the Groningen sources instead of Stek hand-rolling its own
  copy of the same logic.

#### 0.10.1

- Fixed a monetization gap: listings were pushed to the dataset and charged as two separate calls
  (`Actor.pushData()` then `Actor.charge()`). `Actor.charge()` does not throw when the run's charge budget
  (`maxTotalChargeUsd`) is exhausted — it silently returns `chargedCount: 0` — so once a caller-set budget cap
  was hit, every subsequent listing across all sources was still delivered to the dataset for free, with no
  visibility anywhere. Now uses the SDK's combined `Actor.pushData(item, eventName)` form, which checks the
  remaining budget before pushing; a listing that couldn't be charged is now tracked separately
  (`overgeslagenDoorBudget` in the internal run summary) instead of silently counted as a normal delivery.
- Fixed a crash risk: ViaDaan's per-listing loop was the only one of the 20 sources with no per-item
  error handling — an exception there (e.g. a transient delivery failure) would crash the entire run instead
  of just that one listing, losing the rest of that run's results. Now wrapped in the same try/catch +
  failure-counting pattern used by every other source.
- Removed a duplicated `cardMatchesStad` helper (an unused copy lived in `lib.ts`, exercised only by tests,
  while the actual runtime code kept its own separate copy in `scrapers.ts`) — now a single implementation.

#### 0.10.0

- Added **Zwolle** as a 4th city. The 4 national franchises (123Wonen/Rotsvast/ViaDaan/HouseHunting) needed
  no new code — their mechanism already works for any city; at the time of this release only 123Wonen had
  live Zwolle inventory in its sitemap (~30 listings), the other 3 had none yet but will pick it up
  automatically once they do.
- Added **Makelaardij Stek**, the first independent, Utrecht-only rental office (same shared vastgoedsoftware
  vendor as Gruno Verhuur/050Vastgoed/Thuis Verhuurmakelaars/Desegno — `sitemap_listings_res_rent.xml` +
  `.object_data`/`.object_characteristics`/`h1.obj_address` — confirming that vendor isn't Groningen-specific).
- Researched and deliberately **rejected/deferred** 4 candidate Zwolle offices found during this expansion:
  an agency whose listing widget turned out to be powered directly by Pararius's own broker software/CDN
  (would have meant re-scraping Pararius through a side door — conflicts with this Actor's core "not
  Pararius" positioning); a general real-estate office whose "rental" sitemap turned out to be ~90% sold
  properties with only 2 actual rentals, both already rented out at time of research; an office with a clean,
  reliable `/huren/<stad>/` URL pattern but only 5 total Zwolle rental listings, 4 already rented (mechanism
  confirmed to work — worth re-probing later if inventory grows, same as several previously-deferred
  Groningen candidates); and one site that returned a persistent server error and couldn't be evaluated.

#### 0.9.1

- Fixed a data-quality gap: 7 of the 19 sources (123Wonen, Rotsvast, ViaDaan, HouseHunting, Gruno Verhuur,
  Bulten Vastgoed, DC Wonen — the earliest sources added, before the "already rented" check existed) did not
  skip listings the source itself already marks as rented/unavailable, unlike the other 12. All 19 sources now
  consistently return `null` (skip, don't charge) for an already-rented listing.

#### 0.8.0

- Added `maxPrijs`, `minOppervlakte` and `typen` input filters, so a run can return only listings that
  actually match what you're looking for instead of everything found. A listing missing the filtered field
  is excluded rather than guessed at.
- Fixed ViaDaan's long-standing pagination gap: previously only the first ~10 of its ~64 national listings
  were read (the rest loaded via a Livewire "infinite scroll" component with no page-number URLs to follow).
  ViaDaan now runs through a headless browser and repeatedly triggers the underlying Livewire component
  method directly (`$wire.loadMore()`) until no new listings appear — scroll-event simulation didn't reliably
  trigger the site's IntersectionObserver in headless Chromium, but calling the component method directly
  does and is faster besides.

#### 0.7.0

- Added a `nieuwSinds` input setting (`YYYY-MM-DD`) to only return listings first seen by this Actor on or
  after a given date. None of the 19 sources publish a "posted on" date themselves, so this Actor now tracks
  each listing URL's first-seen date in a persistent, cross-run key-value store and adds it to every output
  record as `eerst_gezien_op`. The first run you do always shows everything (no history exists yet) and
  establishes the baseline; subsequent runs can filter on it.

#### 0.6.0

- Added 3 more independent Groningen-only offices from a deeper follow-up investigation of the ~15 Tier-2
  candidates left over from 0.5.0: **II DAM B.V.**, **Nova Vastgoed Groningen** and **F1 & Riant Makelaars**.
  All 3 are plain-HTTP sources (no headless browser needed).
- Of the 12 candidates actually investigated in depth this round, 9 were confirmed not usable and excluded:
  wrong business model (Schove Groep/Schovast and Confides Verhuur only refer to Funda/Pararius/Kamernet
  instead of hosting their own listings; Overnachteninstijl.nl is confirmed short-stay/holiday rental, not
  regular housing), technical blocks (Stadeo Vastgoed renders price/size/rooms through a client-side widget
  with no static fallback; KD Vastgoed returns HTTP 403 regardless of User-Agent, likely an IP/WAF block, not
  solved by browser rendering), or currently-empty inventory despite a working mechanism (Bakker Real Estate,
  Reinders Makelaars, Midas Vastgoed, PROPRIETAS Vastgoedmanagement) — see PROJECTINFORMATIE.txt for the full
  per-office accounting.
- F1 & Riant Makelaars' own "rental listings" page currently mixes already-rented listings (struck-through
  price) with for-sale listings ("k.k.") — both are explicitly skipped, so 0 results for this source is
  expected at the time of writing, not a bug (comparable to the existing Gout Beheer behavior).

#### 0.5.0

- Added 5 more independent Groningen-only offices found via a "Tier 2" triage of ~39 smaller candidate
  offices (4 parallel research passes): **050Vastgoed**, **Thuis Verhuurmakelaars**, **Desegno**, **Lucas
  Klamer Makelaardij** and **ben-s.nl verhuur en beheer**. All 5 are plain-HTTP sources (no headless browser
  needed). Gruno Verhuur, 050Vastgoed, Thuis Verhuurmakelaars and Desegno were found to share the same
  sitemap naming convention (`sitemap_listings_res_rent.xml`, likely a common real-estate software vendor)
  even though each uses different page markup — only the listing-discovery logic could be shared, not the
  data-extraction itself.
- Triaged ~39 smaller candidate offices; confirmed several as not usable for this Actor: some only list via
  Kamernet/Pararius/Funda (already excluded platforms), some require a login portal, one ("Ad Hoc Groningen")
  turned out to be the same nationwide anti-squatting/vacancy-management chain already excluded elsewhere,
  and a few (Nijestee, Xior Student Housing) operate a waitlist/institutional booking model rather than
  free-market listings.
- Fixed a surface-area bug on Desegno: some listings show both plot size ("perceeloppervlakte") and actual
  living space ("gebruiksoppervlakte") in that order — the parser previously picked the larger plot size
  instead of the living space.

#### 0.4.0

- Added a 4th national franchise: **HouseHunting**, with a Groningen office (also active nationally, but
  only verified/enabled for Groningen so far). Uses `?filter_location=<stad>` on its listing index, which
  doesn't appear to filter the total result count server-side but does surface the requested city on the
  first page — results are filtered client-side by matching the city name in the listing URL.
- Investigated one more Tier-1 Groningen candidate, **Groningse Panden Beheer** — found no publicly
  browsable listings on their site at all (no sitemap, no listing links, no data-loading network request
  even after rendering the page) despite being one of the largest managed portfolios in the original
  research. Not included; documented as a dead end, not a bug.
- Fixed `parseBeschikbaarVanaf` missing dates where the "vanaf"/"per" label appears before a colon (e.g.
  HouseHunting's "Beschikbaar per: 01-12-2023") — previously only handled the label appearing after any
  colon/whitespace, not before.

#### 0.3.0

- Added 4 more independent Groningen-only offices: Gout Beheer, K\&P Makelaars, Van der Meulen Makelaars and
  Solide Verhuur. These 4 require a headless browser (Playwright) to read their client-side-rendered
  listings — the first browser-based source in this Actor, alongside the existing plain-HTTP sources.
- Added automatic skipping of listings already marked "Verhuurd" (rented) on sources that keep showing them.
- Fixed prices being reported as `0` instead of `null` when a source shows a broken/placeholder price for a
  no-longer-active listing (seen on Solide Verhuur).
- Fixed surface area parsing for decimal values (e.g. "94.00 m²") that were previously truncated to the
  decimal part only (e.g. read as 0 instead of 94).

#### 0.2.0

- Added 3 independent Groningen-only offices: Gruno Verhuur, Bulten Vastgoed and DC Wonen — each a
  custom-built website with its own adapter (no shared CMS between them or with the franchises).
- Fixed a Gruno Verhuur sitemap bug: category/filter pages (e.g. `.../type-appartement`) were mixed into the
  listings sitemap and misread as individual objects, producing a duplicate. Now filtered out.

#### 0.1.0

- Initial release: 123Wonen, Rotsvast and ViaDaan for Groningen, Utrecht and Nijmegen. ViaDaan reads only the
  first page of its national listing index (no pagination yet) — a known, documented limitation, not a bug.

# Actor input Schema

## `steden` (type: `array`):

Which cities to check. Each franchise runs its own listing search per city.

## `bronnen` (type: `array`):

Which sources to check. 123Wonen/Rotsvast/ViaDaan/HouseHunting are national franchises (work for any selected city, though current inventory in Zwolle varies by franchise). Stek/Your House are Utrecht-only, SOOF/KBS Vastgoedbeheer are Nijmegen-only; all remaining sources are independent Groningen-only offices (ignored for other cities). Gout Beheer/K\&P Makelaars/Van der Meulen/Solide Verhuur (Groningen only) and ViaDaan (national, needed for its infinite-scroll pagination) require a headless browser and are slower per listing.

## `maxPerBron` (type: `integer`):

Optional cap on how many listings to fetch per source per city — useful for a cheap test run. Leave empty for no limit.

## `nieuwSinds` (type: `string`):

Optional: only include listings this Actor first saw on or after this date (YYYY-MM-DD). Sources don't publish a "posted on" date, so this Actor tracks per-listing first-seen dates itself, persisted across runs. The very first run (no prior history yet) always shows everything regardless of this setting.

## `maxPrijs` (type: `integer`):

Optional: only include listings with a stated price at or below this amount (€/month). Listings with no stated price are excluded when this is set — we can't confirm they fit, so we'd rather skip than guess.

## `minOppervlakte` (type: `integer`):

Optional: only include listings with a stated surface area at or above this size (m²). Listings with no stated surface area are excluded when this is set, for the same reason as maxPrijs.

## `typen` (type: `array`):

Optional: only include these property types. Leave empty for all types. "Unknown" means the source page didn't clearly state a type.

## Actor input object example

```json
{
  "steden": [
    "groningen",
    "utrecht",
    "nijmegen",
    "zwolle"
  ],
  "bronnen": [
    "123wonen",
    "rotsvast",
    "viadaan",
    "househunting",
    "gruno",
    "bulten",
    "dcwonen",
    "goutbeheer",
    "kpmakelaars",
    "vandermeulen",
    "solideverhuur",
    "vijftigvastgoed",
    "uwthuis",
    "desegno",
    "lucasklamer",
    "bens",
    "iidam",
    "novavastgoed",
    "riantmakelaars",
    "stek",
    "yourhouse",
    "soof",
    "kbs"
  ]
}
```

# Actor output Schema

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

Alle aanbod-records in het default dataset.

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("codeclouds/nl-studentenwoonruimte-monitor").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 = {}

# Run the Actor and wait for it to finish
run = client.actor("codeclouds/nl-studentenwoonruimte-monitor").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 '{}' |
apify call codeclouds/nl-studentenwoonruimte-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,codeclouds/nl-studentenwoonruimte-monitor"
        }
    }
}
```

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/gGe0gUCpOP8wBCgar/builds/IRFD2mCa0waGSCado/openapi.json
