
DUO open education data
Every school and programme with its address, and per school location the number of pupils, school advice, end-of-primary test scores, exam results and forecasts. From primary school to university. DUO publishes it as downloads and as an API (56 datasets with 274 tables in the datastore on 16 October 2026).

Everything that's in it348
All datasets in DUO's catalogue, by subject as DUO groups them, with every file, fetched every night. Every table has its own page with the number of rows, the columns and the API call.
Arbeidsovereenkomst en cao5
Basisonderwijs74
Hoger onderwijs38
Inburgering3
Middelbaar beroepsonderwijs43
Speciaal onderwijs en leerproblemen55
Voortgezet onderwijs33
Zonder groep165
Pupils in primary education: 1.36 million in 2025/26, 10% less than in 1996/97
Pupils in primary education, 1996/97 – 2025/26, in million. The highest point was 2008/09 (1.55).
Vocational (MBO) students by field: most Health and social care (141,737 students), then Business and administration and Engineering and process industry
Vocational (MBO) students by field, October 2025, in students. Total: 475,113; the largest 12 of 17 categories are shown. Health and social care is 30% of the total. Enrolments on 1 October 2025 (provisional according to DUO). 567 cells with 1 to 4 students are suppressed and not counted.
What you can build with it1
What can you do with it?4 ideas
What advice do children get at your primary school?
Count the school advice per location per year (since 2010) and compare the school with others in the municipality.
Shrinking and growing schools
Pupils per location are available from 2010 to 2025. Find schools that lost half their pupils in ten years.
School advice next to the neighbourhood
Link locations by postcode or municipality number to income and education from the CBS key figures. Do children in poorer areas get lower advice?
Where do the pupils come from?
The postcode4 file shows per location which postcode areas the pupils come from. That tells you how far parents travel for a school.
How to process it6 steps
Find the dataset in the catalogue
The catalogue is a CKAN portal. Search for a word and check which files (resources) belong to each dataset.
https://onderwijsdata.duo.nl/api/3/action/package_search?q=advies&rows=5
Query rows through the datastore
Each CSV file is also in the datastore. With resource_id, filters and sort you fetch only what you need. This example: school advice for one school in 2024.
https://onderwijsdata.duo.nl/api/3/action/datastore_search?resource_id=454c5dd4-6815-4b52-ae2f-b9eebc6afa6d&filters=%7B%22INSTELLINGSCODE%22%3A%2200AP%22%2C%22PEILJAAR%22%3A2024%7D
Or download the whole file
For a full analysis the CSV is easier. Some download links in the catalogue point to an internal server (beheer-ggm-ckan-prd…); replace the host with onderwijsdata.duo.nl.
https://onderwijsdata.duo.nl/dataset/d6903313-8c64-4b12-b4ac-07e5655ef9f4/resource/454c5dd4-6815-4b52-ae2f-b9eebc6afa6d/download/brin6_advies.csv
Join to addresses
Pupil files only have the institution and location code. Name, address, postcode and municipality number are in the address files (for example all primary school locations).
https://onderwijsdata.duo.nl/api/3/action/datastore_search?resource_id=dcc9c9a5-6d01-410b-967f-810557588ba4&limit=5
Remove the small numbers
A -1 means 1 to 4 pupils (suppressed). Don't count it as a number; use a lower and upper bound or leave it out.
Accredited programmes in higher education
The Overzicht Erkenningen ho (CC BY) gives per institution, location and programme the recognition, accreditation dates, study load, degree and status, current and historical: 74,173 rows on 16 October 2026. This dataset is only in DUO's catalogue, not on data.overheid.nl.
https://onderwijsdata.duo.nl/api/3/action/datastore_search?resource_id=28a4d89b-c223-4dbc-8deb-9d02a533f215&limit=5
Interesting combinations


Is school advice related to income in the municipality?
Put GM before the municipality number from the address file (1699 becomes GM1699) and join to the key figures of the same year.


What kind of buildings are the schools in?
Look up each location's address in the BAG for construction year and floor area.
Similar sources
CBS StatLineCBS has education figures per municipality and nationally, not per school.
- SKScholen op de kaart not in the index yet
School profiles for parents, partly based on DUO data. A website, not a dataset.
What to watch out for
- Counts below 5 are suppressed
The API then shows -1, some files show '<5'. For small schools that is a large share of the cells.
- Not every dataset is current
The register of schools and programmes (RIO) is refreshed daily, pupil counts once a year (reference date 1 October). Exam results per location in the API run to school year 2017 (as of 15 October 2026).
- Codes without explanation in the API
The ADVIES field is a number (for example 5, 7 or 11), not text. Look up the meaning at DUO before you count.
- No SQL on the datastore
datastore_search_sql returns 'no access'. Grouping and counting is up to you, after fetching.
- Not everything is also on data.overheid.nl
A few datasets are only in the DUO catalogue, such as Overzicht Erkenningen ho and Sectorale Ontwikkelpaden (which programmes belong to a development path and whether SLIM subsidy is possible). Sectorale Ontwikkelpaden has no licence in the catalogue (16 October 2026): licence not established.
APICKAN (Action API v3 + datastore) · read 16 Oct 2026
No key or account needed tested
tables in the datastore (of 290 files in 56 datasets) tested
jsoncsv
Base URL
https://onderwijsdata.duo.nl/api/3/action
Endpoints 8
- GET
/package_listNames of all 56 datasets (e.g. adressen_bo, 03_voex-v1, historische-leerlingenaantallen-po). - GET
/package_search?q=…&fq=…&rows=…Search the catalogue; rows=0 returns the count only. Also returns each dataset's resources (files) with id, format and datastore_active. - GET
/package_show?id={naam}One dataset: description, licence, metadata_modified and the resources with their resource_id and download URL. - GET
/resource_show?id={resource_id}One file: name, format, last_modified, download URL. - GET
/group_listThe eight topic groups (basisonderwijs, voorgezet-onderwijs, hoger-onderwijs, inburgering …). - GET
/group_show?id={groep}One group with title and description. - GET
/datastore_search?resource_id={id}The rows of one table, with filters, full-text search, sorting, column choice and paging. The response also lists the columns with their type. - GET
/dataset/{dataset-id}/resource/{resource_id}/download/{bestand}.csvThe whole file as CSV (path without /api/3/action, on https://onderwijsdata.duo.nl).
Filtering tested
No query language: equality filters as JSON in filters (value or list of values), plus full-text search with q. No greater-than or less-than; datastore_search_sql is disabled.
Parameters 10
resource_id | Required for datastore_search: the file id (from package_show). | tested |
filters | Equality filter per column as JSON, also with a list: {"PEILJAAR":[2023,2024]}. Column names exactly as in fields (sometimes with spaces: "BEVOEGD GEZAG NUMMER"). | tested |
q | Full-text search in all columns, or per column: {"PLAATSNAAM":"utrecht"} (word match, case-insensitive). | tested |
fields | Only these columns, comma-separated. | tested |
sort | Sorting, e.g. PEILJAAR desc. | tested |
limit | Number of rows; default 100 (CKAN). 40,000 was served fine; limit=0 returns only total and columns. | tested |
offset | Paging; the response has _links.next with the next page. | tested |
records_format | objects (default), lists or csv (rows as one CSV string, no header). | tested |
include_total | false skips counting (faster on large tables). | tested |
q / fq / rows / start / sort (package_search) | Solr search in the catalogue: fq=groups:basisonderwijs (24 datasets), sort=metadata_modified desc. | tested |
Fields in the datastore (selection: school advice and primary school addresses) 8
| Name | Type | Meaning |
|---|---|---|
INSTELLINGSCODE | text | BRIN-code van de school (4 tekens, bv. 00AP) |
VESTIGINGSCODE | text | Vestiging: in leerlingbestanden 2 tekens (00), in adressenbestanden BRIN+2 (32JK00) |
PEILJAAR | numeric | Jaar van de telling (peildatum 1 oktober) |
ADVIES | numeric | Schooladvies als getalcode; de betekenis staat in de bestandsbeschrijving op duo.nl, niet in de API |
AANTAL_LEERLINGEN | numeric | Aantal leerlingen; -1 = 1 tot 4 (afgeschermd) |
GEMEENTENUMMER | text | Gemeentecode zonder GM, als tekst met voorloopnullen ("1699") |
POSTCODE | text | Postcode van de vestiging (adressenbestanden) |
_id | int | Rijnummer in de datastore |
Limits and pitfalls
- No key and no documented rate limit. Default 100 rows per call; a limit of 40,000 was served. Page with offset or _links.next. tested
- Only some CKAN actions are open (package_list, package_search, package_show, resource_show, group_list, group_show, datastore_search). tested
- Other CKAN actions (status_show, organization_list, tag_list, datastore_info, help_show) return HTTP 200 with the website's HTML, not a JSON error. Always check that the response is JSON. datastore_search_sql returns 400 "Action name not known". tested
- distinct=true counts correctly (total 15 years in the school advice table) but returned only one row. For unique values, prefer a small column selection with a high limit. tested
- Privacy: counts of 1 to 4 appear as -1 in the API (as <5 in some files). Don't add them up as numbers. tested
- Nine download URLs in the catalogue point to an internal host (beheer-ggm-ckan-prd.apps.prd.duo.rijksapps.nl) that doesn't resolve outside DUO; replace the host with onderwijsdata.duo.nl. tested
- Pupil files only have BRIN and branch code; name, address and municipality come from the address files (adressen_bo, adressen_vo …). Municipality number has no GM prefix: add GM and leading zeros to join with CBS. experience
- Resource ids don't change with a new delivery; the dataset's metadata_modified and the resource's last_modified give freshness (addresses are refreshed daily). experience
Try it HTTP 200 · 694 ms · 16 Oct
https://onderwijsdata.duo.nl/api/3/action/datastore_search?resource_id=454c5dd4-6815-4b52-ae2f-b9eebc6afa6d&filters={"PEILJAAR"%3A2024}&sort=AANTAL_LEERLINGEN desc&limit=3
{
"help": "https://onderwijsdata.duo.nl/api/3/action/help_show?name=datastore_search",
"success": true,
"result": {
"filters": {
"PEILJAAR": 2024
},
"include_total": true,
"limit": 3,
"records_format": "objects",
"resource_id": "454c5dd4-6815-4b52-ae2f-b9eebc6afa6d",
"sort": "AANTAL_LEERLINGEN desc",
"total_estimation_threshold": null,
"records": [
{
"_id": 104633,
"INSTELLINGSCODE": "05VX",
"VESTIGINGSCODE": "00",
"TYPE_PO": "BO",
"PEILJAAR": 2024,
"ADVIES": 12,
"AANTAL_LEERLINGEN": 234
},
{
"_id": 492855,
"INSTELLINGSCODE": "17ON",
"VESTIGINGSCODE": "00",
"TYPE_PO": "BO",
"PEILJAAR": 2024,
"ADVIES": 12,
"AANTAL_LEERLINGEN": 78
}
],
"fields": [
{
"id": "_id",
"type": "int"
},
{
"id": "INSTELLINGSCODE",Shortened response, saved at harvest time. Click the URL for the real, current response.
Read from CKAN Action API (platform documentation) · CKAN datastore_search: filters, q, sort, fields, distinct · DUO: explanation per topic and file descriptions. Labels: from the description stated in what the api itself publishes. tested from our own small call. platform applies to every api on this platform, per its documentation. experience learned while building open data index.
How to fetch it
curl -G "https://onderwijsdata.duo.nl/api/3/action/datastore_search" --data-urlencode "resource_id=454c5dd4-6815-4b52-ae2f-b9eebc6afa6d" --data-urlencode "filters={\"PEILJAAR\":2024}" --data-urlencode "limit=5"More from DUO in our index
461 datasets in DUO open onderwijsdata, data.overheid.nl. These changed most recently.
- controlebestand2026-10-30
- aangeboden_ho_opleidingen2026-10-30
- ho_opleidingsoverzicht2026-10-30
- HO_Erkenningen_RIO2026-10-30
- aangeboden_bo_opleidingen2026-10-30
- RIO nfo, po, vo, vavo, mbo en ho2026-10-30
More in this topic
See how this page is built
bron: duo-onderwijsdata blokken: [] secties: - ideeen - verwerken - koppelingen - vergelijkbaar - beperkingen - code cache: tag: bron:duo-onderwijsdata ververs: na statuscheck, als de bron nieuwer is statuscheck: methode: "CKAN-catalogus: nieuwste gewijzigde dataset (metadata_modified)" verwacht: dagelijks
Source: content/bronnen/duo-onderwijsdata.yaml · Markdown

