The RembrandtOpen Impression Project

Machine-readable

Public API

The whole record, read-only, as JSON, JSON-LD, CSV, BibTeX and IIIF. No key, no sign-up, no terms to accept. The metadata is CC0 1.0 — take it, check it, and publish the corrections.
Base URL
https://rembrandt.aidispatch.xyz/api/v1
Authentication
None. There are no keys and no quotas to apply for.
Metadata licence
CC0 1.0 Universal (Public Domain Dedication) https://creativecommons.org/publicdomain/zero/1.0/
Rate limit
120 requests per minute per IP address on the endpoints that take parameters.
CORS
Access-Control-Allow-Origin: * on every endpoint. Fetch it from a browser.
Caching
Cache-Control: public, max-age=3600
Dataset version
0.9, released 2026-08-12

Before you start

Four things that will save you a round trip.

Fetch the whole thing once

https://rembrandt.aidispatch.xyz/api/v1/dataset returns every record in one response — object, claims, evidence, census, questions, provenance, bibliography, states, changelog, glossary, comparators. It is a few hundred kilobytes. Pull it once per release and work locally rather than crawling the individual routes.

Every response is deterministic

Nothing here carries a generation timestamp. Responses are assembled from the released dossier version, so the same release always produces the same bytes and a checksum of a response means something. To detect change, compare dataset_version or read the changelog.

Claims arrive with their evidence

You never have to make a second request to find out what an assertion rests on. Every claim carries its evidence objects inline, each with the tier of the source and a URL where a third party can check it.

Confidence is about the evidence

confidence describes the quality of the material under a claim and nothing else. It is not a measure of how much the project would like the claim to be true, and it cannot be raised without adding a source.

A worked example

Everything below assumes curl and jq, but any HTTP client will do.
# the whole record in one request, then work locally
curl -sSL https://rembrandt.aidispatch.xyz/api/v1/dataset -o dataset.json

# which institutions hold a documented state II?
curl -sSL 'https://rembrandt.aidispatch.xyz/api/v1/census?state=II' \
  | jq -r '.records[] | [.institution, .city, .accession_number] | @tsv'

# the census as a spreadsheet
curl -sSL https://rembrandt.aidispatch.xyz/api/v1/census.csv -o census.csv

# check what a claim rests on before you cite it
curl -sSL https://rembrandt.aidispatch.xyz/api/v1/claims \
  | jq '.records[] | select(.id == "CL-STATE") | {value, confidence, evidence: [.evidence[].title]}'

Endpoints

Collection endpoints share one envelope: { dataset_version, dataset_date, licence, type, count, …, records }. The array is always last, so a human reading the raw response sees the metadata before the payload. Examples below are trimmed to the shape; the real responses carry every field.

GET /api/v1/object

application/json
The object record, plus every active claim about it with the evidence resolved inline, plus the authority identifiers with their verification status. Identifiers that could not be confirmed at their own authority — VIAF, FAST, BnF, Art UK — are published here carrying verified: false and are deliberately kept out of the JSON-LD and the IIIF manifest, where a bare URI reads as an assertion of fact.
{
  "dataset_version": "0.9",
  "object": {
    "id": "np-rmb-001",
    "accession": "NP-RMB-001",
    "title": "Self-Portrait in a Velvet Cap with Plume",
    "date": "1638",
    "medium": "Etching, possibly with drypoint"
  },
  "claim_count": 18
}

GET /api/v1/object.jsonld

application/ld+json
schema.org VisualArtwork. Only properties that exist in schema.org: there is no provenance property and no inventoryNumber property, so custody history is carried as a PropertyValue in additionalProperty and the accession number as an identifier with propertyID: "accession number". Dimensions are QuantitativeValue with unitCode: "MMT".
{
  "@context": "https://schema.org",
  "@type": "VisualArtwork",
  "name": "Self-Portrait in a Velvet Cap with Plume",
  "artMedium": "Etching, possibly with drypoint",
  "creator": {
    "@type": "Person",
    "name": "Rembrandt Harmenszoon van Rijn",
    "sameAs": [
      "http://vocab.getty.edu/ulan/500011051",
      "http://www.wikidata.org/entity/Q5598"
    ]
  }
}

The Wikidata item for the 1638 self-portrait identifies the work and the plate, not this sheet, so it is emitted as exampleOfWork and never as sameAs. Many impressions exist; they are different objects.

GET /api/v1/census

application/json
Every located impression, one record each, with the URL of the institutional record it was transcribed from. 27 records at this version. Where institutions disagree, both readings are carried: state is the normalised, filterable value and is null wherever the holding institution states none, while state_verbatim preserves the source’s exact wording.
?state=
Normalised New Hollstein state — I, II, IV. Rows whose institution states no state are excluded rather than guessed at.
?holder=
museum · library · university · private · unlocated · art_market
?country=
Country of the current custodian, e.g. Netherlands.
{
  "type": "census",
  "count": 27,
  "records": [
    {
      "id": "CN-rijksmuseum-rp-p-1961-989",
      "institution": "Rijksmuseum",
      "city": "Amsterdam",
      "accession_number": "RP-P-1961-989",
      "state": "I",
      "source_url": "https://www.rijksmuseum.nl/en/collection/RP-P-1961-989"
    }
  ]
}

Filters combine with AND and are case-insensitive. Unknown parameters are ignored and named back to you in the response rather than treated as an error.

GET /api/v1/census.csv

text/csv
The same records as a spreadsheet: 27 rows, 39 columns, RFC 4180 quoting, CRLF line endings, and a UTF-8 byte order mark so Excel does not mangle the “×” in every dimension field. Repeating fields (collector marks, exhibitions, literature) are joined with a semicolon.
id,holder_type,institution,city,country,accession_number,state
CN-rijksmuseum-rp-p-1961-989,museum,Rijksmuseum,Amsterdam,Netherlands,RP-P-1961-989,I
CN-the-british-museum-1910-0212-376,museum,The British Museum,London,United Kingdom,1910-0212-376,II

GET /api/v1/claims

application/json
Every claim on the record with its evidence inline. Superseded and withdrawn claims are included and marked, never deleted — a record that quietly drops what it used to say cannot be audited.
{
  "type": "claims",
  "count": 18,
  "records": [
    {
      "id": "CL-ARTIST",
      "field": "Artist",
      "value": "Rembrandt Harmenszoon van Rijn (1606–1669)",
      "confidence": "probable",
      "evidence": [
        {
          "id": "EV-ACQ-INVOICE",
          "tier": "primary_document",
          "title": "Bonhams invoice and purchase documentation, New York, 2023"
        }
      ]
    }
  ]
}

GET /api/v1/evidence

application/json
Every source the record rests on, with its tier (1 strongest, 8 weakest) and the claims that cite it. Tier is a property of the source, not of how convenient its contents are. Evidence held privately is listed with the reason and the terms on which a copy can be seen.
{
  "type": "evidence",
  "count": 5,
  "records": [
    {
      "id": "EV-ACQ-INVOICE",
      "tier": "primary_document",
      "tier_rank": 2,
      "title": "Bonhams invoice and purchase documentation, New York, 2023",
      "access": "on_request",
      "cited_by": [
        "CL-ACQUISITION"
      ]
    }
  ]
}

GET /api/v1/questions

application/json
The 12 open research questions: why each matters, what evidence exists now, what would settle it, and — under negative_findings — the searches already made that produced nothing, so nobody repeats them.
{
  "type": "questions",
  "count": 12,
  "records": [
    {
      "id": "RQ-01",
      "slug": "does-this-impression-carry-the-signature",
      "title": "Does this impression carry the signature “Rembrandt f. 1638”, and if not, why not?",
      "status": "open",
      "difficulty": "low",
      "suitable_for_student": true
    }
  ]
}

GET /api/v1/provenance

application/json
The custody timeline, gaps included. A gap is a first-class event with is_gap: true and the same fields as any other, because an unbroken-looking chain with the undocumented centuries removed is a fiction.
{
  "type": "provenance",
  "gaps": 1,
  "records": [
    {
      "id": "PV-003",
      "type": "gap",
      "is_gap": true,
      "date_display": "1638 – 2023",
      "title": "No documented ownership",
      "confidence": "unknown"
    }
  ]
}

GET /api/v1/bibliography

application/json
13 entries, 3 of them verified against a published record. Fields reading UNVERIFIED were not confirmed and were not filled in from memory — that is how a wrong citation enters the literature and stays there.
{
  "type": "bibliography",
  "verified": 3,
  "records": [
    {
      "id": "white-boon-1969",
      "type": "catalogue_raisonne",
      "authors": [
        "Christopher White",
        "Karel G. Boon"
      ],
      "title": "Rembrandt's Etchings: An Illustrated Critical Catalogue",
      "year": 1969,
      "verified": true
    }
  ]
}

GET /api/v1/bibliography.bib

application/x-bibtex
The same entries as BibTeX. Catalogues raisonnés and exhibition catalogues map to @book, articles to @article, chapters to @incollection, websites and datasets to @misc with howpublished and urldate. Titles are double-braced so styles that impose sentence case cannot lowercase a proper noun, and every unverified entry says so in its note.
@book{white-boon-1969,
  author       = {Christopher White and Karel G. Boon},
  title        = {{Rembrandt's Etchings: An Illustrated Critical Catalogue}},
  publisher    = {Van Gendt & Co.},
  address      = {Amsterdam},
  year         = {1969},
  note         = {Bibliographic details confirmed against a published record.}
}

GET /api/v1/states

application/json
The state atlas, plus the census records attributed to each state. Read divisions first: catalogues disagree about how many states this plate has, and each division is recorded with the catalogue asserting it rather than flattened into one number.
{
  "type": "states",
  "catalogue": "New Hollstein (Hinterding & Rutgers, Rembrandt)",
  "count": 4,
  "divisions": 5,
  "records": [
    {
      "state": "I",
      "headline": "Before the jaw is strengthened",
      "documented": true
    }
  ]
}

GET /api/v1/glossary

application/json
The 28 terms the record uses, defined once, with a source where the definition is not the project's own.
{
  "type": "glossary",
  "count": 28,
  "records": [
    {
      "term": "Impression",
      "category": "technique"
    }
  ]
}

GET /api/v1/changelog

application/json
Every change to the canonical record, newest first, each with the reason it was made and the credit where someone outside the project prompted it. Nothing changes without an entry here.
{
  "type": "changelog",
  "count": 5,
  "records": [
    {
      "id": "CH-001",
      "date": "2026-08-12",
      "type": "release",
      "summary": "Record published at version 0.9"
    }
  ]
}

GET /api/v1/dataset

application/json
Everything, in one envelope, with per-entity counts. This is the endpoint to build on: fetch it once per release rather than polling the others.
{
  "@context": "https://schema.org",
  "@type": "Dataset",
  "dataset_version": "0.9",
  "dataset_date": "2026-08-12",
  "counts": {
    "claims": 18,
    "census": 27,
    "questions": 12,
    "comparators": 8
  }
}

IIIF

Presentation API 3.0. Drop these into Mirador, Universal Viewer, Clover or anything else that speaks IIIF.

GET /iiif/np-rmb-001/manifest.json

application/ld+json;profile="http://iiif.io/api/presentation/3/context.json"
The manifest for the object. It has three canvases — recto, verso and transmitted light — and none of them carries an image, because no photograph of this sheet exists. Each canvas has a label, a summary saying the view has not been captured, and no painting annotation. A viewer will show empty frames. That is the state of the record, not a fault in the manifest.

Two things to know if you validate it. The canvases carry behavior: ["placeholder"], which is not one of the registered Presentation 3.0 behavior values and will be flagged as unrecognised; it is an extension value, and the authoritative statement is in each canvas’s summary. And the canvas height/width are the recorded dimensions of the sheet in millimetres rather than pixels of a master image, since there is no master image; canvas dimensions are unitless and carry only the aspect ratio.

GET /iiif/comparators/collection.json

application/ld+json
A Collection of the 8 openly licensed comparator impressions, each as a Manifest reference with a thumbnail and a homepage pointing at the holding institution’s own record.

GET /iiif/comparators/{id}/manifest.json

application/ld+json
One manifest per comparator, with a real painting annotation over the derivative hosted here, the institution’s exact credit line in requiredStatement, and rights set from the institution’s own licence — CC0 or the Public Domain Mark. Where a licence cannot be mapped confidently the property is omitted rather than guessed.

Ids are the comparator ids from the dataset, for example rijksmuseum-rp-p-1961-989.

Rate limit

120 requests per minute per IP address, counted in a fixed window.

Most endpoints are generated at build time and served as static files, so the limiter never runs for them. It applies in practice to the two endpoints that take parameters — /api/v1/census with filters and /api/v1/search — which are the only ones a runaway script can actually make work.

Over the limit you get 429 with a Retry-After header in seconds. Every response also carries X-RateLimit-Limit and X-RateLimit-Remaining so you can pace yourself without being refused first.

The counter is held in memory in a single process and resets on deploy. That is stated plainly because it is true: it exists to stop a loop, not to meter anything. If you need a lot of data quickly, take /api/v1/dataset once — it is one request and it contains everything — or write and we will send you a dump.

{
  "error": "rate_limited",
  "limit": 120,
  "window_seconds": 60,
  "retry_after_seconds": 37
}

Versioning and stability

What will change, what will not, and how you will know.
URL version
/api/v1/. A breaking change to the shape of a response — a removed field, a renamed key, a changed type — gets a new path, and v1 keeps working alongside it. Added fields are not breaking; write your parser so it ignores keys it does not recognise.
Data model version
1.0.0, published on every response. It tracks the shape of the data, independently of the record’s contents.
Dataset version
0.9, released 2026-08-12. It tracks the contents. Compare it to find out whether anything has changed, and read the changelog to find out what.
Identifiers
Object, claim, evidence, census, question, provenance and bibliography ids are permanent. A superseded claim keeps its id and gains a status; it is never reused and never deleted.
Corrections
This record is expected to be wrong in places. Corrections are the most useful thing you can send, and they are credited — how to send one.

Citing the dataset

Cite the version you used. No DOI has been minted yet, and the reason is on the publications page: a DOI makes a version permanently citable, and a permanent citation of an unchecked record is worse than no citation at all.

Plain citation

The Rembrandt Open Impression Project. Self-Portrait in a Velvet Cap with Plume: open research record for impression NP-RMB-001. Version 0.9. Dataset. 2026-08-12. https://rembrandt.aidispatch.xyz/api/v1/dataset.

BibTeX

@misc{rembrandt_open_impression_0_9,
  author       = {{The Rembrandt Open Impression Project}},
  title        = {{Self-Portrait in a Velvet Cap with Plume: open research record for impression NP-RMB-001}},
  year         = {2026},
  version      = {0.9},
  howpublished = {Data set},
  url          = {https://rembrandt.aidispatch.xyz/api/v1/dataset},
  note         = {Metadata dedicated to the public domain under CC0 1.0. No DOI has been minted yet.}
}

If you are citing a single assertion rather than the dataset, cite the claim id — every claim on this record has a permanent one, and it carries its own evidence and confidence with it.

What is not in the API

  • Images of this impression. There are none. The sheet has not been photographed, and the project does not publish a stand-in picture. The comparator images are of other impressions, held elsewhere, and each says so.
  • Anything about what the object is worth. This project does not buy, sell, value or broker works of art, and publishes no figures of that kind.
  • Custody detail. Location is published to the level of a city. Storage, security and insurance arrangements are not held in this database at all, so there is nothing here to leak.
  • Personal data. Evidence that carries a private individual’s name or address is marked on_request and supplied redacted, to scholars and institutions.
  • Write access. The API is read-only. Send corrections through the contribution route so they land in the changelog with your name on them.