# 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, 83.3% 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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

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

Get current room/studio/apartment rental listings from independent Dutch rental offices in Groningen,
Utrecht and Nijmegen — 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),
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 and Nijmegen."
- "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 and Nijmegen**, selectable per run.
- 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** — 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 sources that keep showing them after the fact
  (Gout Beheer, K\&P Makelaars, Van der Meulen) — 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.
- 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`. Default: all 3. |
| `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). Default: all 19. |
| `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 19 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 19 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 3 cities at once. The 15 Groningen offices are the
highest-confidence independent offices identified so far for that city out of a much longer candidate list —
one Tier-1 candidate (Groningse Panden Beheer) and several Tier-2 candidates 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), 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 3 national franchises deliberately don't publish the exact postal code/house number (a lead-gen
choice by those offices). The independent Groningen 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: 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, 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.*

### Keywords

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

### Changelog

#### 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). All others 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"
  ],
  "bronnen": [
    "123wonen",
    "rotsvast",
    "viadaan",
    "househunting",
    "gruno",
    "bulten",
    "dcwonen",
    "goutbeheer",
    "kpmakelaars",
    "vandermeulen",
    "solideverhuur",
    "vijftigvastgoed",
    "uwthuis",
    "desegno",
    "lucasklamer",
    "bens",
    "iidam",
    "novavastgoed",
    "riantmakelaars"
  ]
}
```

# 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/bLDxugzxUg1kQgKDP/openapi.json
