Skip to main content

Snapshots

A snapshot freezes query results. You give Andromeda one or more queries, it runs them once, stores everything they matched, and from then on you read and write that stored copy under its own identifier. A live query answered page by page can span minutes, so its later pages hold values the earlier ones never saw. A snapshot answers from one instant instead, so every read sees the same data.

This guide shows how to create a snapshot, work under it, watch it become ready, and discard or clone it. The configuration keys live under Snapshots.

Creating a snapshot​

Send the queries whose results you want to keep to POST /ngsi-ld/v1/snapshots.

POST /ngsi-ld/v1/snapshots
Content-Type: application/json

{
"id": "urn:ngsi-ld:Snapshot:1",
"type": "Snapshot",
"snapshotQueries": [
{
"type": "Query",
"entities": [
{
"type": "Vehicle"
}
]
}
]
}
HTTP/1.1 201 Created
Location: /ngsi-ld/v1/snapshots/urn:ngsi-ld:Snapshot:1

The answer is 201 with an empty body and a Location header naming the snapshot. If you leave the id out, the broker generates one. A second create under an id that already exists answers 409 AlreadyExists.

Alongside snapshotQueries you can set snapshotTemporalQueries, temporal queries whose results keep the history they matched. Each entry carries a temporalQ, and a snapshotQueries entry carries none.

{
"type": "Query",
"entities": [
{
"type": "Vehicle"
}
],
"temporalQ": {
"timerel": "after",
"timeAt": "2026-10-24T08:00:00Z"
}
}

snapshotLifetime is an ISO 8601 duration asking how long the snapshot should live. The broker caps it at snapshot.max_lifetime, 30 days by default, and stores the resulting expiresAt. An hour is the default when you ask for nothing. snapshotPriority is an integer from 1 to 10, 5 by default, and when a tenant runs over its snapshot quota, the broker deletes low-priority snapshots first. endpoint and receiverInfo say where to send a notification when the snapshot is ready, and which headers to add to it.

At least one of snapshotQueries and snapshotTemporalQueries is required on create, and an empty snapshotQueries counts as present. A query that breaks its own shape, such as an empty entities array or an entity query carrying temporalQ, is refused with 400 BadRequestData.

The broker answers before it runs the queries. The capture then proceeds in the background. Each query runs as the ordinary query would, including every page and every registered context source, and the results are stored. A temporal capture stores history, whether it came from the local store or from a peer. While that runs, the snapshot's status reads preparing.

Working under a snapshot​

Every Core, Temporal and entity-subscription operation accepts an NGSILD-Snapshot header naming a snapshot. The operation then applies to the snapshot alone.

GET /ngsi-ld/v1/entities/urn:ngsi-ld:Vehicle:A4567
Accept: application/json
NGSILD-Snapshot: urn:ngsi-ld:Snapshot:1
{
"id": "urn:ngsi-ld:Vehicle:A4567",
"type": "Vehicle",
"speed": {
"type": "Property",
"value": 7
}
}

The same request without the header answers the live entity, which may already hold newer values. The header is echoed on success and on error alike, so you can always tell which copy answered. In Rust, client.on_snapshot(&snapshot) gives the same handles as the client, each aimed at that snapshot.

Under a snapshot you can work as follows.

  • Read and write entities, with retrieve, query, create, update, merge, replace, delete, and the batch forms.
  • Read discovery, where the available types and attributes, and their information, describe what the snapshot holds.
  • Read and write history, with temporal retrieval, temporal queries, aggregation, and temporal writes, which add to the snapshot's history alone.
  • Subscribe, where a subscription created under a snapshot hears only the writes made under it, and its notifications carry the NGSILD-Snapshot header.

A write under a snapshot changes the snapshot and never the live entity. A snapshot is its own working copy. Create an entity under it and it reads there alone. Update speed under it and the live speed stays as it is. A Core write under a snapshot also records history, in the snapshot's history.

Operations under a snapshot are always local. A local=false parameter is ignored, and no registration is contacted, because the frozen copy is the whole point. A snapshot-scoped request never creates a tenant either, so naming a tenant that does not exist answers 404 NonexistentTenant.

A snapshot that is still preparing holds up an operation until its capture finishes, or until snapshot.preparation.scoped_operation_timeout passes, 20 seconds by default, after which the operation answers 503 with a Retry-After header. A snapshot that ended empty is still usable, since its capture ran and matched nothing, which is a finished result.

The header is refused with 400 BadRequestData when its value is not a URI, and with 404 ResourceNotFound when no snapshot of that identifier exists in the tenant. On endpoints outside the core, temporal and entity-subscription families, such as registrations, the header is refused.

Watching it become ready​

GET /ngsi-ld/v1/snapshots/urn:ngsi-ld:Snapshot:1 returns the snapshot's status.

GET /ngsi-ld/v1/snapshots/urn:ngsi-ld:Snapshot:1
Accept: application/json
{
"id": "urn:ngsi-ld:Snapshot:1",
"type": "Snapshot",
"snapshotStatus": "preparing",
"createdAt": "2026-10-24T08:00:00.000Z",
"modifiedAt": "2026-10-24T08:00:00.000Z",
"expiresAt": "2026-10-24T09:00:00.000Z",
"lastUsedAt": "2026-10-24T08:00:00.000Z"
}

Ask for application/ld+json instead and the answer opens with its @context, while application/json answers without one and carries a Link header in its place.

snapshotStatus walks a fixed ladder once the capture ends.

  • success when every query ran and each returned at least one result.
  • partial when at least one query ran and returned a result, and another failed.
  • empty when every query ran and none returned a result.
  • failure when nothing ran successfully.

When it ends, the status carries snapshotQueriesDetails and snapshotTemporalQueriesDetails, one entry per query in the order you listed them. Each entry holds a resultStatus of success, empty or failure, and a problemDetails when it failed.

The timestamps come from the broker. createdAt and modifiedAt mark the snapshot itself, expiresAt when it will be removed, and lastUsedAt when something last read or wrote it. lastUsedAt starts at creation and never moves back.

Keeping it, trimming it, discarding it​

PATCH /ngsi-ld/v1/snapshots/urn:ngsi-ld:Snapshot:1 changes the members a client may set, namely snapshotLifetime, which recomputes expiresAt from the update, snapshotPriority, endpoint and receiverInfo. Writing the NGSI-LD null for a member removes it. The read-only members, the id, the type, snapshotQueries and snapshotTemporalQueries, are refused, and so is a JSON null. The answer is 200 with the updated status.

DELETE /ngsi-ld/v1/snapshots/urn:ngsi-ld:Snapshot:1 removes it, answering 204. A snapshot ceases to exist at its expiresAt too. Deleting one also deletes the subscriptions created under it, so nothing keeps firing against a copy that is gone.

Many go at once with a purge. DELETE /ngsi-ld/v1/snapshots takes a q filter over the snapshot's own members, such as type, id, snapshotPriority or endpoint.

DELETE /ngsi-ld/v1/snapshots?q=snapshotPriority==1

The purge answers 204 and removes every snapshot the filter selects. A filter that selects nothing answers 404 ResourceNotFound, and q is the only parameter it takes.

Removing a snapshot, by delete, purge or expiry, sends no notification.

Cloning​

POST /ngsi-ld/v1/snapshots/urn:ngsi-ld:Snapshot:1/clone copies everything the snapshot holds into a new one. The body is a Snapshot document without queries, namely an id for the clone and optionally snapshotLifetime, snapshotPriority, endpoint and receiverInfo.

POST /ngsi-ld/v1/snapshots/urn:ngsi-ld:Snapshot:1/clone
Content-Type: application/json

{
"id": "urn:ngsi-ld:Snapshot:2",
"type": "Snapshot",
"snapshotLifetime": "PT2H",
"snapshotPriority": 6
}
HTTP/1.1 201 Created
Location: /ngsi-ld/v1/snapshots/urn:ngsi-ld:Snapshot:2

The clone prepares in the background like any snapshot, and a clone of a snapshot still preparing waits for that capture and carries its data. Once it is ready, the two are fully independent. A write to the clone touches neither its source nor the live entity, and deleting one leaves the other alone. Cloning a snapshot that does not exist answers 404 ResourceNotFound, and a second clone under the same id answers 409 AlreadyExists.

Notifications​

With an endpoint set, the broker sends a SnapshotNotification when the capture reaches its final status, and again after every status update.

{
"@context": "https://uri.etsi.org/ngsi-ld/v1/ngsi-ld-core-context-v1.9.jsonld",
"id": "urn:ngsi-ld:SnapshotNotification:...",
"type": "SnapshotNotification",
"snapshotId": "urn:ngsi-ld:Snapshot:1",
"snapshotStatus": "success",
"snapshotPriority": 5,
"expiresAt": "2026-10-24T09:00:00.000Z",
"notifiedAt": "2026-10-24T08:00:01.250Z"
}

The notification is application/ld+json with its @context in the body, and each receiverInfo entry travels as an HTTP header, so {"key": "X-Snapshot-Test", "value": "..."} arrives as X-Snapshot-Test: .... The notification carries no flag saying the snapshot is ready, so read snapshotStatus to decide that. A receiver that refuses the notification gets it again, retried.

Limits and housekeeping​

There is no snapshot quota by default. An operator can set snapshot.quota.max_per_tenant or snapshot.quota.max_storage_per_tenant per tenant, and a tenant over its quota loses snapshots in a fixed order, the lowest snapshotPriority first, then the one expiring soonest, then the one used least recently. Snapshot configuration lists every key, including how quickly operations give up on a preparing snapshot and how the broker recovers a preparation a crash interrupted.

Tenants​

A snapshot belongs to the tenant it was created in. The same id under two tenants names two snapshots, each readable under its own tenant only, and a scoped operation carries NGSILD-Tenant and NGSILD-Snapshot together. Tenants covers the header itself.