303.489 datasets geïndexeerd1.419 uitgevers
Open Data IndexDe index van open data over Nederland
Open Data Index›Bouwen›Dashboard asielopvang: landelijk en per gemeente
DashboardOm te beginnenBevolkingWonen en ruimte

Dashboard asielopvang: landelijk en per gemeente

Een dashboard dat laat zien hoeveel mensen er in de COA-opvang wonen sinds 2016, hoeveel er binnenkomen en vertrekken, en hoeveel opvangplaatsen elke gemeente heeft, ook per 1.000 inwoners.

Je AI-assistentOpen Data Index MCP aan
De MCP-server van Open Data Index staat aan in je chat. Zo leest de assistent dit project en de bronnen zelf.
Jij plakt de prompt van dit project
De assistent haalt op bij Open Data Index
  • geef_project("asielopvang-dashboard")
  • geef_bron("coa-opvang")
  • geef_bron("coa-locaties")
  • geef_bron("cbs-bevolking-maand")
De assistent

Ik heb het project, de geteste aanroepen en de valkuilen. Ik bouw een dashboard met Asielopvang in cijfers (COA), COA-opvanglocaties en Bevolkingsontwikkeling per maand. Zo kan het eruitzien:

Dit bouw je zelf. Kopieer de prompt naar Claude, ChatGPT of Cursor, met de MCP-server aan, of volg de stappen hieronder.

Volg de stappen
Opvang asielzoekersMaandrapport 09/2026
01  Bezetting COA-opvang
83.150

bewoners op COA-locaties, september 2026. Een jaar eerder: 76.770. Laagste punt: 20.010 (juni 2018).

20.000
40.000
60.000
80.000
20.010
201620182020202220242026
0218.300vergunning­houders, wachten op een woning
034.190alleenstaande minderjarigen
0436.570instroom, jan–sep 2026
0533.570uitstroom, jan–sep 2026
06  Opvangplaatsen per 1.000 inwoners
GemeentePlaatsenPer 1.000
Westerwolde2.00079,8
Cranendonck1.50070,7
Gilze en Rijen1.20042,4
Wassenaar93033,0

Nederland: 3,8 per 1.000 · 68.779 plaatsen in 160 gemeenten

Bron: Min. van Asiel en Migratie (Migratieketen, CC0); COA-locatiepagina's3
Mockup

Dit is een mockup, geen werkende versie, met de cijfers van het ministerie van Asiel en Migratie (bezetting tot en met september 2026) en de COA-locatiepagina's, zoals opgehaald in onze proefdraai van 16 oktober 2026. De MCP-server zet je aan met het adres op Voor AI.

Voor wie

Raadsleden en griffies die het debat over opvang en de spreidingswet met cijfers willen voeren, lokale journalisten, factcheckers en iedereen die wil weten hoeveel opvang er in de eigen gemeente is, naast het landelijke beeld.

Wat je krijgt

Een pagina met drie delen, getest op 16 oktober 2026:

- **Landelijk door de tijd**: de bezetting van de COA-opvang per maand sinds januari 2016 (129 maanden). In september 2026 woonden er 83.150 mensen in de opvang, tegen 76.770 een jaar eerder en 20.010 op het laagste punt in juni 2018. Van hen hadden er 18.300 al een verblijfsvergunning (ze wachten op een woning) en 4.190 waren alleenstaande minderjarigen. Syrië is veruit het grootste herkomstland (28.190 bewoners), daarna Eritrea (5.760). - **Instroom en uitstroom**: van januari tot en met september 2026 kwamen er 36.570 mensen in de opvang en vertrokken er 33.570 (in heel 2025: 51.660 in, 44.730 uit). - **Per gemeente**: 261 locatiepagina's van het COA, 251 met een capaciteit, samen 68.779 opvangplaatsen in 160 gemeenten. Gekoppeld aan het inwonertal (CBS, begin augustus 2026) is dat landelijk 3,8 plaatsen per 1.000 inwoners. Westerwolde (Ter Apel) staat bovenaan met 79,8 per 1.000, daarna Cranendonck (70,7) en Gilze en Rijen (42,4). Zuid-Holland (13.086) en Noord-Holland (13.055) hebben de meeste plaatsen, Zeeland de minste (1.871). 182 gemeenten hebben geen COA-locatie met een eigen pagina.

Let op het verschil: de capaciteit op de locatiepagina's (68.779) is lager dan het aantal bewoners (83.150). Niet elke locatie heeft een pagina (het COA noemt er zelf 329) en een capaciteit is geen bezetting.

Bewoners van COA-opvanglocaties: 83.150 bewoners in september 2026, 77% meer dan in januari 2016

Bewoners van COA-opvanglocaties, januari 2016 – september 2026, in bewoners. Het hoogste punt was juni 2026 (83.420).

Bron: Ministerie van Asiel en Migratie, Migratieketen: COA-opvang (data.overheid.nl, CC0) · opgehaald 25 oktober 2026 · selectie: Alle bewoners van COA-locaties, ook vergunninghouders; Aantallen afgerond op tientallenOpen de tabel Los blok

Zo zou je kunnen starten7 stappen

  1. 1

    Vind de jaarbestanden van de bezetting

    Het ministerie van Asiel en Migratie zet de COA-cijfers als CSV per jaar op data.overheid.nl (CC0). De links staan op de datasetpagina, ook in de schema.org-gegevens (DataDownload, contentUrl). Gebruik die pagina en niet de CKAN-API: robots.txt van data.overheid.nl verbiedt /data/, waar die API zit. Valkuil: 2016 tot en met 2023 staan in een andere map dan 2024 tot en met 2026, en 2020 heet '…2020_2.csv'. Sorteer dus op het jaartal in de bestandsnaam, niet op de hele URL.

    https://data.overheid.nl/dataset/immigratie-coa-opvang-en-onderdak-bezetting
    {
      "@context": "http://schema.org/",
      "@type": "DataDownload",
      "contentUrl": "https://data.overheid.nl/sites/default/files/dataset/98a8a44c-ddab-4748-b771-34243dd0e14b/resources/Asiel%20-%20COA%20opvang%20en%20onderdak%20bezetting%202026.csv",
      "name": "Asiel - COA Opvang En Onderdak Bezetting 2026",
      ...
            "license": "CC-0 (1.0)",
      "fileFormat":  "CSV",
  2. 2

    Lees de bezetting per maand

    Puntkomma's als scheidingsteken. Elke regel heeft een kolom 'Totaal van' die zegt welke uitsplitsing het is: 'Jaar, Maand' is het maandtotaal, 'Jaar, Maand, Vergunninghouder' splitst naar status, 'Jaar, Maand, Amv' telt alleenstaande minderjarigen. *Alle* betekent: alle waarden samen. Kies per getal één uitsplitsing, anders tel je dubbel. Valkuilen: de codering wisselt (2024 en 2025 UTF-8 met BOM, de andere jaren Windows-1252), de maand staat als '1' of als '01', en de kolom Amv bestaat pas sinds 2024. Neem de laatste kolom als aantal, want die heet in elk bestand anders.

    https://data.overheid.nl/sites/default/files/dataset/98a8a44c-ddab-4748-b771-34243dd0e14b/resources/Asiel - COA opvang en onderdak bezetting 2026.csv
    Nummer;Totaal van;Jaar;Maand;Opvangvorm;Verblijfsduur;Vergunninghouder;Land van herkomst;Amv;Aantal bezetting opvang
    1;Jaar, Maand;2026;1;*Alle*;*Alle*;*Alle*;*Alle*;*Alle*;81120
    1;Jaar, Maand;2026;2;*Alle*;*Alle*;*Alle*;*Alle*;*Alle*;81740
    ...
    1;Jaar, Maand;2026;9;*Alle*;*Alle*;*Alle*;*Alle*;*Alle*;83150
    11;Jaar, Maand, Vergunninghouder;2026;9;*Alle*;*Alle*;Geen Vergunninghouder;*Alle*;*Alle*;64850
    11;Jaar, Maand, Vergunninghouder;2026;9;*Alle*;*Alle*;Vergunninghouder;*Alle*;*Alle*;18300
    12;Jaar, Maand, Opvangvorm;2026;9;Gezinslocaties;*Alle*;*Alle*;*Alle*;*Alle*;550
    12;Jaar, Maand, Opvangvorm;2026;9;Opvang excl. onderdaklocaties;*Alle*;*Alle*;*Alle*;*Alle*;82460
    12;Jaar, Maand, Opvangvorm;2026;9;Vrijheidsbeperkende locatie;*Alle*;*Alle*;*Alle*;*Alle*;140
    14;Jaar, Maand, Amv;2026;9;*Alle*;*Alle*;*Alle*;*Alle*;Amv;4190
  3. 3

    Zet instroom en uitstroom ernaast

    Instroom en uitstroom zijn twee aparte datasets met dezelfde opbouw (immigratie-coa-opvang-en-onderdak-instroom en -uitstroom), maar met minder kolommen. Hier is er ook een regel 'Jaar' met het jaartotaal tot nu toe. Alles is afgerond op tientallen: de negen maanden van 2026 tellen op tot 36.590, de jaarregel zegt 36.570. Gebruik de jaarregel voor het jaar en de maandregels voor de grafiek, en tel nooit details op tot een totaal.

    https://data.overheid.nl/sites/default/files/dataset/32e0debc-306f-47be-9eb2-d98d9e14b89a/resources/Asiel - COA opvang en onderdak instroom 2026.csv
    Nummer;Totaal van;Jaar;Maand;Land van herkomst;Aantal ontvangen in opvang
    1;Jaar;2026;*Alle*;*Alle*;36570
    2;Jaar, Maand;2026;1;*Alle*;4190
    2;Jaar, Maand;2026;2;*Alle*;4140
    ...
    2;Jaar, Maand;2026;8;*Alle*;3220
    2;Jaar, Maand;2026;9;*Alle*;3630
  4. 4

    Loop de locaties af in onze index

    Het COA heeft geen bestand of API met locaties; elke locatie heeft alleen een pagina op coa.nl. Wij lezen die 261 pagina's elke maandag, zoeken het adres op in de BAG en leggen het in de gemeentegrenzen. Onze catalogus-API geeft de boom provincie, gemeente, locatie: zonder pad de provincies, met ?pad=/0 de gemeenten in Drenthe, met ?pad=/0/0 de locaties in Assen. De map 'Onbekend' bevat één locatie zonder bruikbaar adres. Zo hoef je coa.nl zelf niet 261 keer aan te roepen.

    https://www.opendataindex.nl/api/catalogus/coa-locaties?pad=/0/0
    {"pad":"/0/0","code":"Assen","titel":"Assen","n":1,"submappen":0,"kinderen":[],
     "items":[{"sleutel":"assen-schepersmaat","titel":"Assen - Schepersmaat","soort":"locatie",
               "gewijzigd":null,"records":null,"periode":null}],"meer":0}
  5. 5

    Lees de capaciteit per locatie

    Elke locatie heeft een datasetpagina bij ons met capaciteit, type, looptijd van de bestuursovereenkomst, adres en gemeente. De beschrijving begint altijd met 'Capaciteit: <getal>', ook in de meta-description, dus één regex is genoeg. Tien van de 261 pagina's noemen geen capaciteit: die tellen niet mee. De tekst komt letterlijk van coa.nl en kan verouderd zijn (Assen: 'loopt af op 1 april 2026', terwijl de locatie nog op de site staat).

    https://www.opendataindex.nl/dataset/coa-locaties/ter-apel-ter-apelervenen
    <meta name="description" content="Capaciteit: 2000 opvangplaatsen. Type locatie: centrale ontvangstlocatie (col), procesopvanglocatie (pol), alleenstaande minderjarige vreemdelingen (amv) en …"
    ...
    Capaciteit: 2000 opvangplaatsen. Type locatie: centrale ontvangstlocatie (col), procesopvanglocatie (pol), alleenstaande minderjarige vreemdelingen (amv) en vrijheidsbeperkende locatie (vbl).. De opvang bestaat sinds 1996. De huidige bestuursovereenkomst loopt tot 17 mei 2040. Adres: Ter Apelervenen 4 en 5, 9561 MC Ter Apel. Gemeente Westerwolde (Groningen).
  6. 6

    Of lees coa.nl zelf

    Wil je niet van onze index afhangen: de sitemap van coa.nl (sitemap.xml?page=1 en page=2) noemt elke locatiepagina onder /nl/locatie/, en robots.txt staat die toe. Elke URL staat twee keer in de sitemap: ontdubbel. Capaciteit, type en overeenkomst staan in blokjes (li class=infographic__item) in wisselende volgorde; zoek op de titel, niet op de plek. Het overzicht /nl/locaties gaf 403. Neem geen namen, telefoonnummers of e-mailadressen van medewerkers over. Gemeente uit het adres: BAG-WFS van PDOK op postcode en huisnummer, niet de PDOK Locatieserver (robots.txt van api.pdok.nl verbiedt die).

    https://www.coa.nl/nl/locatie/assen-schepersmaat
    <li class="infographic__item">
    <h4 class="infographic__itemTitleAlt">Capaciteit</h4>
    <figure class="infographic__itemIcon infographic__itemIcon--capacity">
    <svg …></svg>
    <span class="infographic__itemIconData">1000</span>
    </figure>
    <article class="infographic__itemText">
    <h4 class="infographic__itemTitle" aria-hidden="true">Capaciteit</h4>
    <p class="infographic__itemDescription">1000 opvangplaatsen</p>
    ...
    <h4 class="infographic__itemTitleAlt">Type locatie</h4>
    ...
    <p class="infographic__itemDescription">asielzoekerscentrum (azc)</p>
  7. 7

    Haal het inwonertal per gemeente op

    CBS-tabel 37230ned geeft per maand de bevolking aan het begin van de periode (maat M000098). Wij gebruiken de nieuwe OData v4 op datasets.cbs.nl: daar zijn codes niet met spaties aangevuld ('GM0363', niet 'GM0363 ') en op 16 oktober 2026 gaf de oude v3 (opendata.cbs.nl) een tijd lang 503. De lijst bevat ook opgeheven gemeenten met Value null: 556 regels, 342 met een waarde. Namen haal je uit RegioSCodes; haal ' (gemeente)' van het eind ('Utrecht (gemeente)', "'s-Gravenhage (gemeente)"), dan sluiten ze aan op de gemeentenamen in onze index (160 van 160). De nieuwste maanden zijn voorlopig.

    https://datasets.cbs.nl/odata/v1/CBS/37230ned/Observations?$filter=Measure eq 'M000098' and Perioden eq '2026MM08' and startswith(RegioS,'GM')&$select=RegioS,Value
    {"@odata.context":"https://datasets.cbs.nl/odata/v1/CBS/37230ned/$metadata#Observations(RegioS,Value)",
     "value":[{"Value":26178.0,"RegioS":"GM1680"},{"Value":null,"RegioS":"GM0738"},
              {"Value":33700.0,"RegioS":"GM0358"},{"Value":27718.0,"RegioS":"GM0197"},
              {"Value":null,"RegioS":"GM0480"},{"Value":null,"RegioS":"GM0305"},
              {"Value":28296.0,"RegioS":"GM0059"},{"Value":20212.0,"RegioS":"GM0482"},
              {"Value":26657.0,"RegioS":"GM0613"},{"Value":null,"RegioS":"GM0483"},
              {"Value":113291.0,"RegioS":"GM0361"}, ...

Hoe de bronnen samenkomen

  • Asielopvang in cijfers (COA)COA-opvanglocatiesvia landelijk totaal (bewoners tegen plaatsen)

    De maandcijfers tellen bewoners, maar alleen landelijk. De locatiepagina's geven de capaciteit per locatie en dus per gemeente. Zet ze naast elkaar als bezetting tegen capaciteit, maar deel ze niet door elkaar: niet elke locatie heeft een pagina (261 pagina's, het COA noemt 329 locaties) en de som van de capaciteit (68.779) is lager dan het aantal bewoners (83.150, september 2026).

  • COA-opvanglocatiesBevolkingsontwikkeling per maandvia gemeentenaam, daarna gemeentecode (GM1950)

    Onze index geeft per locatie de gemeente (indeling 2025). Koppel op naam aan RegioSCodes van CBS 37230ned na het weghalen van ' (gemeente)' en neem dan de GM-code mee; daarmee deel je de plaatsen door het inwonertal. Getest: alle 160 gemeenten met opvang vonden een inwonertal.

  • Asielopvang in cijfers (COA)Bevolkingsontwikkeling per maandvia maand (2026MM08)

    Beide zijn maandreeksen. De bezetting gaat over de stand in een maand, de CBS-bevolking over het begin van de maand: zet ze in een grafiek met dezelfde maand op de as en noem het verschil in peildatum.

Laat AI het bouwen

Plak deze prompt in Claude, ChatGPT, Cursor of Lovable. Hij noemt de echte endpoints, de koppelsleutels en de valkuilen, zodat de assistent meteen werkende code schrijft. Controleer de uitkomst altijd met de bron zelf.

Bouw een dashboard over asielopvang in Nederland (Node 20+ voor het ophalen, de pagina in gewone HTML met JavaScript of een klein Next.js-project). Geen sleutels nodig.

Deel 1, landelijk per maand sinds 2016 (COA, via het ministerie van Asiel en Migratie, CC0):
- Haal https://data.overheid.nl/dataset/immigratie-coa-opvang-en-onderdak-bezetting op en pak alle links naar .csv (regex op https://data.overheid.nl/sites/default/files/dataset/…csv). Gebruik NIET de CKAN-API onder /data/api: robots.txt verbiedt /data/.
- Sorteer de bestanden op het jaartal in de naam: 2016 tot en met 2023 staan in een andere map dan 2024 tot en met 2026, en 2020 heet "…2020_2.csv".
- CSV met puntkomma's. Codering wisselt: probeer UTF-8 strikt (fatal), anders Windows-1252; haal een BOM weg. Maand staat als "1" of "01".
- Kolom "Totaal van" bepaalt de uitsplitsing. Totaal per maand: "Jaar, Maand". Vergunninghouders: "Jaar, Maand, Vergunninghouder" met Vergunninghouder = "Vergunninghouder". Alleenstaande minderjarigen: "Jaar, Maand, Amv" met Amv = "Amv" (kolom bestaat pas sinds 2024). Herkomstland: "Jaar, Maand, Land van herkomst". "*Alle*" betekent alle waarden samen. Nooit uitsplitsingen mengen of details optellen: alles is afgerond op tientallen en aantallen onder 5 ontbreken.
- Het aantal staat in de laatste kolom; die heet per dataset anders ("Aantal bezetting opvang", "Aantal ontvangen in opvang", "Aantal uitgestroomd uit opvang").
- Landnamen veranderen per jaar: "Syrië" in 2023, "Syrie" in 2025 en 2026; "Bosnië-Herzegovina" werd "Bosnië en Herzegovina". Normaliseer (accenten weg, kleine letters) voordat je landen over jaren volgt.
- Instroom en uitstroom: dezelfde aanpak met de datasets immigratie-coa-opvang-en-onderdak-instroom en immigratie-coa-opvang-en-onderdak-uitstroom; daar is "Jaar" het jaartotaal.
- Een nieuw maandbestand kan eerdere maanden corrigeren: haal elke maand het hele jaar opnieuw op.

Deel 2, opvangplaatsen per gemeente (COA-locatiepagina's, wekelijks gelezen door Open Data Index):
- Boom: https://www.opendataindex.nl/api/catalogus/coa-locaties geeft provincies (kinderen met pad), ?pad=/0 de gemeenten in een provincie, ?pad=/0/0 de locaties (items met sleutel en titel). Map "Onbekend" overslaan.
- Per locatie: https://www.opendataindex.nl/dataset/coa-locaties/<sleutel>. De meta-description begint met "Capaciteit: <getal>". Geen getal = niet meetellen (10 van 261 op 16 oktober 2026). Maximaal 4 verzoeken tegelijk.
- Dit is capaciteit, geen bezetting, en niet elke locatie heeft een pagina. Gemeentelijke crisisnoodopvang ontbreekt. Zeg dat op de pagina.

Deel 3, inwoners (CBS 37230ned, OData v4, geen spaties in codes):
- Nieuwste maand: https://datasets.cbs.nl/odata/v1/CBS/37230ned/PeriodenCodes?$orderby=Identifier desc&$top=1
- Bevolking begin van de maand: https://datasets.cbs.nl/odata/v1/CBS/37230ned/Observations?$filter=Measure eq 'M000098' and Perioden eq '<periode>' and startswith(RegioS,'GM')&$select=RegioS,Value (regels met Value null zijn opgeheven gemeenten).
- Namen: https://datasets.cbs.nl/odata/v1/CBS/37230ned/RegioSCodes?$filter=startswith(Identifier,'GM'); haal " (gemeente)" van het eind en koppel op naam aan de gemeenten uit deel 2. Meld gemeenten die niet koppelen.
- Als opendata.cbs.nl (v3) een 503 geeft: probeer het later of blijf bij v4.

Uitkomst: bovenaan drie kerncijfers (bewoners nu, verschil met een jaar eerder, vergunninghouders) met de maand erbij. Een lijngrafiek van de bezetting sinds 2016 met de vergunninghouders als tweede lijn. Staafjes instroom tegen uitstroom per maand. Een sorteerbare tabel per gemeente: plaatsen, aantal locaties, plaatsen per 1.000 inwoners, provincie, met een zoekveld. Onder elk onderdeel de bron, de peildatum en "afgerond op tientallen". Gebruik een eerlijke User-Agent zoals "mijn-project/1.0 (+jij@voorbeeld.nl)" en bewaar de opgehaalde data als JSON, zodat de pagina niet bij elk bezoek alle bronnen aanroept.

Probeer het zelf

De kern van het project in een klein script. Nodig: Node 20+ (fetch zit erin), geen pakketten. Getest op 2026-10-16; hieronder de echte uitvoer van die proefdraai.

// Asielopvang: landelijke bezetting per maand (COA via data.overheid.nl), opvangplaatsen per gemeente
// (COA-locatiepagina's, via de index van opendataindex.nl) en plaatsen per 1.000 inwoners (CBS 37230ned).
// Node 20+, geen pakketten nodig: node asielopvang.mjs
const UA = { "User-Agent": "mijn-project/1.0 (+jij@voorbeeld.nl)" };
const ODI = "https://www.opendataindex.nl";
const nl = (n, d = 0) => n.toLocaleString("nl-NL", { minimumFractionDigits: d, maximumFractionDigits: d });

async function haal(url, soort = "json") {
  for (let poging = 1; ; poging++) {
    const r = await fetch(url, { headers: UA });
    if (r.ok) return soort === "json" ? r.json() : soort === "bytes" ? new Uint8Array(await r.arrayBuffer()) : r.text();
    if (poging === 3) throw new Error(`${r.status} bij ${url}`);
    await new Promise((ok) => setTimeout(ok, 2000 * poging));
  }
}
// De CSV's wisselen van codering: 2024 en 2025 UTF-8 (met BOM), de andere jaren Windows-1252.
function leesCsv(bytes) {
  let t;
  try { t = new TextDecoder("utf-8", { fatal: true }).decode(bytes); } catch { t = new TextDecoder("windows-1252").decode(bytes); }
  const [kop, ...rijen] = t.replace(/^/, "").trim().split(/\r?\n/).map((l) => l.split(";"));
  return rijen.map((x) => Object.fromEntries(kop.map((k, i) => [k.trim(), (x[i] ?? "").trim()])));
}
const waarde = (r) => Number(Object.values(r).at(-1)); // de laatste kolom is het aantal (naam verschilt per bestand)

// 1. Landelijk: de jaarbestanden staan als links op de datasetpagina (niet via /data/api: robots.txt verbiedt /data/).
const pagina = await haal("https://data.overheid.nl/dataset/immigratie-coa-opvang-en-onderdak-bezetting", "text");
// Sorteer op het jaartal in de bestandsnaam: 2016-2023 en 2024-2026 staan in verschillende mappen ("…2020_2.csv").
const jaar = (u) => u.match(/(20\d\d)(_\d)?\.csv$/)[1];
const csvs = [...new Set(pagina.match(/https:\/\/data\.overheid\.nl\/sites\/default\/files\/dataset\/[^"]+?\.csv/g))].sort((a, b) => jaar(a) - jaar(b));
const maanden = [];
let laatsteRijen = [];
for (const url of csvs) {
  const rijen = leesCsv(await haal(url, "bytes"));
  for (const r of rijen) if (r["Totaal van"] === "Jaar, Maand") maanden.push({ p: `${r.Jaar}-${r.Maand.padStart(2, "0")}`, v: waarde(r) });
  laatsteRijen = rijen;
}
maanden.sort((a, b) => a.p.localeCompare(b.p));
const nu = maanden.at(-1), laagste = maanden.reduce((a, b) => (b.v < a.v ? b : a));
const mnd = Number(nu.p.slice(5));
const deel = (kolom, w) => waarde(laatsteRijen.find((r) => r["Totaal van"] === `Jaar, Maand, ${kolom}` && Number(r.Maand) === mnd && r[kolom] === w));
console.log(`Bezetting COA-opvang ${nu.p}: ${nl(nu.v)} bewoners (${csvs.length} jaarbestanden, ${maanden.length} maanden)`);
console.log(`  laagste punt ${laagste.p}: ${nl(laagste.v)}; een jaar eerder (${maanden.at(-13).p}): ${nl(maanden.at(-13).v)}`);
console.log(`  waarvan vergunninghouders ${nl(deel("Vergunninghouder", "Vergunninghouder"))}, alleenstaande minderjarigen ${nl(deel("Amv", "Amv"))}`);

// 2. Opvangplaatsen per locatie: de boom van onze index (provincie > gemeente > locatie) en per locatie de datasetpagina.
const boom = async (pad) => haal(`${ODI}/api/catalogus/coa-locaties?pad=${encodeURIComponent(pad)}`);
const locaties = [];
for (const prov of (await boom("")).kinderen) {
  for (const gem of (await boom(prov.pad)).kinderen) {
    for (const it of (await boom(gem.pad)).items) locaties.push({ provincie: prov.titel, gemeente: gem.titel, sleutel: it.sleutel, titel: it.titel });
  }
}
async function inStukjes(lijst, n, f) { for (let i = 0; i < lijst.length; i += n) await Promise.all(lijst.slice(i, i + n).map(f)); }
await inStukjes(locaties, 4, async (l) => {
  const html = await haal(`${ODI}/dataset/coa-locaties/${l.sleutel}`, "text");
  const m = html.match(/<meta name="description" content="Capaciteit: (\d+)/);
  l.plaatsen = m ? Number(m[1]) : null; // tien pagina's noemen geen capaciteit: die tellen niet mee
});
const met = locaties.filter((l) => l.plaatsen);
const som = met.reduce((s, l) => s + l.plaatsen, 0);
console.log(`\nLocatiepagina's: ${locaties.length}, met capaciteit ${met.length}, samen ${nl(som)} plaatsen`);

// 3. Inwoners per gemeente: CBS 37230ned via OData v4 (datasets.cbs.nl), bevolking aan het begin van de nieuwste maand.
const V4 = "https://datasets.cbs.nl/odata/v1/CBS/37230ned";
const per = (await haal(`${V4}/PeriodenCodes?$orderby=Identifier desc&$top=1`)).value[0];
const obs = (await haal(`${V4}/Observations?$filter=${encodeURIComponent(`Measure eq 'M000098' and Perioden eq '${per.Identifier}' and startswith(RegioS,'GM')`)}&$select=RegioS,Value`)).value;
const codes = (await haal(`${V4}/RegioSCodes?$filter=${encodeURIComponent("startswith(Identifier,'GM')")}&$select=Identifier,Title`)).value;
const naam = Object.fromEntries(codes.map((c) => [c.Identifier, c.Title.replace(/ \(gemeente\)$/, "")]));
const inw = new Map(obs.filter((o) => o.Value != null).map((o) => [naam[o.RegioS], { gm: o.RegioS, n: o.Value }]));

const perGemeente = new Map();
for (const l of met) {
  const g = perGemeente.get(l.gemeente) ?? { plaatsen: 0, locaties: 0, provincie: l.provincie };
  g.plaatsen += l.plaatsen; g.locaties++; perGemeente.set(l.gemeente, g);
}
const tabel = [...perGemeente].map(([gemeente, g]) => ({ gemeente, ...g, ...inw.get(gemeente) }));
const zonder = tabel.filter((t) => !t.gm).map((t) => t.gemeente);
console.log(`Inwoners: CBS 37230ned, ${per.Title} (${per.Status}), ${inw.size} gemeenten; ${tabel.length} gemeenten met opvang${zonder.length ? `, niet gekoppeld: ${zonder.join(", ")}` : ""}`);
console.log("\nMeeste opvangplaatsen per 1.000 inwoners:");
for (const t of tabel.filter((t) => t.gm).sort((a, b) => b.plaatsen / b.n - a.plaatsen / a.n).slice(0, 10))
  console.log(`  ${t.gm} ${t.gemeente.padEnd(22)} ${String(t.plaatsen).padStart(5)} plaatsen  ${String(t.locaties).padStart(2)} loc.  ${nl(1000 * t.plaatsen / t.n, 1).padStart(5)} per 1.000`);
const totInw = [...inw.values()].reduce((s, x) => s + x.n, 0);
const provs = new Map(); for (const l of met) provs.set(l.provincie, (provs.get(l.provincie) ?? 0) + l.plaatsen);
console.log(`\nPer provincie: ${[...provs].sort((a, b) => b[1] - a[1]).map(([p, n]) => `${p} ${nl(n)}`).join(", ")}`);
console.log(`Landelijk: ${nl(1000 * som / totInw, 1)} plaatsen per 1.000 inwoners; ${342 - tabel.length} van de 342 gemeenten hebben geen COA-locatie met een pagina`);
Uitvoer
Bezetting COA-opvang 2026-09: 83.150 bewoners (11 jaarbestanden, 129 maanden)
  laagste punt 2018-06: 20.010; een jaar eerder (2025-09): 76.770
  waarvan vergunninghouders 18.300, alleenstaande minderjarigen 4.190

Locatiepagina's: 261, met capaciteit 251, samen 68.779 plaatsen
Inwoners: CBS 37230ned, 2026 augustus (Voorlopig), 342 gemeenten; 160 gemeenten met opvang

Meeste opvangplaatsen per 1.000 inwoners:
  GM1950 Westerwolde             2000 plaatsen   1 loc.   79,8 per 1.000
  GM1706 Cranendonck             1500 plaatsen   1 loc.   70,7 per 1.000
  GM0784 Gilze en Rijen          1200 plaatsen   1 loc.   42,4 per 1.000
  GM0629 Wassenaar                930 plaatsen   1 loc.   33,0 per 1.000
  GM0050 Zeewolde                 600 plaatsen   1 loc.   24,2 per 1.000
  GM0303 Dronten                 1000 plaatsen   1 loc.   21,9 per 1.000
  GM0376 Blaricum                 250 plaatsen   1 loc.   19,4 per 1.000
  GM0171 Noordoostpolder         1000 plaatsen   1 loc.   19,1 per 1.000
  GM1979 Eemsdelta                822 plaatsen   2 loc.   18,4 per 1.000
  GM0737 Tytsjerksteradiel        600 plaatsen   1 loc.   18,2 per 1.000

Per provincie: Zuid-Holland 13.086, Noord-Holland 13.055, Gelderland 8.236, Noord-Brabant 6.960, Groningen 4.130, Utrecht 3.900, Drenthe 3.798, Limburg 3.663, Flevoland 3.620, Overijssel 3.250, Fryslân 3.210, Zeeland 1.871
Landelijk: 3,8 plaatsen per 1.000 inwoners; 182 van de 342 gemeenten hebben geen COA-locatie met een pagina

Waar het misgaat

  • Uitsplitsingen door elkaar: elke CSV bevat het maandtotaal én tientallen uitsplitsingen (naar status, herkomstland, verblijfsduur, opvangvorm, combinaties). Filter altijd op één waarde van 'Totaal van'. Wie alle regels van een maand optelt, krijgt een veelvoud van het echte aantal.
  • Afronding op tientallen: details tellen niet op tot het totaal. De herkomstlanden van september 2026 tellen samen 83.200, het totaal is 83.150; de maanden instroom 2026 samen 36.590, de jaarregel 36.570. Toon de totaalregels en zet 'afgerond op tientallen' bij elk getal.
  • Codering en spelling per jaar: 2024 en 2025 zijn UTF-8 met BOM, de rest Windows-1252. Lees je alles als UTF-8, dan krijg je 'Syrië' of een fout. En landnamen veranderen: 'Syrië' (2023) werd 'Syrie' (2025, 2026), 'Bosnië-Herzegovina' werd 'Bosnië en Herzegovina'. Normaliseer namen voordat je een land over de jaren volgt.
  • Capaciteit is geen bezetting: de locatiepagina's noemen het aantal plaatsen, niet hoeveel mensen er wonen. Ter Apel heeft 2.000 plaatsen, maar zegt niets over hoe vol het is. En de som van de pagina's (68.779) is niet de hele opvang: 261 pagina's tegen 329 locaties volgens het COA, plus gemeentelijke crisisnoodopvang die hier helemaal niet in staat.
  • Tekst van een website: de looptijd van de bestuursovereenkomst is wat de pagina zegt, en die kan achterlopen (Assen-Schepersmaat: 'loopt af op 1 april 2026', in oktober 2026 nog online). Toon de datum van onze oogst en link naar de locatiepagina op coa.nl.
  • Robots.txt en adressen: data.overheid.nl verbiedt /data/ (de CKAN-API), dus lees de datasetpagina. api.pdok.nl verbiedt de Locatieserver: zoek adressen op met de BAG-WFS op service.pdok.nl. Het overzicht coa.nl/nl/locaties stuurt door naar /nl/node/262 en dat geeft 403: gebruik de sitemap.
  • CBS-codes en storingen: de oude OData v3 vult codes aan met spaties ('GM0363 ') en gaf op 16 oktober 2026 een tijd lang 503. De v4 op datasets.cbs.nl doet dat niet, maar geeft ook opgeheven gemeenten terug met Value null: filter die eruit, anders deel je door nul.

Verder bouwen

  • Overeenkomsten die aflopen: lees uit elke locatiebeschrijving de zin 'loopt tot <datum>' of 'loopt af op <datum>' en toon per gemeente hoeveel plaatsen de komende twee jaar kunnen verdwijnen.
  • Asielverzoeken ernaast: CBS-tabel 83102NED telt de asielverzoeken per maand en nationaliteit. Zet die naast de instroom in de opvang per herkomstland (na het normaliseren van de landnamen).
  • Wachten op een woning per gemeente: zet het landelijke aantal vergunninghouders in de opvang (18.300 in september 2026) naast de taakstelling huisvesting per gemeente, en laat zien hoeveel woningen er nodig zijn.
  • Kaart: elke locatie heeft in onze index een punt uit de BAG. Toon de locaties op een kaart met de gemeentegrenzen van PDOK (pdok-wijkenbuurten) en kleur gemeenten naar plaatsen per 1.000 inwoners.

Meer met deze data

Vergunninghouders in de COA-opvang (met verblijfsvergunning, wachtend op een woning): 18.300 bewoners in september 2026, 21% meer dan in januari 2016

Vergunninghouders in de COA-opvang (met verblijfsvergunning, wachtend op een woning), januari 2016 – september 2026, in bewoners. Het hoogste punt was juni 2024 (20.390).

Bron: Ministerie van Asiel en Migratie, Migratieketen: COA-opvang (data.overheid.nl, CC0) · opgehaald 25 oktober 2026 · selectie: Alleen bewoners met een verblijfsvergunning (vergunninghouders); Aantallen afgerond op tientallenOpen de tabel Los blok

Instroom in de COA-opvang: 51.660 mensen in 2025, 44% meer dan in 2016

Instroom in de COA-opvang, 2016 – 2025, in mensen. Het hoogste punt was 2023 (55.980).

Bron: Ministerie van Asiel en Migratie, Migratieketen: COA-opvang (data.overheid.nl, CC0) · opgehaald 25 oktober 2026 · selectie: Alleen volle jaren; Aantallen afgerond op tientallenOpen de tabel Los blok

Bewoners van COA-opvanglocaties per land van herkomst: het meest Syrie (28.190 bewoners), dan Eritrea en Irak

Bewoners van COA-opvanglocaties per land van herkomst, september 2026, in bewoners. Totaal: 83.150; de grootste 12 van 119 categorieën staan in de grafiek. Syrie is 34% van het totaal. Bewoners van COA-locaties naar land van herkomst, september 2026. Landnamen zoals het ministerie ze schrijft; afgerond op tientallen, landen met minder dan 5 bewoners staan er niet in.

Bron: Ministerie van Asiel en Migratie, Migratieketen: COA-opvang (data.overheid.nl, CC0) · opgehaald 25 oktober 2026Open de tabel Los blok

Opvangplaatsen op COA-locaties per provincie: het meest Zuid-Holland (13.086 plaatsen), dan Noord-Holland en Gelderland

Opvangplaatsen op COA-locaties per provincie, oktober 2026, in plaatsen. Totaal: 68.779; de grootste 12 van 12 categorieën staan in de grafiek. Zuid-Holland is 19% van het totaal. Opvangplaatsen volgens de 261 locatiepagina's van het COA (251 noemen een capaciteit), samen 68.779 plaatsen. Dat is de capaciteit, niet hoeveel mensen er nu wonen. Crisisnoodopvang van gemeenten en veiligheidsregio's staat er niet in. Provincie uit het adres (BAG) en de gemeentegrenzen van 2025.

Bron: COA, locatiepagina's op coa.nl (wekelijks gelezen door Open Data Index) · opgehaald 25 oktober 2026Open de tabel Los blok

Opvangplaatsen op COA-locaties per gemeente: het meest Amsterdam (5.196 plaatsen), dan Rotterdam en Westerwolde

Opvangplaatsen op COA-locaties per gemeente, oktober 2026, in plaatsen. Totaal: 68.779; de grootste 12 van 160 categorieën staan in de grafiek. Amsterdam is 8% van het totaal. Opvangplaatsen volgens de 261 locatiepagina's van het COA (251 noemen een capaciteit), samen 68.779 plaatsen. Dat is de capaciteit, niet hoeveel mensen er nu wonen. Crisisnoodopvang van gemeenten en veiligheidsregio's staat er niet in. Gemeente uit het adres (BAG) en de gemeentegrenzen van 2025.

Bron: COA, locatiepagina's op coa.nl (wekelijks gelezen door Open Data Index) · opgehaald 25 oktober 2026Open de tabel Los blok

De bronnen3

Asielopvang in cijfers (COA)

Ministerie van Asiel en Migratie, met cijfers van het COANederland, land, per maand

Hoeveel mensen er in de opvang van het COA wonen, hoeveel er per maand binnenkomen en vertrekken, uit welke landen ze komen en hoeveel er al een verblijfsvergunning hebben en op een woning wachten. Het ministerie van Asiel en Migratie publiceert deze cijfers van de migratieketen maandelijks als CSV op data.overheid.nl, één bestand per jaar. Landelijke cijfers: niet per gemeente of per locatie.

OpenDownloadBevolkingWonen en ruimte
Actueel
bijgewerkt 4 dagen geleden

COA-opvanglocaties

COA (locatiepagina's), gelezen door Open Data IndexNederland, locatie

Het COA heeft geen open bestand of API met zijn locaties. Wel heeft elke locatie een eigen pagina op coa.nl met de capaciteit, het type opvang (azc, noodopvang, opvang voor alleenstaande minderjarigen …), de looptijd van de bestuursovereenkomst met de gemeente en het adres. Wij lezen die pagina's elke maandag, zoeken het adres op in de BAG en leggen het in de gemeentegrenzen. Zo staat hier per gemeente en provincie hoeveel opvangplaatsen er zijn.

OpenZoekdienstBevolkingWonen en ruimte
Bereikbaar
wekelijks

Bevolkingsontwikkeling per maand

CBSNederland, gemeente

Per gemeente en per maand: hoeveel mensen er geboren werden, overleden, kwamen wonen of vertrokken.

OpenAPIBevolking
Actueel
bijgewerkt 17 dagen geleden

Meer om te bouwen