Version history of an OpenStreetMap object
Every version of a node, way or relation since 2012, with its date, its user and what changed, from the Overpass API.
timeline(node, 26863664); returns one entry for each version of the element, with the time it was made. Run retro (_.val) { … } on each of those times to get that version itself, with out meta for the version number, the time and the user. The history starts with the version that was current on 12 September 2012.
The query
The summit of the Matterhorn, node 26863664:
[out:json][timeout:60];
timeline(node, 26863664);
for (t["created"]) {
retro (_.val) {
node(26863664);
out meta;
}
}
timeline makes one derived element per version, with the tags refversion, created and, for all but the current version, expired. for (t["created"]) loops over the creation times, and retro (_.val) runs the block on each of them, so node(26863664) returns the version that was current then. timeline alone took 0.2 seconds; all 35 versions with retro took 1.1 seconds.
What changed in each version
The versions come in order. The script compares the tags of each version with those of the one before; when no tag changed, the node was moved. It needs Python 3 and nothing else; the key comes from OVERPASS_CH_KEY.
import json
import os
import urllib.parse
import urllib.request
URL = "https://api.overpass.ch/v1/" + os.environ["OVERPASS_CH_KEY"] + "/interpreter"
QUERY = """[out:json][timeout:60];
timeline(node, 26863664);
for (t["created"]) {
retro (_.val) {
node(26863664);
out meta;
}
}"""
data = urllib.parse.urlencode({"data": QUERY}).encode()
versions = json.loads(urllib.request.urlopen(URL, data=data, timeout=90).read())["elements"]
before = None
for v in versions:
tags = v.get("tags", {})
if before is None:
what = "the version of 12 September 2012, where the history starts"
else:
keys = sorted(k for k in before.keys() | tags.keys() if before.get(k) != tags.get(k))
what = ", ".join(f"{k}: {before.get(k, '-')} -> {tags.get(k, '-')}" if k == "name" else k for k in keys) or "position"
print(f"v{v['version']:<3} {v['timestamp'][:10]} {v['user'][:16]:<16} {what}")
before = tags
v16 2011-10-08 Thomas_Schmitt the version of 12 September 2012, where the history starts v17 2013-06-24 efa alt_name v18 2014-10-12 Marco_T wikipedia v19 2015-04-23 captain_slow wikipedia:de, wikipedia:en v20 2015-04-23 captain_slow wikipedia:de, wikipedia:en v21 2015-06-29 vancori alt_name, name: Matterhorn -> Cervino v22 2015-07-14 SimonPoole alt_name, name: Cervino -> Matterhorn v23 2017-02-17 ouleyang wikidata v24 2018-10-28 ManelG prominence, source:prominence v25 2020-04-12 AntEntro name:zh, name:zh-Hant v26 2021-08-11 Thomas_Schmitt position v27 2022-09-16 ManelG prominence, source:prominence v28 2023-07-14 woodрeck_reрair name:ru v29 2023-07-15 快乐的老鼠宝宝 name:ru … (16 more versions in July and August 2023 that change name:ru) v46 2024-02-09 Trailpapst fixme v47 2024-02-09 Trailpapst position v48 2024-02-09 Trailpapst position v49 2025-06-17 ds8the1st fixme v50 2025-09-30 harahu fixme
Reading it
- Version 16 is from 2011, before the history starts; it is the version that was current on 12 September 2012. Earlier versions are not in the history data.
- In June 2015 the summit was renamed Cervino, its Italian name, and two weeks later named Matterhorn again.
- From 14 July to 21 August 2023, 18 versions in a row changed only
name:ru, the Russian name. - The user of a version is the OpenStreetMap account that saved it.
out metaalso gives the changeset; on openstreetmap.org,/changeset/<id>shows its comment and the other changes in it.
Ways and relations
timeline(way, id) and timeline(relation, id) work the same way. A way gets a new version when its tags or its list of nodes change, not when one of its nodes moves; to follow the shape, query the nodes as well. Inside retro, way(id); (._; >;); out meta; returns the way and its nodes at that time.
On overpass.ch
History queries are part of the Medium and Large plans; other plans get HTTP 403 history_not_in_plan. Overpass history queries explains [date:], [diff:] and [adiff:], and the example every version of an object runs this query live.