Design Museum Gent API
Home
Developers
Policy
Github
Swagger
Home
Developers
Policy
Github
Swagger
  • Start here

    • Overview
    • DCAT catalog
  • Objects

    • Browse objects
    • Single object
  • Agents

    • Browse agents
    • Single agent
  • Exhibitions

    • Browse exhibitions
    • Single exhibition
  • Concepts

    • Browse concepts
    • Single concept
  • Indexes

    • Colours
    • Production
    • Types
    • Materials
    • Nationalities
    • Similar

Similar

Info

Returns the objects whose photographs sit nearest to a given object's in embedding space. Nothing here was catalogued: the grouping falls out of the images themselves.

endpoint:

GET https://data.designmuseumgent.be/v2/id/object/{PID}/similar
GET https://data.designmuseumgent.be/v2/id/object/1975-0061/similar
GET https://data.designmuseumgent.be/v2/id/object/1975-0061/similar?limit=24
GET https://data.designmuseumgent.be/v2/id/object/1975-0061/similar?minSimilarity=0.85

Parameters

ParameterTypeDefaultDescription
limitinteger12Number of objects to return. Clamped to 1–50
minSimilarityfloat0Only return objects at or above this similarity. 0 returns everything ranked

What this measures

Each photograph is encoded as a 768-dimension vector by CLIP (ViT-L-14-openai), a model trained on images paired with the text found near them online. Images described in similar ways end up near each other.

The model has never seen this collection, was never told what a decorative tile is, and has no access to any catalogue record. It compares pictures.

That it works is easiest to see by example. The twelve nearest objects to 1975-0061, a tile painted with a sea creature, are all decorative tiles and almost all depict sea creatures — across different designs, workshops and centuries, at similarities of 0.92 to 0.95:

ObjectTitleSimilarity
1975-0069Decortegel met hippocampus0.9475
1740Decortegel met zeewezen0.9428
1693Decortegel met zeewezen0.9404
1751Decortegel met gevleugelde zeemeermin0.9344
1980-0085Decortegel met vis0.9342

No cataloguer ever recorded "sea creature" as a category.

What it cannot know

CLIP carries the associations of the corpus it was trained on, including its blind spots and its clichés. It will group objects for reasons that have nothing to do with design history — a shared backdrop, a similar crop, a photographic convention. Treat similarity as a measurement of two photographs, not as a judgement about two objects.


Coverage

7,307 of the 10,238 published objects carry an image embedding. Roughly three in ten have none, and for those this endpoint returns an empty collection.

An empty result is not an error

hydra:member: [] means this object has no embedding — not that it has no similar objects. The response is 200, because the object exists and the question is simply unanswerable for it. Clients should handle the empty case rather than assuming every object has neighbours.


Multiple views per object

The embedding table holds 12,325 vectors for 7,307 objects: an object photographed from six angles has six vectors.

Ranking naively would let one object's six views fill the whole result, so each candidate object is reduced to its single closest view before ranking. Each object therefore appears at most once, and its similarity is that of its best-matching photograph.

The query object is anchored on its first image, deterministically, so repeated requests return the same neighbours.


Response fields

FieldDescription
hydra:totalItemsNumber of objects returned — not a total available count
hydra:memberObjects, nearest first

Each member:

FieldDescription
@idURI of the object — resolvable to the full record
@typeAlways crm:E22_Human-Made_Object
rdfs:labelDutch title
similarityCosine similarity of the two closest photographs, 0–1
crm:P138i_has_representationValidated IIIF images, as elsewhere in the API
imageConvenience field repeating the first image
crm:P129i_is_subject_ofIIIF manifest, when available

Reading similarity

Cosine similarity between unit vectors. Useful for ranking and for judging how tight a set is — not a probability and not a percentage of anything. In practice close matches sit around 0.92 and unrelated objects around 0.60, but those figures are characteristic of this collection rather than absolute.


Example response

{
  "@context": {
    "crm": "http://www.cidoc-crm.org/cidoc-crm/",
    "rdfs": "http://www.w3.org/2000/01/rdf-schema#",
    "hydra": "http://www.w3.org/ns/hydra/core#"
  },
  "@id": "https://data.designmuseumgent.be/v2/id/object/1975-0061/similar",
  "@type": "hydra:Collection",
  "rdfs:label": "Objects visually similar to 1975-0061",
  "hydra:totalItems": 12,
  "hydra:member": [
    {
      "@id": "https://data.designmuseumgent.be/v2/id/object/1975-0069",
      "@type": "crm:E22_Human-Made_Object",
      "rdfs:label": "Decortegel met hippocampus",
      "similarity": 0.9475,
      "image": {
        "@id": "https://beeldbank-temp.stad.gent/iiif/image/8025/full/max/0/default.jpg",
        "@type": "crm:E38_Image",
        "thumbnail": "https://beeldbank-temp.stad.gent/iiif/image/8025/full/thm/0/default.jpg"
      }
    }
  ]
}

Building a thread

Because each neighbour is itself an object, the endpoint composes: take any result and ask for its neighbours.

// Walk outward from a starting object, following the closest match each time.
async function thread(pid, steps = 5) {
    const path = [pid]

    for (let i = 0; i < steps; i++) {
        const res = await fetch(
            `https://data.designmuseumgent.be/v2/id/object/${pid}/similar?limit=5`,
            { headers: { Accept: 'application/ld+json' } }
        )
        const data = await res.json()
        const members = data['hydra:member']

        // No embedding, or nowhere left to go.
        if (!members.length) break

        // Skip anything already visited, or the walk oscillates between a
        // mutually-nearest pair forever.
        const next = members.find(m => !path.includes(m['@id'].split('/').pop()))
        if (!next) break

        pid = next['@id'].split('/').pop()
        path.push(pid)
    }

    return path
}

Mutual nearest neighbours

Two objects are very often each other's closest match, so a naive walk gets stuck bouncing between them immediately. Track what you have visited, as above.

Last Updated:: 9/20/26, 11:39 AM
Contributors: Olivier.VanD'huynslager
Prev
Nationalities