OSM Features PostGIS Schema
OpenStreetMap lives in three tables: one for points, one for lines, one for polygons. A cafe is a point. A road is a line. A park is a polygon. Each row is one feature: an OSM id, a geometry, common tags as their own columns, and the rest in tags.
For fast, low-latency queries, you need to narrow the search to a specific table. Set type and way_shape to do so. Geometry is EPSG:4326. The rows below are examples of what is stored.
Geometry Tables
Points
Nodes. Always a Point. Cafes, shops, benches, peaks, bus stops. Query with type=node.
osm.osm_point| osm_id | osm_type | tags | geom |
|---|---|---|---|
| 412883101 | N | {"name": "Vete-Katten", "amenity": "cafe"} | POINT(18.0649 59.3326) |
| 266091882 | N | {"name": "Fabrique", "shop": "bakery"} | POINT(18.0712 59.3341) |
| 178220340 | N | {"amenity": "bench", "backrest": "yes"} | POINT(18.0731 59.3388) |
Lines
Roads, paths, rivers, route relations, and the line copy of a boundary. Closed loops such as roundabouts stay here. Query with type=way&way_shape=line. Filter length with min_length_m and max_length_m.
osm.osm_line| osm_id | osm_type | length_m | tags | geom |
|---|---|---|---|---|
| 231501210 | W | 142.3 | {"name": "Drottninggatan", "highway": "residential"} | LINESTRING(18.061 59.333, 18.064 59.332) |
| 88441209 | W | 87.1 | {"highway": "footway", "surface": "paving_stones"} | LINESTRING(18.072 59.339, 18.073 59.338) |
| 15600344 | W | 2410.5 | {"name": "Norrström", "waterway": "river"} | LINESTRING(18.069 59.328, 18.072 59.327) |
Polygons
Buildings, parks, lakes, landuse, and boundary areas. Query with type=way&way_shape=polygon. Filter area with min_area_m2 and max_area_m2.
osm.osm_polygon| osm_id | osm_type | building | area_m2 | tags | geom |
|---|---|---|---|---|---|
| 24876102 | W | yes | 186.4 | {"building": "yes", "addr:housenumber": "12"} | POLYGON((18.070 59.334, 18.071 59.334, 18.071 59.333, 18.070 59.333, 18.070 59.334)) |
| 99120411 | W | NULL | 45200 | {"name": "Humlegården", "leisure": "park"} | POLYGON((18.072 59.339, 18.078 59.339, 18.078 59.336, 18.072 59.336, 18.072 59.339)) |
| 4401288 | R | NULL | 85000 | {"name": "Brunnsviken", "natural": "water"} | POLYGON((18.043 59.361, 18.055 59.361, 18.055 59.354, 18.043 59.354, 18.043 59.361)) |
Every row has osm_id, osm_type (N, W, or R), tags, and geom. API response ids look like node/123 or way/456. Filter any key that is not its own column through tags: key, key=value, or a numeric compare such as ele>500.
How to pick a table
type is the OSM element: node, way, or relation. way_shape is the geometry class for ways and relations: line or polygon. Nodes are always points, so they ignore way_shape. Omit both parameters to read all three tables.
| You ask for | The API reads |
|---|---|
type=node | osm_point |
type=way & way_shape=line or type=relation & way_shape=line | osm_line |
type=way & way_shape=polygon | osm_polygon |
type=node,way & way_shape=polygon | points plus polygons |
| omit both | points, lines, and polygons |
limit is the maximum number of features in the response (free tier is 75000 features). A mixed-geometry query reads each matching table, unions the rows, and applies that cap once. Set way_shape when you know the shape so the query reads one table. Parameter reference: OSM Features API.
Indexes
Filters that match these stay fast. Common tag keys use their own column index. Other keys use the tags index.
| Index | Table | What it speeds up |
|---|---|---|
GiST on geom | point, line, polygon | Bounding box and around searches. |
GIN on tags | point, line, polygon | Tag filters for keys without their own column. |
| btree on osm id | point, line, polygon | Lookup by OSM id, and paging. |
| btree on name | point, line, polygon | Name filters. |
btree on length_m | line | Line length filters. |
btree on area_m2 | polygon | Polygon area filters. |
| btree on common tags | point, line, polygon | Filters such as amenity, highway, and building. |
How to plan a query
- Bound the search: a viewport
bbox,locationplusradius,withinan OSM polygon, orosm_ids. Geocode named places first. - Set
typeandway_shapewhen the shape is known (buildings: polygon; roads: line; benches: node). - Filter with native OSM tags. Prefer
key=valueover a bare key. There is no proprietary category tree. - Keep a spatial bound. Country-wide tag scans are not a supported query shape.
- Count with
/v2/osm_features/count. - If an OR group has five or more tags (
tags=a|b|c|d|e), split it into separate queries with a few tags each and merge the results. One large OR is much slower. - Regex (
name~"^Star") cannot use an index. It filters rows already found by your bbox and indexed tags, so pair it with a tag such asamenity=cafe&name~"^Star". A regex-only query stays broad and gets the no-tag area cap.