Guide

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 meta also 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.