OSM XML format: native OpenStreetMap data — export via Overpass and JOSM

2026-05-0918 min read
osmxmlopenstreetmapnativeexchange

OSM XML is the native data format of the OpenStreetMap project, holding the entire planet.osm since 2004. Unlike GeoJSON, Shapefile, or KML, OSM XML is not "yet another exchange format among many" — it is literally the OSM world itself, serialized to disk: the exact same nodes, ways, relations, and tags that live inside the central OSM API database. Everything you see on openstreetmap.org and everything stored in JOSM during editing is OSM XML or its binary twin PBF. The format is plain text, human-readable, opens in Notepad, and parses with any XML parser without special libraries. On osm2cdr.ru osm_xml_exporter.py pulls a bbox slice from a local PostGIS copy of the planet and assembles a valid <osm version="0.6"> document, ready for import back into JOSM, processing by osm2pgsql, analysis with osmium, or conversion to any other format via GDAL.

Map generation of Rome city center

History: Steve Coast 2004, API 0.6 (2009)

The OpenStreetMap project was founded by Steve Coast at University College London in 2004 as a reaction to the closed nature of the UK Ordnance Survey data — which charged users money even for the map of their own street. The idea was simple: volunteers walk the city with a GPS tracker, draw roads in an editor, upload to a shared database — and a free world map under an open license emerges. The first OSM API version appeared in 2005 and used XML as the transport between editor and server — natural for the 2005 web era, before JSON spread through the geo stack.

API 0.3 (2007) introduced relations — multi-objects combining several ways into one entity (multipolygons, bus routes, administrative boundaries). API 0.5 (2008) added changesets — atomic groups of edits with author and comment, turning OSM into a Wikipedia-style system with history. API 0.6 (April 2009) is the current and only supported version today. It introduced the required version attribute on each element for optimistic locking during API edits.

OSM XML was never formally ISO/OGC standardized — it's an open spec described on the OSM Wiki and in the openstreetmap-website Rails app source (github.com/openstreetmap/openstreetmap-website). Nevertheless the format became the de facto standard for the entire OSM ecosystem: JOSM, iD editor, Vespucci, osmosis, osmium, osm2pgsql, GDAL OSM driver — all read and write OSM XML, and any tool in the OSM world is required to handle it.

By 2010 planet.osm in XML format weighed ~250 GB, and it became clear that a more compact format was needed for the full planet dump. Jochen Topf in 2010 designed PBF (Protocol Buffer Format) — a binary format based on Google Protobuf — that compresses the same nodes/ways/relations 5-10x smaller. Today planet.osm.pbf weighs ~70 GB, planet.osm in XML — ~700 GB uncompressed. PBF became the standard for full-planet and regional extracts on download.geofabrik.de. OSM XML stayed the primary format for bbox extracts via Overpass API, for editing exchange in JOSM, and for small to medium extracts where human-readable text matters more than file size.

Inside the file: nodes, ways, relations, tags

OSM XML is a flat XML tree with a root <osm version="0.6" generator="..."> and three child element types: <node>, <way>, <relation>. All three can have <tag k="..." v="..."> children — key-value metadata. Ways have <nd ref="..."> children — references to nodes by id. Relations have <member type="..." ref="..." role="..."> children — references to nodes, ways, or other relations with role specification.

Basic example — a cafe POI (one node with tags):

<?xml version="1.0" encoding="UTF-8"?>
<osm version="0.6" generator="osm2cdr">
  <bounds minlat="59.9300" minlon="30.3100" maxlat="59.9400" maxlon="30.3300"/>
  <node id="1234567890" lat="59.9342" lon="30.3160" version="3"
        timestamp="2024-08-15T10:23:11Z" changeset="142567" user="mapper42" uid="98765">
    <tag k="amenity" v="cafe"/>
    <tag k="name" v="Coffee House"/>
    <tag k="cuisine" v="coffee_shop"/>
    <tag k="opening_hours" v="08:00-22:00"/>
  </node>
</osm>

A <node> is a point with required id (globally unique 64-bit integer in OSM database), lat, lon attributes. Optional attributes are edit history meta-data: version (for optimistic locking), timestamp (UTC ISO 8601), changeset (link to edit group), user + uid (author of last edit). Tags are an unordered set of key-value pairs where both key and value are Unicode strings.

Way (a polyline or polygon) is an ordered list of node references plus its own tags:

<way id="9876543" version="2" timestamp="2024-09-01T14:00:00Z">
  <nd ref="1234567890"/>
  <nd ref="1234567891"/>
  <nd ref="1234567892"/>
  <nd ref="1234567893"/>
  <nd ref="1234567890"/>
  <tag k="building" v="yes"/>
  <tag k="building:levels" v="5"/>
  <tag k="addr:street" v="Nevsky Prospect"/>
  <tag k="addr:housenumber" v="28"/>
</way>

<nd ref="..."> points to a <node> by id. If the first and last <nd> reference the same node — that's a closed way (a polygon, e.g. a building). If different — an open way (a road, a river). OSM XML doesn't distinguish polygon from line by attribute — that's determined by tags: building=* or landuse=* implies polygon, highway=* or waterway=* — line.

Relation (a multi-object) combines several ways/nodes into one entity:

<relation id="5432" version="7" timestamp="2024-10-12T09:00:00Z">
  <member type="way" ref="9876543" role="outer"/>
  <member type="way" ref="9876544" role="inner"/>
  <tag k="type" v="multipolygon"/>
  <tag k="natural" v="water"/>
  <tag k="name" v="Lake with island"/>
</relation>

<member type="..." ref="..." role="..."> references a way (or node, or another relation) by id with a role. Typical roles: outer/inner for multipolygons, forward/backward for route relations, stop/platform for public transport. Relations exist for anything that doesn't fit into a single way: a park with lakes inside (multipolygon), a bus route, an administrative boundary, a turn restriction.

All three element types (node, way, relation) share the id namespace — but id 1234567890 for a node and id 1234567890 for a way are different entities; the type must be specified.

Order matters in XML. OSM XML must contain nodes before the ways that reference them, and ways before the relations that reference them — this lets streaming parsers (SAX, osmium) process the file in one pass without random access. Within one type, order doesn't matter, but convention is sorted by id ascending.

Compressed variants. OSM XML compresses very well with gzip/bz2 thanks to repeated tag keys. .osm.gz (gzip, ~10x compression) and .osm.bz2 (bzip2, ~15x compression) are the standard download variants. Geofabrik publishes regional extracts in .osm.bz2 for those who don't want to deal with PBF.

Map generation of Yaroslavl city center

OSM XML vs OSM PBF: text or binary

OSM PBF (Protocol Buffer Format) is the binary alternative to OSM XML, designed by Jochen Topf in 2010 to solve the planet.osm size problem. PBF uses Google Protocol Buffers — a compact schema-based binary format — and applies delta encoding for coordinates (instead of storing lat=59.9342 it stores the delta from the previous node) and string deduplication for tags (each key like building is stored once and referenced by an int).

Differences:

Property OSM XML OSM PBF
Type Text (UTF-8 XML) Binary (Protobuf)
Planet size ~700 GB ~70 GB
Compression gzip/bz2 (~10-15x) Already compressed
Human-readable Yes No
Streaming parse SAX/expat Protobuf decoder
Random access No No (sequential)
Edit support Native (JOSM saves XML) Read-only typically
Full-planet standard No Yes
Bbox-extract standard Yes (Overpass output) Optional
Tooling XML parsers + osmium osmium + osm2pgsql

OSM XML wins where human-readable format matters: debugging, manual viewing, edit exchange between mappers, small extracts (<100 MB), import into JOSM. PBF wins where size and parse speed matter: full planet, regional extracts, batch processing pipelines (osm2pgsql loads PBF 2-3x faster than XML).

On osm2cdr.ru we export .osm (uncompressed XML) for bbox sizes up to ~50 MB — usually enough for a city or district. For full country extracts we recommend PBF from Geofabrik; we don't duplicate that functionality.

Overpass API: OSM XML on demand

Overpass API is a read-only HTTP service over the OSM database, designed by Roland Olbricht in 2008. Unlike the main OSM API (optimized for editing — small selections by changeset), Overpass is optimized for query — letting you extract large slabs of data by complex criteria. For example: "all pharmacies within 500 m of every metro station in Moscow" — one request, response in OSM XML format.

A query is written in Overpass QL (Query Language) or older Overpass XML. The simplest example — all cafes in the Nevsky Prospect bbox:

[out:xml][timeout:25];
node["amenity"="cafe"](59.93,30.30,59.94,30.34);
out body;

[out:xml] specifies response format — OSM XML (alternative is [out:json]). node["amenity"="cafe"](...) — filter by tag + bbox. out body; — what to include in response (attributes + tags, no node references). The result is a standard OSM XML document that can be saved as a .osm file and opened in JOSM, imported into PostGIS via osm2pgsql, or converted to GeoJSON via osmtogeojson.

The public Overpass instance at overpass-api.de is free but rate-limited (~2 concurrent requests per IP) and capped at 180-second timeout. For production load you need to deploy your own Overpass instance via Docker — full planet needs ~150 GB disk + 64 GB RAM. The alternative is overpass-turbo.eu, a web UI with auto-completion and map preview of the response.

The main Overpass use case is extracting bbox extracts in OSM XML format for downstream tooling. osm2cdr doesn't use Overpass under the hood (we keep our own PostGIS copy of the planet via osm2pgsql), but our osm_xml_exporter.py assembles structurally identical XML that can be fed to any Overpass-ready tool.

Use cases: four scenarios

OSM mapper with JOSM. A mapper wants to improve building coverage in their district. Downloads OSM XML for the district bbox from osm2cdr, opens in JOSM (File → Open), sees existing nodes/ways/relations with tags and edit history. Adds missing buildings, fixes wrong tags, uploads back to OSM via File → Upload data. JOSM works with OSM XML natively, supports sophisticated workflows: layer management, conflict resolution under concurrent edits, plugin system with 200+ plugins.

OSM developer with osm2pgsql. A backend developer builds a service on OSM data — say, POI density analytics. Downloads a bbox extract from osm2cdr in OSM XML, imports into PostGIS via osm2pgsql -d osm -O flex --slim file.osm. Gets planet_osm_point, planet_osm_line, planet_osm_polygon tables with ready geometries and hstore tags. Then — SQL queries through PostGIS, no OSM-specific tooling needed. That's the path hundreds of OSM-based services follow, including osm2cdr itself.

Researcher with osmium. A scientist analyzes global building coverage in OpenStreetMap. Uses osmium-tool (github.com/osmcode/osmium-tool) — a Swiss army knife of OSM processing. Command osmium tags-filter file.osm building=* -o buildings.osm filters only buildings. osmium export buildings.osm -f geojson converts to GeoJSON for downstream analysis in pandas/QGIS. osmium streams through the file and handles tens of GB of data on a laptop without issue.

Data engineer with GDAL. An ETL engineer builds a pipeline OSM → internal data lake. Downloads OSM XML via osm2cdr, converts to any needed format through the GDAL OSM driver: ogr2ogr -f "GeoJSON" output.geojson input.osm or ogr2ogr -f "GPKG" output.gpkg input.osm. GDAL automatically interprets OSM tags: building=yes → polygon, highway=primary → linestring, amenity=cafe → point. From there, data flows to Spark/Athena pipelines without OSM-specific dependencies.

Strengths and weaknesses

Strengths. Native OSM project format — guaranteed lossless support by all OSM tools. Human-readable — open in Notepad, read, fix manually. Simple XML schema — parses with any XML parser (Python's xml.etree, Java's SAX, JavaScript's DOMParser). Preserves all OSM metadata: changeset history, author, timestamp, version. Streaming-friendly — SAX parser handles files of any size without OOM. Compresses well with gzip/bz2 (~10-15x). Reversible roundtrip: data doesn't get lost in OSM XML → JOSM → OSM XML. De facto standard for bbox extracts via Overpass.

Weaknesses. Large size vs PBF — 5-10x bigger for full planet. XML parsing is 2-5x slower than binary formats. No built-in geometric semantics — the client must assemble polygons from way <nd ref> references. Multipolygons with inner holes require non-trivial relation member processing. No CRS field — coordinates are always WGS 84 (EPSG:4326), which sometimes needs to be explicit for downstream tooling. No spatial index — bbox search requires full document scan. Tag-keys-as-strings — no typed schema (all string), attribute typing is the reading program's job. Not for huge planet-scale work (PBF is for that).

OSM XML vs GeoJSON vs Shapefile vs GPX

Property OSM XML GeoJSON Shapefile GPX
Origin OSM 2004 IETF RFC 7946, 2016 ESRI 1998 Topografix 2002
Container UTF-8 XML UTF-8 JSON Binary (4 files) UTF-8 XML
Native geo semantics OSM-specific (nodes/ways/relations) GeoJSON Feature types ESRI shape types Track/route/waypoint
Tags / attributes <tag k v> unlimited properties object DBF columns (10 char limit) Limited fields
Multi-geometry Yes (relations) Yes (MultiPolygon etc.) Yes No
CRS WGS84 fixed WGS84 mandatory Via .prj WGS84 fixed
Streaming Yes (SAX) GeoJSON-Seq only No Yes (SAX)
Editor support JOSM, iD, Vespucci native QGIS, ArcGIS QGIS, ArcGIS, all GIS Garmin BaseCamp, JOSM
File size (10K features) ~50 MB ~30 MB ~10 MB ~20 MB
Edit roundtrip Lossless Lossless Lossy (DBF type limits) Lossless for track

OSM XML wins where OSM-specific semantics matter (nodes/ways/relations with tags), reversible roundtrip into JOSM, integration with osmium/osm2pgsql. GeoJSON — for web maps and APIs. Shapefile — for legacy GIS tools. GPX — for GPS devices and track recording.

Software for OSM XML

JOSM (josm.openstreetmap.de) — Java desktop editor, native OSM XML support. Free, open source, the main professional editor for OSM mapping. Supports sophisticated workflows: layer management, conflict resolution, undo/redo across sessions, 200+ plugins. File → Open file for .osm; File → Upload data for submission to the OSM database. Requirements: Java 21+, 4 GB RAM minimum for moderate bbox.

iD editor (github.com/openstreetmap/iD) — browser-based editor, the default for new mappers on openstreetmap.org. Doesn't open local .osm files (only via main OSM API), but you can export via "Help → Download Data". Good for casual editing of small areas.

Vespucci (vespucci.io) — Android editor for on-site mapping from a phone. Supports OSM XML files as a layer, full offline editing with later upload. The main mobile editor in the OSM community.

Osmium-tool (github.com/osmcode/osmium-tool) — CLI Swiss army knife of OSM processing by Jochen Topf. Opens OSM XML, PBF, .o5m formats. Commands: osmium cat, osmium tags-filter, osmium extract, osmium export, osmium merge. C++, very fast, streams multi-GB files without OOM. The standard tool in OSM CLI pipelines.

osmconvert (wiki.openstreetmap.org/wiki/Osmconvert) — lightweight converter between OSM XML, PBF, and the proprietary o5m. Single-file C tool, no dependencies. Useful for quick conversions: osmconvert input.osm.pbf -o=output.osm.

osm2pgsql (osm2pgsql.org) — the main OSM → PostGIS importer. Reads OSM XML or PBF, creates PostgreSQL tables with ready geometries. Used inside osm2cdr to load planet.osm into our database. osm2pgsql -d osm -O flex --slim file.osm — the standard command.

Osmosis (wiki.openstreetmap.org/wiki/Osmosis) — Java tool for pipeline-style OSM processing. Older than osmium but still used for specific workflows (diff applying, replication). Reads/writes OSM XML and PBF.

GDAL OSM driver (gdal.org/drivers/vector/osm.html) — vector driver for OSM XML and PBF. Lets you open .osm files as a regular vector dataset: ogr2ogr -f "GeoJSON" output.geojson input.osm. Interprets tags into logical geometry types: points, lines, multilinestrings, multipolygons.

QGIS (qgis.org) — desktop GIS, opens OSM XML via the GDAL OSM driver: Layer → Add Layer → Add Vector Layer → pick the .osm file. Optionally via the QuickOSM plugin — Overpass-style queries right in the QGIS UI.

Pipeline in OSM2CDR

Our osm_xml_exporter.py is a LAYERS exporter that reads selected OSM layers from a local PostGIS copy of the planet and assembles a valid OSM XML document. It's not an Overpass mirror — we keep our own database via osm2pgsql and generate XML directly from PostGIS rows, which gives sub-second latency for most bbox requests instead of 30-180 second Overpass timeouts.

Export algorithm:

  1. Query PostGIS. From planet_osm_point / planet_osm_line / planet_osm_polygon / planet_osm_roads we select features intersecting bbox via ST_Intersects(way, ST_MakeEnvelope(...)). Apply user filters (e.g., only buildings, or only POIs).
  2. Resolve node references. For each way pull the list of node ids via a PostGIS join. For each relation — the list of members with types.
  3. Reconstruct topology. Polygons in OSM XML are closed ways or multipolygon relations with outer/inner roles. Lines are open ways. We convert PostGIS geometries back into OSM topology.
  4. Serialize XML. A streaming XML writer (lxml xmlfile context manager) writes nodes, then ways, then relations — the order required for streaming readers. All attributes (lat, lon, id, version, timestamp, user, uid) emit from PostGIS columns.
  5. Bounds element. First <osm> child is <bounds minlat="..." minlon="..." maxlat="..." maxlon="..."/> with actual bbox bounds.

Output:

<?xml version="1.0" encoding="UTF-8"?>
<osm version="0.6" generator="osm2cdr/2.0">
  <bounds minlat="55.7" minlon="37.6" maxlat="55.8" maxlon="37.7"/>
  <node id="..." lat="..." lon="..." version="..." timestamp="..." user="..." uid="...">
    <tag k="..." v="..."/>
  </node>
  <!-- ...thousands of nodes... -->
  <way id="..." version="..." timestamp="..." user="..." uid="...">
    <nd ref="..."/>
    <!-- ...nd refs... -->
    <tag k="..." v="..."/>
  </way>
  <!-- ...ways... -->
  <relation id="..." version="..." timestamp="..." user="..." uid="...">
    <member type="way" ref="..." role="outer"/>
    <!-- ...members... -->
    <tag k="..." v="..."/>
  </relation>
</osm>

Limits and parameters. Bbox area limit depends on user tier (free up to ~5 km², pro up to ~500 km²). For large areas we recommend taking PBF from Geofabrik regional extracts instead of XML from osm2cdr — faster and more compact. Encoding is always UTF-8 (XML 1.0 standard). XML pretty-print is on by default (2-space indentation) for human readability — disabled via pretty=false parameter to shrink size by ~15%.

Roundtrip safety. A file exported from osm2cdr can be opened in JOSM, examined, and (theoretically) uploaded back to the OSM API. In practice we don't recommend that workflow — our exports can lag behind live OSM by several hours (replication runs hourly), and uploading stale versions can cause version conflicts. For serious editing pull fresh data directly from the OSM API through JOSM's "Download data" command, and use osm2cdr for analytics, archival, and downstream tooling.

Map generation of Kazan city center

FAQ

OSM XML vs OSM PBF — which to choose? OSM XML — for small and medium extracts (up to ~100 MB), for import into JOSM, for debugging and manual viewing. PBF — for full planet and regional extracts (>100 MB), for batch processing pipelines, when size and speed matter more than human readability. All OSM tools support both formats; conversion via osmconvert input.osm -o=output.osm.pbf is trivial.

Can I edit in JOSM after export? Yes, technically — JOSM will open the .osm file, show nodes/ways/relations, allow editing. But uploading back to the OSM API is not recommended because the export may be several hours stale, and upload may cause version conflicts. For live mapping pull fresh data via JOSM's "Download data" from the main OSM API.

Does OSM XML include relations and multipolygons? Yes, osm2cdr exports full relation structure including multipolygons (type=multipolygon with outer/inner roles), routes (type=route for public transport), boundaries (type=boundary for administrative areas). All members with real ref ids, no dereferencing. Import into JOSM or osm2pgsql reassembles topology automatically.

What do the meta-attributes — timestamp, version, user, uid — include? OSM XML carries full history metadata of the last edit of each element: version (for optimistic locking on upload), UTC timestamp, author username and uid, changeset id. This is public information in the OSM database (all editing history is open). On export through osm2cdr these attributes are filled from our PostGIS snapshot — reflecting OSM state as of the last replication.

Can I import OSM XML directly into QGIS? Yes, via the GDAL OSM driver. QGIS → Layer → Add Layer → Add Vector Layer → pick the .osm file. GDAL automatically interprets OSM tags into logical geometry types (points, lines, multilinestrings, multipolygons) and creates 4 sublayers: points, lines, multilinestrings, other_relations. You can open just the needed sublayer or all at once.

What to do with very large OSM XML files? If the file is larger than 1 GB — use streaming tools: osmium, osmosis, GDAL streaming mode. JOSM opens up to ~500 MB on an 8-GB RAM system, beyond that it starts swapping. The alternative is converting to PBF via osmconvert file.osm -o=file.osm.pbf (gets 5-10x smaller) and working with PBF from there.

Does osm2cdr support .osm.pbf export? At the time of writing — no, only uncompressed .osm XML. If PBF is needed — convert via osmconvert or osmium locally. For full country or region extracts, better to download ready PBF from download.geofabrik.de — they're generated daily and an order of magnitude smaller.

Sources

← All articles