A pass is a node tagged mountain_pass=yes, on a road or a path. To get road passes only, collect the main roads first and then the pass nodes on them.
The heights here agree with ele to within a few metres. A pass is a saddle, where the terrain is smooth, which suits a 90 m elevation model well.
The query
[out:json][timeout:90]; area["ISO3166-1"="CH"][admin_level=2]->.ch; way["highway"~"^(primary|secondary)$"](area.ch)->.roads; node(w.roads)["mountain_pass"="yes"]; out;
Run it
Live result from overpass.ch, cached for up to 12 hours. Map data © OpenStreetMap contributors (ODbL). Heights produced using Copernicus WorldDEM™-90 © DLR e.V. 2010-2014 and © Airbus Defence and Space GmbH 2014-2018 provided under COPERNICUS by the European Union and ESA; all rights reserved. Sea depths: GEBCO Compilation Group (2026) GEBCO 2026 Grid.
How it works
way["highway"~"^(primary|secondary)$"](area.ch)->.roads;collects the main roads of Switzerland in the setroads.node(w.roads)["mountain_pass"="yes"]takes the nodes of those roads and keeps the ones tagged as a pass. Hiking passes are not on these roads, so they drop out.- With
elevation=1each pass gets its height. The highest are the Umbrail, the Nufenen and the Furka, all above 2,400 m. - Collecting all main roads of a country takes a while: this query runs for about 15 seconds.
Variations
- Smaller roads too:
["highway"~"^(primary|secondary|tertiary|unclassified)$"](slower). - Every pass and col, including hiking passes:
node["mountain_pass"="yes"](area.ch);, ornatural=saddlefor saddles without a route.
Run it from your code
The query is unchanged; elevation=1 in the URL adds the heights. Each height uses one lookup from your elevation allowance.
curl 'https://api.overpass.ch/v1/YOUR_API_KEY/interpreter?elevation=1' \
--data-urlencode 'data@query.overpassql'
import requests
query = open("query.overpassql").read()
r = requests.post("https://api.overpass.ch/v1/YOUR_API_KEY/interpreter",
params={"elevation": 1}, data={"data": query}, timeout=120)
r.raise_for_status()
for el in r.json()["elements"]:
height = el.get("elevation") or el.get("center", {}).get("elevation")
print(el.get("tags", {}).get("name"), height)
const res = await fetch("https://api.overpass.ch/v1/YOUR_API_KEY/interpreter?elevation=1", {
method: "POST",
body: new URLSearchParams({ data: query }),
});
if (!res.ok) throw new Error(await res.text());
const { elements } = await res.json();
for (const el of elements) console.log(el.tags?.name, el.elevation ?? el.center?.elevation);
Save the query as query.overpassql. It works the same in Overpass Turbo, QGIS and JOSM.