Overpass alternativePostGISPerformancePlanet-scale

Building the New OSM Features API: A Planet-Scale Overpass Alternative

How we built a production OSM Features API on PostGIS: scaling the import to the full planet, the 1.5 TB storage footprint, the indexes that make popular-tag queries fast, and real measured performance against public Overpass.

Joel Stenkvist · May 23, 2026Building the New OSM Features API: A Planet-Scale Overpass Alternative

Public Overpass servers are shared community infrastructure. They are great for prototypes, but at production traffic you hit timeouts, soft rate limits, and latency spikes that have nothing to do with your query. Running your own Overpass stack is possible, but it is operationally heavy: recursive query resolution, a bespoke schema, and a lot of infrastructure to babysit. Commercial mapping vendors solve the reliability problem, but usually force a proprietary category tree and SDK, so queries, tests, and AI-generated code stop being portable.

MapLark's OSM Features API is the practical middle path: a production HTTP API, backed by PostGIS, that keeps Overpass-style tag semantics (amenity=cafe, building=yes) and returns standard GeoJSON FeatureCollections (also FlatGeobuf, GeoParquet, CSV, and TSV) behind a stable /v2/osm_features endpoint. Here is how it is actually built, how it scaled to the full planet, and how it performs against public Overpass in practice.

What MapLark returns

The OSM Features API keeps OSM semantics intact and returns GeoJSON FeatureCollections by default so that you can feed straight into Leaflet, MapLibre, OpenLayers, or any geospatial toolchain. Other formats include FlatGeobuf, GeoParquet, CSV, and TSV. The translation layer is minimal:

  • node Point
  • open way LineString
  • closed way Polygon
  • relation MultiPolygon or grouped geometries like train stations with tracks.

You filter with the same tags mappers already use (amenity=cafe, building=yes, and so on). Knowledge from Overpass, osm2pgsql, or tagging docs transfers immediately.

Scaling the import to the full planet

The OSM ingestion pipeline runs osm2pgsql with a flex-output pass using lua scripts. Features are split by geometry into dedicated point, line, and polygon tables, each with a single geom column in EPSG:4326. The import can take between 24 to 128+ hours to complete, depending on the hardware, regions, and database schema.

What planet-scale storage actually costs: 1.5 TB

Our full planet import lands at roughly 1.5 TB in PostGIS at EPSG:4326. That number would be meaningfully higher with vector-tile serving turned on: Martin-style tile serving needs a second GiST index family on a 3857 projection of every geometry, which is a significant fraction of a terabyte on its own. That is why tile serving is off by default on the planet database.

Two things make /v2/osm_features fast at planet scale, and both are ordinary PostGIS indexing, not magic:

  • Spatial prefilter: every table has a GIST(geom) index, so a bbox or around query narrows to the relevant slice of the planet before anything else runs.
  • Indexes tuned for common tags: high-traffic keys like building, highway, and amenity get dedicated indexing so filters such as tags=building or tags=highway=cycleway stay cheap after the spatial prefilter. Rarer tags remain queryable; we simply optimize hardest for the ones that show up in production traffic.

Render on a free basemap, then reason about the results

Because the response is plain GeoJSON, you can drop it straight onto a free basemap like OpenFreeMap or any MapLibre style with no paid tile vendor involved. You can even display it ontop of Google Maps or Apple Maps. The point of returning full geometry and tags instead of a tile is that your application code can then compute on the result, not just draw it. A few concrete examples, each with the actual query behind it:

Example Queries

Get building polygons

Fetch building footprints as full polygons with vertex rings instead of a generalized tile outline.

Buildings
curl "https://api.maplark.com/v2/osm_features?bbox=18.06,59.33,18.08,59.34&type=way&shape=polygon&tags=building&limit=200" \
  -H "Authorization: Bearer swHAvOmreIm_uew6eqbn1UbIsVQT6p8PRohmqmAJiu4"

Routing through a city on bike paths

Pull the cycle-permitted path network as LineStrings and build a shortest-path graph client-side.

Bike routing
curl "https://api.maplark.com/v2/osm_features?bbox=18.05,59.31,18.10,59.33&type=way&shape=line&or_tags=highway=cycleway&or_tags=cycleway=lane&or_tags=cycleway=track" \
  -H "Authorization: Bearer swHAvOmreIm_uew6eqbn1UbIsVQT6p8PRohmqmAJiu4"

Finding cafes with opening hours

The API filters by tag existence and value, so it returns every cafe that carries an opening_hours tag.

Cafes
curl "https://api.maplark.com/v2/osm_features?around=18.063,59.334,500&type=node&tags=amenity=cafe&tags=opening_hours" \
  -H "Authorization: Bearer swHAvOmreIm_uew6eqbn1UbIsVQT6p8PRohmqmAJiu4"

Counting trees in a city

Every natural=tree node in a bounding box comes back as a FeatureCollection; the count is just features.length, and large areas page through X-Next-Cursor.

Trees in a city
curl "https://api.maplark.com/v2/osm_features?bbox=18.05,59.32,18.10,59.35&type=node&tags=natural=tree&limit=1000" \
  -H "Authorization: Bearer swHAvOmreIm_uew6eqbn1UbIsVQT6p8PRohmqmAJiu4"

MapLark vs Overpass: measured

Rather than quote fabricated numbers, we ran the same queries against the production MapLark API and the public overpass-api.de instance and checked that both returned the same features before comparing timing.

Buildings in a bbox

tags=building=yes. 532 MapLark vs 531 Overpass elements (same dataset)

826ms
767ms
760ms
744ms
1400ms
9430ms
timed out
MapLark Overpass

Cafes within 1.5 km

tags=amenity=cafe. 231 MapLark vs 233 Overpass elements (same dataset)

688ms
541ms
551ms
1038ms
10203ms
timed out
MapLark Overpass

Captured 2026-08-08, single session, against the production MapLark API (demo key) and the public overpass-api.de instance. Row counts were confirmed to match between the two APIs for each query. MapLark stayed within a tight band across every run; Overpass swung more than 6x between two successful runs of the identical query, on top of an outright request timeout. Public Overpass load varies constantly, so run the commands below yourself for a live comparison rather than trusting a chart.

Try it yourself

Both commands below time themselves with curl's own -w flag, so the numbers are consistent whether you run them from bash, zsh, or PowerShell.

MapLark Overpass
curl -s -o /dev/null -w "%{http_code} in %{time_total}s\n" \
  "https://api.maplark.com/v2/osm_features?bbox=18.05,59.32,18.10,59.34&type=way&shape=polygon&tags=building=yes" \
  -H "Authorization: Bearer swHAvOmreIm_uew6eqbn1UbIsVQT6p8PRohmqmAJiu4"

The demo key above is shared and free-tier rate limited. If you get a 429, that is the rate limit, not a bug. Sign up for your own free key and try again.

Using it from Overpass Turbo or Ultra

Use Overpass Turbo or Ultra to see results on a map. Ultra is a newer, community-built reimplementation by Daniel Schep, using MapLibre GL JS instead of Leaflet. Same story as Turbo: it sends your query to an Overpass API server and renders results on a map. Note that these are not separate backends: they are both browser IDEs that send your query to this same instance.

Try it out in Overpass Ultra and see the example code below.

Overpass Ultra query
---
type: javascript
---
export async function source() {
  const res = await fetch(
    "https://api.maplark.com/v2/osm_features?bbox={{wsen}}&type=node&tags=amenity=restaurant&limit=10000",
    { headers: { Authorization: "Bearer swHAvOmreIm_uew6eqbn1UbIsVQT6p8PRohmqmAJiu4" } },
  );
  if (!res.ok) {
    throw new Error(`MapLark API returned ${res.status}: ${await res.text()}`);
  }
  return { type: "geojson", data: await res.json() };
}

Migrating from Overpass

/v2/osm_features is designed so existing Overpass-style use cases map cleanly onto it: the same OSM tags, the same node/way/relation model, and geometry shapes you already know how to work with. What changes is the operational story with production SLA instead of shared community servers.

Ready to build?

Get unlimited free access during public preview.

Get your API key Read the docs