Overpass speed: overpass-rust and Overpass API compared
overpass.ch answers queries on current data with overpass-rust, a reimplementation of the Overpass API in Rust. In a test on the queries of our customers, it was typically 84 times faster than Overpass API 0.7.62.11 on the same hardware for answers under 100 KB, and 14 times faster in total time.
On this page, “C++” means Overpass API, the software of the public Overpass servers, and “Rust” means overpass-rust. Rust gives the same answers as C++, so Overpass Turbo, QGIS, OSMnx and other clients work with it unchanged. History queries run on C++.
The test
The test ran on an AMD Ryzen 9 3900 with 128 GB of RAM and NVMe disks, with Rust and C++ on the same machine and the same planet data, kept current with minutely diffs. It used the queries of our customers. After Rust answered a query, the same query was sent to C++, at most two at a time and within 5 minutes, and the two answers were compared. Only queries with identical answers count. Both sides were timed until the last byte of the answer; for Rust, the time spent waiting for the client is left out.
Queries from 11 November 2026, 05:34, to 12 November, 06:00 UTC:
| Median | 95th percentile | 99th percentile | |
|---|---|---|---|
| C++ | 295 ms | 6.8 s | 20.5 s |
| Rust | 4 ms | 0.22 s | 2.6 s |
C++ was timed on a replay, while the machine had other load. The two servers do not share files, so the replay does not profit from the Rust run. The C++ database also holds 341 GiB of attic data for history queries.
The figures move with the mix of queries. In the night of 12 November, with more heavy area queries, the same method gives a median of 58 times for answers under 100 KB and 9 times in total.
Why Rust is faster
C++ starts a new process for each request, and that process registers with the dispatcher before it reads any data. In the test, the quickest C++ answers took about 50 ms. Rust answers every query inside one server process that reads a memory-mapped LMDB file. Each query gets its own snapshot while a single writer applies the diffs, and the quickest answers took less than 1 ms.
The database is less than half the size. Elements are packed into lz4-compressed chunks of up to 16 KiB, with coordinates stored column by column and a tag dictionary in each chunk, and the indexes are delta-coded. The planet with metadata and areas takes 190 GiB in Rust and 428 GiB in C++, attic data not counted. With 128 GB of RAM, this decides whether the parts of the database in use stay in the page cache or are read from disk.
Data is sorted by spatial cell and then by id, and the tag index points to cell and id, so the elements found through a tag are read in the order they lie on disk. Every way and relation keeps its bounding box. For (bbox), around, poly and areas the box settles most cases without loading node coordinates, and out center and out bb come straight from it.
A planner picks where each statement starts (ids, an input set, the tag index or spatial cells, whichever is cheapest) and begins with the most selective filter. Tags are checked before an element is decoded. An area is turned into ranges of cells, and only those cells are read. Decoding, geometry tests, output and scans of the tag index run on up to 8 threads within one query, where C++ uses one thread per query.
Why the gap grows with the data
| Data | Measure | Rust faster by |
|---|---|---|
| Synthetic, 640,000 nodes, one process per query | 17 queries, total | 2.5× |
| Luxembourg, 4.9 million elements | Typical queries | 1.8× to 17× |
| Germany, 515 million elements, both in RAM | 212 queries, total (geometric mean) | 3.7× (12×) |
| Planet, customer queries | Total (median) | 14× (56×) |
On small data, most of the time goes into starting a process and parsing the query, on both sides. On large data it goes into reading indexes and data. On the planet the C++ database for current data is more than three times the size of the RAM, so many C++ queries wait for the disk. The rows use different queries and compare only roughly.
The Germany run used the Geofabrik extract of 1 November 2026 with metadata, on a Xeon E3-1275 v5 with 8 threads, 64 GB of RAM and NVMe. C++ ran as the wiktorn/overpass-api:v0.7.62.11 image, and both servers were queried over HTTP.
| C++ | Rust | |
|---|---|---|
| Import with metadata | 90 min | 17.5 min |
| Database | 28 GB | 9.8 GB |
| 60 minutely planet diffs | 97 s | 7.9 s |
| 212 hand-written queries, warm, sum of medians | 393 s | 106 s |
500 generated multi-statement queries (foreach, for, make, if) | 775 s | 49 s |
| Throughput, 8 clients, queries under 2 s | 13.7 q/s | 56.5 q/s |
Same answers
The semantics of Rust follow specifications extracted from the C++ sources. Where C++ behaviour shows in the output, as with number formatting, iteration order or area geometry, it is ported. Of 598 test queries over ten datasets, 528 give byte-identical output and 41 are rejected by both with the same error. 12 depend on timing. The other 17 differ on purpose, for example where C++ keeps stale state after a node changes. On the Germany extract, the answers stayed identical through 36 hours of diffs, except where the C++ database itself became inconsistent.
Source code
overpass-rust is open source under the GNU Affero General Public License, on GitHub, with a changelog of the releases. It is based on Overpass API by Roland Olbricht et al.
Switching to overpass.ch
Queries, libraries and tools work unchanged. Only the server URL changes:
https://overpass-api.de/api/interpreter
→ https://api.overpass.ch/v1/YOUR_API_KEY/interpreter
The documentation shows the setting for Overpass Turbo, JOSM, QGIS, Python, R and JavaScript.