# KB, National Library of the Netherlands (linked data)

On data.bibliotheken.nl the KB publishes its catalogues as linked data under CC0: the Dutch national bibliography NBT (4,911,178 books), the Short Title Catalogue Netherlands (STCN, 225,218 titles printed before 1801, with 651,395 copies), the Dutch author name thesaurus (2,816,677 persons), the Brinkman subject headings and DBNL titles and authors. Together 258,524,625 statements, queryable with SPARQL (as of 16 October 2026).

- Publisher: KB, nationale bibliotheek
- Area: Netherlands · Finest detail: title
- Period: 1473 – heden · Refreshed: quarterly
- Access: Open · Licence: CC0 1.0 (alle data op data.bibliotheken.nl)
- Join keys: PPN (KB-identifier, bv. p262454742), ISBN, NTA-persoons-id
- Status: ok (2026-09-25) · 258.524.625 statements, 21 grafen
- URL: https://data.bibliotheken.nl

## What can you do with it?
- **The disaster year in print.** The STCN lists 1,485 titles from 1672, against 808 in 1671 and 903 in 1673. 1787 (2,021) and 1795 (2,195) also stand out. Find out which pamphlets they were.
- **Who wrote it?.** The author thesaurus (NTA) gives each person a persistent identifier, often with date of birth, occupation and links to other registers (schema:sameAs). Useful to tell namesakes apart in a fact check.
- **Printers and their cities.** The STCN describes each title's publication: printer (schema:publisher) and a line such as 'Amsterdam, J. Allart, 1781'. Extract the city, count per century and compare Amsterdam, Leiden and The Hague.
- **Language of the books.** Each NBT title has a schema:inLanguage. See how the share of English-language titles develops, with the caveat from the limitations.

## How to process it
1. **A first count.** Each collection is its own graph (nbt, stcn-titles, nta, brinkman …). A GET with ?query= works; ask for CSV with the header Accept: text/csv or JSON with application/sparql-results+json.
   `https://data.bibliotheken.nl/sparql?query=SELECT%20%28COUNT%28%2A%29%20AS%20%3Fn%29%20WHERE%20%7B%20GRAPH%20%3Chttp%3A%2F%2Fdata.bibliotheken.nl%2Fnbt%3E%20%7B%20%3Fs%20a%20%3Chttp%3A%2F%2Fschema.org%2FBook%3E%20%7D%20%7D`
2. **Titles from one year (STCN).** The year of publication sits in an intermediate node, schema:publication/schema:startDate. Uncertain years appear as 16XX or 179X.
   `https://data.bibliotheken.nl/sparql?query=SELECT%20%3Ftitel%20%3Fnaam%20WHERE%20%7B%20GRAPH%20%3Chttp%3A%2F%2Fdata.bibliotheken.nl%2Fstcn-titles%3E%20%7B%20%3Ftitel%20%3Chttp%3A%2F%2Fschema.org%2Fname%3E%20%3Fnaam%20%3B%20%3Chttp%3A%2F%2Fschema.org%2Fpublication%3E%2F%3Chttp%3A%2F%2Fschema.org%2FstartDate%3E%20%221672%22%20%7D%20%7D%20LIMIT%2010`
3. **Books per year (NBT).** In the NBT the year is in http://purl.org/dc/terms#issued, with a hash instead of a slash. The usual dcterms:issued finds nothing.
   `https://data.bibliotheken.nl/sparql?query=SELECT%20%3Fjaar%20%28COUNT%28%3Fs%29%20AS%20%3Fn%29%20WHERE%20%7B%20GRAPH%20%3Chttp%3A%2F%2Fdata.bibliotheken.nl%2Fnbt%3E%20%7B%20%3Fs%20a%20%3Chttp%3A%2F%2Fschema.org%2FBook%3E%20%3B%20%3Chttp%3A%2F%2Fpurl.org%2Fdc%2Fterms%23issued%3E%20%3Fjaar%20%7D%20FILTER%28STR%28%3Fjaar%29%20%3E%3D%20%222015%22%20%26%26%20STR%28%3Fjaar%29%20%3C%3D%20%222025%22%29%20%7D%20GROUP%20BY%20%3Fjaar%20ORDER%20BY%20%3Fjaar`
4. **Take it easy.** robots.txt asks for 2 seconds between requests. Count and group on the server (COUNT, GROUP BY) instead of pulling millions of rows. The portal also has example queries and the data model.
   `https://data.bibliotheken.nl/KB/Production`

## Interesting combinations
- What did pamphlets and government say in the same year? → [nationaal-archief](https://www.opendataindex.nl/en/bron/nationaal-archief.md) via jaar en onderwerp. Put the STCN titles from 1672 or 1795 next to the States General records from those years in the National Archives finding aids.
- Who made prints and who printed books? → [rijksmuseum](https://www.opendataindex.nl/en/bron/rijksmuseum.md) via naam van drukker of maker. Many printers and publishers in the STCN also made or sold prints. Search their name as creator in the Rijksmuseum API.

## Similar sources
- [Delpher](https://www.delpher.nl): Also from the KB, but the digitised texts themselves (newspapers, books, magazines) rather than the catalogue data.
- [rijksmuseum](https://www.opendataindex.nl/en/bron/rijksmuseum.md): Museum objects instead of printed titles; also linked data and keyless.
- [europeana](https://www.opendataindex.nl/en/bron/europeana.md): European collections with digitised books and newspapers, but with an API key.

## What to watch out for
- **Not a measure of book production.** The NBT counts 67,175 titles for 2014, 23,339 for 2024 and 32,578 for 2025. Whether that reflects fewer books or cataloguing and deposit practice is not stated. Do not use the counts as a straight measure of what is published.
- **Not every collection is equally fresh.** Last modified according to the dataset descriptions (16 Oct 2026): authors (NTA) 17 September 2026, NBT 1 June 2026, STCN 23 February 2026, GTT October 2021 (in use until 2012).
- **Years are text.** Besides 1990 there are values such as 199X, 19XX and 2015-2016. Filter on four digits when counting per year, and say how many you left out.
- **Titles and copies mixed.** The stcn-titles graph holds both titles and copies (with schema:exampleOfWork). Count titles through schema:publication, or you count a book several times.

## API (SPARQL 1.1 (TriplyDB, Virtuoso))

- Base: https://data.bibliotheken.nl/sparql
- Access: No key or account needed (tested)
- Formats: json, sparql-results+json, csv, tsv, turtle, n-triples
- Size: 258524625 statements in 21 graphs (TriplyDB, last update 25 Sep 2026) (tested)
- Filtering: SPARQL 1.1: GRAPH to pick a collection, FILTER, VALUES, GROUP BY and COUNT to count on the server. Prefixes schema: = http://schema.org/ (http, not https), kb: = http://data.bibliotheken.nl/def#.; SELECT, CONSTRUCT, DESCRIBE, GRAPH, FILTER, VALUES, GROUP BY, COUNT(DISTINCT), OPTIONAL, pad/pad

### Endpoints
- `GET /sparql?query=…`: SPARQL query in the URL. Without an Accept header or with application/json: a flat JSON list of rows; with application/sparql-results+json the standard format; text/csv a table.
- `POST /sparql`: The same query as form field query= (handy for long queries).
- `GET https://api.bibliotheken.nl/datasets/KB/Production/services/Production-VTS/sparql`: The Virtuoso endpoint itself, which /sparql points to.
- `GET http://data.bibliotheken.nl/id/nbt/{ppn}`: Linked data: requesting a URI with Accept: text/turtle returns all triples about that title (via /describe?resource=).
- `GET /_api/datasets/KB/Production`: Dataset metadata (statements, graphCount, lastGraphsUpdateTime, licence).

### Parameters
- `query`: The SPARQL query (GET in the URL or POST as form field). (tested)
- `Accept`: application/sparql-results+json, application/json (flat list), text/csv, text/turtle (for CONSTRUCT/DESCRIBE and URIs). (tested)
- `GRAPH <http://data.bibliotheken.nl/nbt>`: Dutch national bibliography NBT: 4.9 million books (schema:Book), 194.8 million triples. (tested)
- `GRAPH <http://data.bibliotheken.nl/stcn-titles>`: STCN: 225,218 titles before 1801 plus copies, 13.5 million triples. Also stcn-persons and stcn-printers. (tested)
- `GRAPH <http://data.bibliotheken.nl/nta>`: Dutch author name thesaurus: 2.8 million persons, 41.8 million triples. (tested)
- `andere grafen`: corporaties, dbnla and dbnlt (DBNL authors and titles), gtt, kbcode, brinkman, rise-alba, rise-centsprenten, datasetbeschrijvingen, and per graph an *-invalidated one with withdrawn records. (tested)

### Predicates in the graphs (selection) (14)
`schema:name` tekst, `http://purl.org/dc/terms#issued` tekst, `schema:publication / schema:startDate` tekst, `schema:publication / sem:hasEarliestBeginTimeStamp, sem:hasLatestEndTimeStamp` gYear, `schema:publication / schema:publisher, schema:location` uri, `schema:author / schema:contributor` uri, `schema:inLanguage` tekst, `schema:isbn` tekst, `schema:about` uri, `schema:numberOfPages / schema:bookEdition / schema:bookFormat` tekst, `kb:ppn` tekst, `schema:birthDate / schema:deathDate / schema:hasOccupation` tekst, `schema:sameAs / owl:sameAs` uri, `schema:exampleOfWork` uri

### Limits and pitfalls
- robots.txt asks for Crawl-delay 2: wait 2 seconds between queries. (tested)
- Count and group on the server: a GROUP BY over all 4.9 million NBT books took 4 seconds, a count per graph over all 258 million triples under 1 second (probably from a cache). (tested)
- In the NBT the year is http://purl.org/dc/terms#issued (with a hash). Using the usual dcterms: prefix returns zero results without an error. (tested)
- stcn-titles holds both titles and copies. Count titles via schema:publication and with COUNT(DISTINCT). The result depends on the counting method: with DISTINCT 1672 gave 1,499 titles. (tested)
- Almost all schema.org predicates use http://schema.org/ (http). Exception: the image in stcn-titles is https://schema.org/image. Check the prefix with a count per predicate. (tested)
- Each graph has an *-invalidated counterpart with withdrawn records (nbt-invalidated, nta-invalidated …). Leave those out if you want current counts. (tested)

### Example
```
https://data.bibliotheken.nl/sparql?query=SELECT%20%3Fjaar%20%28COUNT%28DISTINCT%20%3Ft%29%20AS%20%3Ftitels%29%20WHERE%20%7B%20GRAPH%20%3Chttp%3A%2F%2Fdata.bibliotheken.nl%2Fstcn-titles%3E%20%7B%20%3Ft%20%3Chttp%3A%2F%2Fschema.org%2Fpublication%3E%2F%3Chttp%3A%2F%2Fschema.org%2FstartDate%3E%20%3Fjaar%20%7D%20VALUES%20%3Fjaar%20%7B%20%221671%22%20%221672%22%20%221673%22%20%7D%20%7D%20GROUP%20BY%20%3Fjaar%20ORDER%20BY%20%3Fjaar
```

## How to fetch it

### curl
```bash
curl -H "Accept: text/csv" --data-urlencode 'query=SELECT (COUNT(*) AS ?n) WHERE { GRAPH <http://data.bibliotheken.nl/nbt> { ?s a <http://schema.org/Book> } }' https://data.bibliotheken.nl/sparql
```

### python
```python
import requests, time

q = """SELECT ?jaar (COUNT(DISTINCT ?t) AS ?n) WHERE {
  GRAPH <http://data.bibliotheken.nl/stcn-titles> {
    ?t <http://schema.org/publication>/<http://schema.org/startDate> ?jaar } }
GROUP BY ?jaar ORDER BY ?jaar"""
r = requests.post("https://data.bibliotheken.nl/sparql", data={"query": q},
                  headers={"Accept": "application/sparql-results+json"})
for b in r.json()["results"]["bindings"]:
    if b["jaar"]["value"] in ("1672", "1787", "1795"):
        print(b["jaar"]["value"], b["n"]["value"])
time.sleep(2)  # robots.txt: Crawl-delay 2
```


## What is in it: 25 datasets and graphs

What the KB offers on data.bibliotheken.nl: the described datasets (NBT, STCN, thesauri) with the number of statements in their graph, and the graphs without a description of their own. Fetched every night.

- Kinds: dataset 18, graaf 7
- Last changed: 2026-10-16 (1 in the past 30 days)
- With an API call on the dataset page: 25
- Search: https://www.opendataindex.nl/api/catalogus/kb?q=… or the MCP function zoek_datasets with herkomst=kb
- Every row has its own page: https://www.opendataindex.nl/en/dataset/kb/<key>

### Folders
- KB-data (Production) (24): Beschreven datasets (17), Grafen zonder beschrijving (7)
- merian (1)

## Examples with real figures (1)

For each example the finding, the reference date and the call that fetches exactly these figures.

- **Printed titles from the Low Countries per year (STCN, KB): 1,364 titles in 1800, 49 times as much as in 1540** (1800). Table stcn-titles. [Example](https://www.opendataindex.nl/en/voorbeeld/kb-stcn-titels-per-jaar)
  - `https://data.bibliotheken.nl/sparql?query=SELECT%20%3Fjaar%20(COUNT(DISTINCT%20%3Ftitel)%20AS%20%3Fn)%20WHERE%20%7B%0A%20%20GRAPH%20%3Chttp%3A%2F%2Fdata.bibliotheken.nl%2Fstcn-titles%3E%20%7B%0A%20%20%20%20%3Ftitel%20a%20%3Chttp%3A%2F%2Fschema.org%2FBook%3E%20%3B%20%3Chttp%3A%2F%2Fschema.org%2Fpublication%3E%20%3Fpub%20.%0A%20%20%20%20%3Fpub%20%3Chttp%3A%2F%2Fschema.org%2FstartDate%3E%20%3Fjaar%20.%0A%20%20%7D%0A%7D%20GROUP%20BY%20%3Fjaar%20ORDER%20BY%20%3Fjaar`
