v2.6.0 — Time as an index
v2.6.0 — Time as an index
A new endpoint carrying two measures of time — when the collection's objects were made, and when the museum acquired them — plus hex values for the colour index, and the fix that made the first of those possible: production dates were empty on every object, which had silently disabled four documented filters.
All changes are additive within v2. No field has been removed or renamed, and the JSON-LD structure is unchanged.
New: GET /v2/id/production
Two different times run through a collection: when each object was made, and when the museum took it in. They are rarely the same, and this endpoint returns both on the same periods.
curl 'https://data.designmuseumgent.be/v2/id/production?bucket=10&yearFrom=1600'
Production — a span, so it is weighted
This is not a histogram, and the difference matters. Only about a quarter of dated objects carry an exact year. The rest are ranges — 2,376 of them span 26 to 100 years, and 362 span more than a century. Counting first years would pin an object dated 1900/2000 to 1900 and draw it as confidently as one dated 1937, inventing precision for three quarters of the collection.
Instead each object contributes a total weight of exactly 1, spread evenly across every year it might have been made in. An object dated 1937 puts its whole weight on one year; an object dated 1900/2000 puts 1/101 on each of 101 years. Total area is therefore the number of dated objects, so a bucket's weight is a real share of the collection — and vague dating shows up as flatness rather than as a false peak.
Four measures per bucket, answering different questions:
| Field | What it means |
|---|---|
| weight | The distributed measure. The honest one — a genuine share of the collection |
| object_count | Objects whose span merely touches the bucket. Wide spans count in full in every bucket they cross, so this sums to far more than the collection and is not a share of anything |
| exact_count | Objects dated to a single year. The subset needing no interpretation |
| spread_factor | object_count / weight — how vaguely this period is dated. Around 9 in the fifteenth century, around 2 in the twentieth |
The 1980s are the collection's defining decade — more objects acquired than any other, most of them already decades old. After 2010 the relationship inverts: production falls to 124 while acquisition holds at 828. And both thin sharply through the 1930s and 1940s.
Parameters: ?bucket= (5–100 years, default 10), ?yearFrom=, ?yearTo=, ?onDisplay=, ?q=. Every bucket carries a ready-to-use filter URL listing its objects.
Colours you can actually render
GET /v2/id/colors now returns a hex for every base colour and named tone, plus a swatches array breaking each base colour into its constituent tones.
This was needed because the names in css_colors are from the extended Wikipedia/xkcd lists, not CSS keywords — Davy's grey, Grullo and Tuscan tan cannot be passed to a stylesheet, and nothing in the API previously told you what colour they were.
Each hex is a weighted centroid of every occurrence of that tone, averaged in linear light rather than on gamma-encoded sRGB. Channel-wise hex averaging systematically darkens and desaturates, which is the one failure mode that matters when the output is a colour swatch.
swatches exists because a base colour is a bucket, not a tone: orange spans #5a3f29 to #b79585, and a single averaged value lands on an unrepresentative muddy brown.
Also new: ?minCount= to suppress rare tones, and ?swatchesPerBase= to control how many constituent tones each base returns.
Fixed: production dates were empty on every object
production_year_begin and production_year_end were NULL on all 10,238 healthy records. That silently disabled ?date=, ?dateFrom=, ?dateTo= and sortBy=dateBegin / dateEnd — all four documented, all four returning nothing.
Two faults in the extraction:
crm:P108i_was_produced_byandcrm:P94i_was_created_byare arrays, and using->with a text key on an array returns NULL on every row.crm:P82a_begin_of_the_beginis an object —{"@type": "xsd:gYear", "@value": "1898"}— so->>serialised the whole thing rather than reading the year.
Digit handling was also wrong: "1858-03-25" became 18580325 rather than 1858.
9,809 of 10,238 healthy objects now carry a usable production span.
Behaviour change worth knowing: the two columns now take the earliest begin and latest end across both production and creation events, rather than preferring production. An object designed in 1898 and produced in 1902 spans 1898–1902 rather than reporting 1902 alone. No consumer has seen the old behaviour, since the columns were always empty.
Fixed: French concept search returned nothing
?conceptSearch=chaise returned 0 results. chair and stoel returned around 900 each.
The thesaurus vector was built with the French stemmer — chaise becomes chais — while the endpoint queries with the simple config, which does no stemming. The two never met. Dutch and English worked only by luck, because stoel and chair happen to survive their stemmers unchanged.
Fixed by adding unstemmed copies alongside the stemmed vectors on both the thesaurus and the objects table. The same fault affected ?q= against English and French titles, which are indexed with their own stemmers but queried with dutch.
This restores exact-word matching in all three languages. It does not give plurals: chaises still will not find chaise.
Changed: redirecting records excluded from the objects collection
A record whose persistent URI redirects elsewhere — merged, renumbered or withdrawn — no longer appears in GET /v2/id/objects. It stays fully resolvable at /v2/id/object/{PID}, returning 301 or 410 as before.
58 objects are affected. If you maintain a mirror built from the objects collection, those records will disappear on your next sync without any signal — the same limitation already documented for ?modifiedSince=. Reconcile against a full pass rather than relying on incremental sync alone, and check individual PIDs at the object endpoint to find out what happened to them.
Also
- Landing page at
https://data.designmuseumgent.be/with content negotiation.Accept: text/htmlgets a human-readable page; RDF types get303 See Otherto the DCAT catalog. The catalog keeps one canonical URI — the root points at it rather than becoming a second place it lives. MASTER_RESYNCno longer aborts on a single malformed record. A set-returning function in aFROMlist is evaluated before theWHEREclause filters anything, so type guards written besidejsonb_array_elements(...)never ran. One object-shaped record was enough to stop the entire table update.
Documentation
- New reference page for
/v2/id/production ?onDisplay=corrected — it is tri-state, not boolean. Omitting it returns everything;onDisplay=falseactively selects objects not on display, a different and much smaller setcss_colorsdocumented as extended Wikipedia/xkcd names rather than CSS keywords- Base colour list corrected to twelve —
beigeexists in the data and was missing from the documented eleven
Scope
Every figure these endpoints return describes the objects published through this API — around ten thousand of the twenty-four thousand the museum holds. Which objects have been published, and in what order, is a decision made by people, and it shapes every count, share and curve. This is a view of the catalogue, not of the collection.
Full changelog: https://github.com/DesignMuseumGent/dmg-rest-api/blob/main/CHANGELOG.md Documentation: https://api.designmuseumgent.be
# v2.6.0 — Time as an indexA new endpoint carrying two measures of time — when the collection's objects were made, and when the museum acquired them — plus hex values for the colour index, and the fix that made the first of those possible: production dates were empty on every object, which had silently disabled four documented filters.
All changes are additive within v2. No field has been removed or renamed, and the JSON-LD structure is unchanged.
New: GET /v2/id/production
Two different times run through a collection: when each object was made, and when the museum took it in. They are rarely the same, and this endpoint returns both on the same periods.
curl 'https://data.designmuseumgent.be/v2/id/production?bucket=10&yearFrom=1600'Production — a span, so it is weighted
This is not a histogram, and the difference matters. Only about a quarter of dated objects carry an exact year. The rest are ranges — 2,376 of them span 26 to 100 years, and 362 span more than a century. Counting first years would pin an object dated 1900/2000 to 1900 and draw it as confidently as one dated 1937, inventing precision for three quarters of the collection.
Instead each object contributes a total weight of exactly 1, spread evenly across every year it might have been made in. An object dated 1937 puts its whole weight on one year; an object dated 1900/2000 puts 1/101 on each of 101 years. Total area is therefore the number of dated objects, so a bucket's weight is a real share of the collection — and vague dating shows up as flatness rather than as a false peak.
Four measures per bucket, answering different questions:
| Field | What it means |
|---|---|
weight |
The distributed measure. The honest one — a genuine share of the collection |
object_count |
Objects whose span merely touches the bucket. Wide spans count in full in every bucket they cross, so this sums to far more than the collection and is not a share of anything |
exact_count |
Objects dated to a single year. The subset needing no interpretation |
spread_factor |
object_count / weight — how vaguely this period is dated. Around 9 in the fifteenth century, around 2 in the twentieth |
That last figure is not a fact about the objects. It measures when record-keeping became precise.
Acquisition — a point, so it is counted
An acquisition date is not a span. The museum took the object on one day, so acquired is a plain count with no weighting.
It is deliberately spiky, and that is correct: collections grow in events. 173 objects share an acquisition date of 1990-02-16, 152 share 2020-09-10, 136 share 2012-04-20 — single bequests and purchases entering on one date. A smooth curve would misrepresent how a collection actually grows. The series begins in 1904, a year after the museum was founded.
Coverage: 9,809 of 10,238 published objects carry a production span, 7,481 an acquisition year.
What the pair shows
| Decade | Production weight | Acquired |
|---|---|---|
| 1930 | 524 | 15 |
| 1940 | 192 | 9 |
| 1980 | 675 | 2,417 |
| 1990 | 1,503 | 1,790 |
| 2010 | 124 | 828 |
The 1980s are the collection's defining decade — more objects acquired than any other, most of them already decades old. After 2010 the relationship inverts: production falls to 124 while acquisition holds at 828. And both thin sharply through the 1930s and 1940s.
Parameters: ?bucket= (5–100 years, default 10), ?yearFrom=, ?yearTo=, ?onDisplay=, ?q=. Every bucket carries a ready-to-use filter URL listing its objects.
📖 [Full reference](https://api.designmuseumgent.be/v2/production)
Colours you can actually render
GET /v2/id/colors now returns a hex for every base colour and named tone, plus a swatches array breaking each base colour into its constituent tones.
This was needed because the names in css_colors are from the extended Wikipedia/xkcd lists, not CSS keywords — Davy's grey, Grullo and Tuscan tan cannot be passed to a stylesheet, and nothing in the API previously told you what colour they were.
Each hex is a weighted centroid of every occurrence of that tone, averaged in linear light rather than on gamma-encoded sRGB. Channel-wise hex averaging systematically darkens and desaturates, which is the one failure mode that matters when the output is a colour swatch.
swatches exists because a base colour is a bucket, not a tone: orange spans #5a3f29 to #b79585, and a single averaged value lands on an unrepresentative muddy brown.
Also new: ?minCount= to suppress rare tones, and ?swatchesPerBase= to control how many constituent tones each base returns.
Fixed: production dates were empty on every object
production_year_begin and production_year_end were NULL on all 10,238 healthy records. That silently disabled ?date=, ?dateFrom=, ?dateTo= and sortBy=dateBegin / dateEnd — all four documented, all four returning nothing.
Two faults in the extraction:
crm:P108i_was_produced_byandcrm:P94i_was_created_byare arrays, and using->with a text key on an array returns NULL on every row.crm:P82a_begin_of_the_beginis an object —{"@type": "xsd:gYear", "@value": "1898"}— so->>serialised the whole thing rather than reading the year.
Digit handling was also wrong: "1858-03-25" became 18580325 rather than 1858.
9,809 of 10,238 healthy objects now carry a usable production span.
Behaviour change worth knowing: the two columns now take the earliest begin and latest end across both production and creation events, rather than preferring production. An object designed in 1898 and produced in 1902 spans 1898–1902 rather than reporting 1902 alone. No consumer has seen the old behaviour, since the columns were always empty.
Fixed: French concept search returned nothing
?conceptSearch=chaise returned 0 results. chair and stoel returned around 900 each.
The thesaurus vector was built with the French stemmer — chaise becomes chais — while the endpoint queries with the simple config, which does no stemming. The two never met. Dutch and English worked only by luck, because stoel and chair happen to survive their stemmers unchanged.
Fixed by adding unstemmed copies alongside the stemmed vectors on both the thesaurus and the objects table. The same fault affected ?q= against English and French titles, which are indexed with their own stemmers but queried with dutch.
This restores exact-word matching in all three languages. It does not give plurals: chaises still will not find chaise.
Changed: redirecting records excluded from the objects collection
A record whose persistent URI redirects elsewhere — merged, renumbered or withdrawn — no longer appears in GET /v2/id/objects. It stays fully resolvable at /v2/id/object/{PID}, returning 301 or 410 as before.
58 objects are affected. If you maintain a mirror built from the objects collection, those records will disappear on your next sync without any signal — the same limitation already documented for ?modifiedSince=. Reconcile against a full pass rather than relying on incremental sync alone, and check individual PIDs at the object endpoint to find out what happened to them.
Also
- Landing page at
https://data.designmuseumgent.be/with content negotiation.Accept: text/htmlgets a human-readable page; RDF types get303 See Otherto the DCAT catalog. The catalog keeps one canonical URI — the root points at it rather than becoming a second place it lives. MASTER_RESYNCno longer aborts on a single malformed record. A set-returning function in aFROMlist is evaluated before theWHEREclause filters anything, so type guards written besidejsonb_array_elements(...)never ran. One object-shaped record was enough to stop the entire table update.
Documentation
- New reference page for
/v2/id/production ?onDisplay=corrected — it is tri-state, not boolean. Omitting it returns everything;onDisplay=falseactively selects objects not on display, a different and much smaller setcss_colorsdocumented as extended Wikipedia/xkcd names rather than CSS keywords- Base colour list corrected to twelve —
beigeexists in the data and was missing from the documented eleven
Scope
Every figure these endpoints return describes the objects published through this API — around ten thousand of the twenty-four thousand the museum holds. Which objects have been published, and in what order, is a decision made by people, and it shapes every count, share and curve. This is a view of the catalogue, not of the collection.
Full changelog: https://github.com/DesignMuseumGent/dmg-rest-api/blob/main/CHANGELOG.md
Documentation: https://api.designmuseumgent.be