/ OSM Persistent IDs · Spec v0.6-pre

Persist the meaning,
not just the id.

An OSMPID carries an object's version and the timestamp of its newest child. So when the map moves underneath your reference, a single request can say exactly what changed.

/ Anatomy of a persistent id
way/2641352539@7;2026-03-12T10:15:00Z?amenity
element_type element_id version latest_child_timestamp tags
/ Live · osmpid-service

Validate a reference

Paste an OSMPID or pick an example. Checked against current OSM state by the service.

GET / validate status online · live service
Try:

/ The problem

A stable handle for a moving target

OSM identifiers like node/2641352539 are source-local — they name a record inside one system. They say nothing about whether the thing they point at still means what it meant when you stored it.

An OSM Persistent ID closes that gap. It combines the source-local reference with version and timestamp semantics, so a service can decide — using only current OSM data — whether the referenced object has meaningfully changed since you recorded it.

No historical snapshots, no diff history, no attic data. The check relies only on what OSM serves today.

/ Anatomy · 3.1
element_type required

The OSM element class — node, way, or relation. Determines how deep the recursive child check must go.

element_id required

The OSM object's source-local numeric ID. Together with the type it locates the object in the OSM database.

version required

The object version at the time of recording, written @N. A differing current version is the first signal of change.

latest_child_timestamp conditional

ISO-8601 UTC timestamp of the most recent child element, found by recursing the dependency tree. Omitted for nodes, which have no children.

tags optional

url-encoded key[=value] pairs that pin the semantic meaning. If an expected key disappears, the object is considered semantically changed.


/ Handling · 4

Three signals decide if an ID has changed

Resolve the object by type and id, then walk three checks in order. Any one of them flips the reference to changed.

01

Version

Fetch the current object and compare its version with the stored one. A higher version is the first and cheapest signal of change.

02

Recursive child check

A way recurses into its member nodes; a relation into all members, ways and relations alike. If any child's timestamp is newer than latest_child_timestamp, the object changed. Cycles are guarded by tracking visited elements.

03

Tag presence

If the OSMPID pins tag keys or values, those must still be present. A vanished amenity key means the object changed semantically — even at the same version.


/ The motif

Read an OSMPID at a glance

Every part carries meaning. Main parts are separated by a semicolon; the version is appended to the id with @; tags follow a ?, url-encoded and joined by &.

way/2641352539@7;2026-03-12T10:15:00Z?amenity
element_type element_id version latest_child_timestamp tags

/ Outcomes · 6

Five answers, one request

The validate endpoint always returns a definite verdict. Only an unparseable string is an error; every real-world object state resolves with 200 OK.

ResultStatusReason
Unchanged200 OKVersion, children and tags all match.
Changed200 OKVersion, a child element or a tag changed. A new OSMPID is returned.
Deleted200 OKThe object is no longer visible.
Unknown200 OKThis type + id never existed.
Malformed422The string failed to parse.

/ API · 6

A lightweight HTTP endpoint

One GET, one url-encoded id. The response carries a Last-Modified header with the computed latest timestamp — ready to become the next latest_child_timestamp.

request · curl
GET /validate?id=way%2F2641352539%407%3B2026-03-12T10%3A15%3A00Z%3Famenity
# decoded → way/2641352539@7;2026-03-12T10:15:00Z?amenity
200 OK · application/json
{
  "status": "changed",
  "permanent_id": "way/2641352539@8;2026-03-28T14:22:00Z?amenity",
  "element_type": "way",
  "element_id": 2641352539,
  "version": 8,
  "latest_child_timestamp": "2026-03-28T14:22:00Z",
  "changed_reason": {
    "version_changed": true,
    "child_changed": true,
    "tag_changed": false
  }
}

Try it on your own references

Validate a stored ID, or generate a fresh OSMPID for any element.

Validate Generate