{"openapi":"3.1.0","info":{"title":"MapLark APIs","description":"MapLark OSM Features API: query OpenStreetMap features from PostGIS. Default output is GeoJSON; also FlatGeobuf, GeoParquet, CSV, and TSV via the `Accept` header. Find roads, bike paths, buildings, bridges, and more. Filter by bounding box, radius, or OSM ID and by native OSM `key=value` tags. Pagination is in `X-Returned`, `X-Has-More`, and `X-Next-Cursor` on every response.\n\nTry it here: get an API key and put it in Authorize to start making requests for free.","version":"1.4.0"},"paths":{"/health":{"get":{"summary":"Health check","description":"Returns service and DB connectivity status.","operationId":"health_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v2/osm_features":{"get":{"tags":["OSM Features API"],"summary":"Query OpenStreetMap features by tags and bounding box (GeoJSON default; also FlatGeobuf, GeoParquet, CSV).","description":"Returns OSM features matching the given filters in the requested encoding\n(default GeoJSON FeatureCollection).\n\n**Spatial anchors**:\n- `location`, `radius` - within `radius` metres of the point (`location=lat,lng`).\n- `bbox` - intersects the bounding box.\n\n    **Tier-based spatial limits:**\n    - `/v2/osm_features` enforces per-tier bbox-area and radius caps.\n    - Caps differ for tagged (`tags`/`or_tags`) vs tag-free requests.\n    - Use `GET /v1/account` to inspect your tier and its limits.\n\n**Tag filters**\n- require a spatial anchor\n- `tags=key` or `tags=key=value`. Repeat for multiple AND conditions.\n- `or_tags=key` or `or_tags=key=value`. Repeat for multiple OR conditions.\nThe whole OR group is ANDed with `tags` and the spatial anchor.\n- `not_tags=key` or `not_tags=key=value`. Repeat for multiple NOT conditions.\nThe whole NOT group is ANDed with `tags` and the spatial anchor.\n\n**Direct ID lookup:**\n- `osm_ids=id1,id2,...` - fetch specific features by numeric OSM ID.\n- Cannot be combined with `bbox`, `location`/`radius`, `tags`, `or_tags`, or `not_tags`.\n\n**Optional Parameters**:\n\n**Output Format**\n- Set the ``Accept`` header to the media type you want. Default GeoJSON.\n- `application/geo+json` (default)\n- `text/csv` / `text/tab-separated-values` fixed columns (id, geometry WKT, centroid, length_m, area_m2, tags JSON)\n- `application/flatgeobuf` / `application/vnd.apache.parquet` binary from EWKB\n\n**Type**\n- `type=node|way|relation`\n    - One or more comma-separated OSM element types (for example, `type=node,way`).\n- Omit to query all three: node, way, and relation.\n- In OSM, a way can be open (line) or closed (polygon); use `way_shape` to narrow.\n\n**Way shape**\n- Geometry class for ways/relations (`line` / `polygon` / `all`). Nodes are always\n  points and are unioned when `type` includes `node`.\n- `way_shape=line` - open ways (roads, paths, rivers) or line-shaped relations.\n- `way_shape=polygon` - closed ways (buildings, parks) or multipolygon relations.\n- `way_shape=all` (or omit) - both line and polygon geometries.\n- Examples: `type=way&way_shape=polygon&tags=building` (polygon table only);\n  `type=node,way&way_shape=polygon` → `osm_pointpolygon` (points + area POIs);\n  `type=node,way&way_shape=line` → `osm_pointline`.\n- `shape` is a deprecated alias for `way_shape`.\n\n**Size**:\n- `min_length_m` / `max_length_m` - inclusive line length bounds in metres.\n- `min_area_m2` / `max_area_m2` - inclusive polygon area bounds in square metres.\n- `length_m` filters require line-capable routes (`way_shape=line` or `way_shape=all` with way/relation).\n- `area_m2` filters require polygon-capable routes (`way_shape=polygon` or `way_shape=all` with way/relation).\n\n**Pagination:**\n\nEvery response carries pagination in headers:\n- `X-Returned` - features in this page\n- `X-Has-More` - `true` / `false`\n- `X-Next-Cursor` - present when another page exists; pass as `cursor=` next\n\nResults are ordered by `osm_id` so pages are stable and seek efficiently\nregardless of page depth.\n\n**What's rejected (400):**\n- tag filter without a spatial anchor\n- `osm_ids` combined with a spatial anchor or tag filter\n- bbox area or radius exceeding your tier's tagged/tag-free limits\n\n**Examples** (Stockholm):\n\nBuildings, first page then next:\n`GET /v2/osm_features?bbox=18.05,59.33,18.10,59.36&type=way&way_shape=polygon&tags=building&limit=200`\n\n`GET /v2/osm_features?bbox=18.05,59.33,18.10,59.36&type=way&way_shape=polygon&tags=building&limit=200&cursor=<X-Next-Cursor>`\n\nCafes within 500 m:\n`GET /v2/osm_features?location=59.334,18.063&radius=500&tags=amenity=cafe`\n\nCycleways or roads with a bicycle tag:\n`GET /v2/osm_features?bbox=18.05,59.33,18.10,59.36&type=way&way_shape=line&or_tags=highway=cycleway&or_tags=bicycle`\n\nAll roads:\n`GET /v2/osm_features?bbox=18.05,59.33,18.10,59.36&type=way&way_shape=line&tags=highway`\n\nFetch by ID:\n`GET /v2/osm_features?type=way&osm_ids=111,222,333`","operationId":"get_features_v2_osm_features_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"type","in":"query","required":false,"schema":{"type":"string","description":"OSM element type to query. One of: ['node', 'relation', 'way']. Accepts a comma-separated list to query multiple element types. Default when omitted: node, way, and relation. A way can be open (line) or closed (polygon); use the `way_shape` parameter to narrow to one or the other.","title":"Type"},"description":"OSM element type to query. One of: ['node', 'relation', 'way']. Accepts a comma-separated list to query multiple element types. Default when omitted: node, way, and relation. A way can be open (line) or closed (polygon); use the `way_shape` parameter to narrow to one or the other."},{"name":"way_shape","in":"query","required":false,"schema":{"type":"string","description":"Geometry class for ways/relations. One of: ['all', 'line', 'polygon']. Nodes are always points and are unioned when `type` includes `node`; `type=node` alone stays on the point table (way_shape is ignored). `line` - open ways or line-shaped relations (routes, boundaries). `polygon` - closed ways or multipolygon relations. `all` - both shapes (default). Example: `type=way&way_shape=polygon&tags=building` returns building polygons. Example: `type=node,way&way_shape=polygon` returns points plus area POIs.","title":"Way Shape"},"description":"Geometry class for ways/relations. One of: ['all', 'line', 'polygon']. Nodes are always points and are unioned when `type` includes `node`; `type=node` alone stays on the point table (way_shape is ignored). `line` - open ways or line-shaped relations (routes, boundaries). `polygon` - closed ways or multipolygon relations. `all` - both shapes (default). Example: `type=way&way_shape=polygon&tags=building` returns building polygons. Example: `type=node,way&way_shape=polygon` returns points plus area POIs."},{"name":"shape","in":"query","required":false,"schema":{"type":"string","description":"Deprecated alias for `way_shape`. Prefer `way_shape`.","deprecated":true,"title":"Shape"},"description":"Deprecated alias for `way_shape`. Prefer `way_shape`.","deprecated":true},{"name":"location","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Point for a radius search as `lat,lng` (same fields as PlacesSearchRequest.location). Requires `radius`. Example: `location=59.334,18.063&radius=500&tags=amenity=cafe`","title":"Location"},"description":"Point for a radius search as `lat,lng` (same fields as PlacesSearchRequest.location). Requires `radius`. Example: `location=59.334,18.063&radius=500&tags=amenity=cafe`"},{"name":"radius","in":"query","required":false,"schema":{"anyOf":[{"type":"number","exclusiveMinimum":0},{"type":"null"}],"description":"Search radius in metres. Requires `location`. Maximum allowed radius depends on your tier and whether the request has positive tag filters (`tags`/`or_tags`).","title":"Radius"},"description":"Search radius in metres. Requires `location`. Maximum allowed radius depends on your tier and whether the request has positive tag filters (`tags`/`or_tags`)."},{"name":"bbox","in":"query","required":false,"schema":{"type":"string","description":"Bounding box as `min_lon,min_lat,max_lon,max_lat`. Example: `18.05,59.33,18.10,59.36`","title":"Bbox"},"description":"Bounding box as `min_lon,min_lat,max_lon,max_lat`. Example: `18.05,59.33,18.10,59.36`"},{"name":"osm_ids","in":"query","required":false,"schema":{"type":"string","description":"Comma-separated OSM IDs for direct lookup. Mutually exclusive with `bbox`, `location`/`radius`, and all tag filters. Example: `osm_ids=111,222,333`","title":"Osm Ids"},"description":"Comma-separated OSM IDs for direct lookup. Mutually exclusive with `bbox`, `location`/`radius`, and all tag filters. Example: `osm_ids=111,222,333`"},{"name":"tags","in":"query","required":false,"schema":{"type":"array","items":{"type":"string"},"description":"Tags filter (AND-combined, repeat for multiple). Use `key=value` for exact match or a bare `key` for key-exists. Example: `tags=amenity=cafe&tags=outdoor_seating=yes` or `tags=building`","default":[],"title":"Tags"},"description":"Tags filter (AND-combined, repeat for multiple). Use `key=value` for exact match or a bare `key` for key-exists. Example: `tags=amenity=cafe&tags=outdoor_seating=yes` or `tags=building`"},{"name":"or_tags","in":"query","required":false,"schema":{"type":"array","items":{"type":"string"},"description":"OR-combined tags filter (repeat for multiple). Items containing '=' match key=value; bare items match key-exists. The whole group is ANDed with any `tags` / spatial-anchor filters. Must be combined with a spatial anchor (`bbox` or `location`/`radius`). \nExample: `bbox=...&or_tags=highway=cycleway&or_tags=cycleway=lane`","default":[],"title":"Or Tags"},"description":"OR-combined tags filter (repeat for multiple). Items containing '=' match key=value; bare items match key-exists. The whole group is ANDed with any `tags` / spatial-anchor filters. Must be combined with a spatial anchor (`bbox` or `location`/`radius`). \nExample: `bbox=...&or_tags=highway=cycleway&or_tags=cycleway=lane`"},{"name":"not_tags","in":"query","required":false,"schema":{"type":"array","items":{"type":"string"},"description":"Exclusion filter (AND-combined, repeat for multiple). Use `key=value` to exclude features with that exact tag value, or a bare `key` to exclude features where the key exists at all. Must be combined with a spatial anchor (`bbox` or `location`/`radius`). \nExample: `bbox=...&or_tags=bicycle&not_tags=bicycle=no&not_tags=bicycle=private`","default":[],"title":"Not Tags"},"description":"Exclusion filter (AND-combined, repeat for multiple). Use `key=value` to exclude features with that exact tag value, or a bare `key` to exclude features where the key exists at all. Must be combined with a spatial anchor (`bbox` or `location`/`radius`). \nExample: `bbox=...&or_tags=bicycle&not_tags=bicycle=no&not_tags=bicycle=private`"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":10000,"minimum":1,"description":"Max features returned per table (default 1000).","default":1000,"title":"Limit"},"description":"Max features returned per table (default 1000)."},{"name":"disable_budget_warning","in":"query","required":false,"schema":{"type":"boolean","description":"Allow requests that exceed the per-request unit cap to run anyway. Per-second rate limits and monthly unit budgets still apply.","default":false,"title":"Disable Budget Warning"},"description":"Allow requests that exceed the per-request unit cap to run anyway. Per-second rate limits and monthly unit budgets still apply."},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Opaque pagination cursor from a previous response's `X-Next-Cursor` header. Omit to start from the first page. ","title":"Cursor"},"description":"Opaque pagination cursor from a previous response's `X-Next-Cursor` header. Omit to start from the first page. "},{"name":"zoom","in":"query","required":false,"schema":{"anyOf":[{"type":"number","minimum":0},{"type":"null"}],"description":"Map zoom level used to simplify geometry at lower zooms. Default is highest zoom (full detail).","title":"Zoom"},"description":"Map zoom level used to simplify geometry at lower zooms. Default is highest zoom (full detail)."},{"name":"min_length_m","in":"query","required":false,"schema":{"anyOf":[{"type":"number","minimum":0},{"type":"null"}],"description":"Minimum line length in metres (inclusive). Applies to line geometry routes.","title":"Min Length M"},"description":"Minimum line length in metres (inclusive). Applies to line geometry routes."},{"name":"max_length_m","in":"query","required":false,"schema":{"anyOf":[{"type":"number","minimum":0},{"type":"null"}],"description":"Maximum line length in metres (inclusive). Applies to line geometry routes.","title":"Max Length M"},"description":"Maximum line length in metres (inclusive). Applies to line geometry routes."},{"name":"min_area_m2","in":"query","required":false,"schema":{"anyOf":[{"type":"number","minimum":0},{"type":"null"}],"description":"Minimum polygon area in square metres (inclusive). Applies to polygon geometry routes.","title":"Min Area M2"},"description":"Minimum polygon area in square metres (inclusive). Applies to polygon geometry routes."},{"name":"max_area_m2","in":"query","required":false,"schema":{"anyOf":[{"type":"number","minimum":0},{"type":"null"}],"description":"Maximum polygon area in square metres (inclusive). Applies to polygon geometry routes.","title":"Max Area M2"},"description":"Maximum polygon area in square metres (inclusive). Applies to polygon geometry routes."},{"name":"centroid","in":"query","required":false,"schema":{"type":"boolean","description":"When true, non-point features include `properties.centroid` (GeoJSON Point). Default false — skip centroid computation for faster large polygon responses.","default":false,"title":"Centroid"},"description":"When true, non-point features include `properties.centroid` (GeoJSON Point). Default false — skip centroid computation for faster large polygon responses."},{"name":"clipGeometry","in":"query","required":false,"schema":{"type":"boolean","description":"When true (default), clip returned geometry to the requested bbox. Set false to return full geometry for features that intersect the bbox.","default":false,"title":"Clipgeometry"},"description":"When true (default), clip returned geometry to the requested bbox. Set false to return full geometry for features that intersect the bbox."}],"responses":{"200":{"description":"Feature page. Choose encoding with the media-type dropdown (``Accept``). Default ``application/geo+json``.","content":{"application/geo+json":{"schema":{"$ref":"#/components/schemas/GeoJSONFeatureCollectionResponse"},"example":{"type":"FeatureCollection","features":[{"type":"Feature","id":"way/123456789","geometry":{"type":"Polygon","coordinates":[[[18.0701,59.334],[18.0703,59.334],[18.0703,59.3342],[18.0701,59.3342],[18.0701,59.334]]]},"properties":{"tags":{"building":"yes","addr:street":"Kungsgatan"},"centroid":{"type":"Point","coordinates":[18.0702,59.3341]},"area_m2":412.5}}]}},"text/csv":{"schema":{"type":"string"},"example":"id,geometry,centroid,length_m,area_m2,tags\nway/123456789,\"POLYGON((18.0701 59.334,18.0703 59.334,18.0703 59.3342,18.0701 59.3342,18.0701 59.334))\",POINT(18.0702 59.3341),,412.5,\"{\"\"addr:street\"\":\"\"Kungsgatan\"\",\"\"building\"\":\"\"yes\"\"}\"\n"},"text/tab-separated-values":{"schema":{"type":"string"},"example":"id\tgeometry\tcentroid\tlength_m\tarea_m2\ttags\nway/123456789\tPOLYGON((18.0701 59.334,18.0703 59.334,18.0703 59.3342,18.0701 59.3342,18.0701 59.334))\tPOINT(18.0702 59.3341)\t\t412.5\t\"{\"\"addr:street\"\":\"\"Kungsgatan\"\",\"\"building\"\":\"\"yes\"\"}\"\n"},"application/flatgeobuf":{"schema":{"type":"string","format":"binary","description":"FlatGeobuf binary FeatureCollection."}},"application/vnd.apache.parquet":{"schema":{"type":"string","format":"binary","description":"GeoParquet (Apache Parquet) binary FeatureCollection."}}},"headers":{"X-Returned":{"description":"Number of features in this page.","schema":{"type":"integer"}},"X-Has-More":{"description":"Whether another page is available (`true` / `false`).","schema":{"type":"string"}},"X-Next-Cursor":{"description":"Opaque cursor for the next page when X-Has-More is true.","schema":{"type":"string"}}}},"400":{"description":"Bad request: invalid filters, spatial limits, or query parameters."},"401":{"description":"Unauthorized: missing or invalid API key."},"403":{"description":"Forbidden: account tier is not recognized."},"429":{"description":"Too many requests: rate limit or monthly budget exceeded."},"422":{"description":"Validation error in one or more request parameters."}}}},"/v2/osm_features/cost":{"get":{"tags":["OSM Features API"],"summary":"Estimate query cost without executing it","description":"Returns an estimated API credit charge for the given query shape without executing it.\n\nThe estimate is deterministic: identical request shapes always produce the same result.\nUse this endpoint to preview credit impact before sending the request to\n`/v2/osm_features`.\n\nThe same spatial-cap validation as `/v2/osm_features` applies - a 400 is returned when\nthe bbox/radius would be rejected outright.\n\nResponse includes:\n- `estimated_credits` - total API credits expected to be charged\n- `tier_limits` - your current tier quotas and spatial limits\n- `hints` - actionable suggestions to reduce cost if needed","operationId":"get_features_cost_v2_osm_features_cost_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"type","in":"query","required":false,"schema":{"type":"string","title":"Type"}},{"name":"way_shape","in":"query","required":false,"schema":{"type":"string","title":"Way Shape"}},{"name":"shape","in":"query","required":false,"schema":{"type":"string","deprecated":true,"title":"Shape"},"deprecated":true},{"name":"location","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Location"}},{"name":"radius","in":"query","required":false,"schema":{"anyOf":[{"type":"number","exclusiveMinimum":0},{"type":"null"}],"title":"Radius"}},{"name":"bbox","in":"query","required":false,"schema":{"type":"string","title":"Bbox"}},{"name":"osm_ids","in":"query","required":false,"schema":{"type":"string","title":"Osm Ids"}},{"name":"tags","in":"query","required":false,"schema":{"type":"array","items":{"type":"string"},"default":[],"title":"Tags"}},{"name":"or_tags","in":"query","required":false,"schema":{"type":"array","items":{"type":"string"},"default":[],"title":"Or Tags"}},{"name":"not_tags","in":"query","required":false,"schema":{"type":"array","items":{"type":"string"},"default":[],"title":"Not Tags"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":10000,"minimum":1,"default":1000,"title":"Limit"}},{"name":"disable_budget_warning","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Disable Budget Warning"}},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cursor"}},{"name":"zoom","in":"query","required":false,"schema":{"anyOf":[{"type":"number","minimum":0},{"type":"null"}],"title":"Zoom"}},{"name":"min_length_m","in":"query","required":false,"schema":{"anyOf":[{"type":"number","minimum":0},{"type":"null"}],"title":"Min Length M"}},{"name":"max_length_m","in":"query","required":false,"schema":{"anyOf":[{"type":"number","minimum":0},{"type":"null"}],"title":"Max Length M"}},{"name":"min_area_m2","in":"query","required":false,"schema":{"anyOf":[{"type":"number","minimum":0},{"type":"null"}],"title":"Min Area M2"}},{"name":"max_area_m2","in":"query","required":false,"schema":{"anyOf":[{"type":"number","minimum":0},{"type":"null"}],"title":"Max Area M2"}},{"name":"centroid","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Centroid"}},{"name":"clipGeometry","in":"query","required":false,"schema":{"type":"boolean","default":true,"title":"Clipgeometry"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CostEstimateResponse"}}}},"400":{"description":"Bad request: invalid filters, spatial limits, or query parameters."},"401":{"description":"Unauthorized: missing or invalid API key."},"403":{"description":"Forbidden: account tier is not recognized."},"422":{"description":"Validation error in one or more request parameters."}}}},"/v1/places/search":{"post":{"tags":["Places API"],"summary":"Find places in bbox or radius (tags, optional opening hours)","description":"Point and polygon POIs in a bbox or `location`+`radius` (not both).\n\nScans OSM points plus polygon ways/relations. Polygon POIs are returned as\nPoint at the centroid. Known hours set `properties.openNow` true/false;\nunknown hours omit the field.\n\n`openNow=true` pages hours-tagged rows until it has `limit` known-open\nplaces (max 100), the spatial set is empty, or 10k hours-tagged rows have\nbeen scanned. Missing or unparseable hours are dropped. `asOf` evaluates\nhours at that instant (default: now) and requires an OSM `opening_hours`\ntag. Naive `asOf` (no offset) is that local clock at the search location\nor bbox center. Closed places with hours still return unless `openNow` is\nset. Hours use each place's IANA zone from its coordinates.\n\nResponse: GeoJSON FeatureCollection plus `metadata.evaluated_at` (UTC\ninstant) and `metadata.estimated_units`.","operationId":"places_search_v1_places_search_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlacesSearchRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/v1/places/nearby":{"post":{"tags":["Places API"],"summary":"Nearest places from a location (straight-line ranked)","description":"One set of places ranked by spheroid `ST_Distance` from `location`.\n\nRequires `tags` or `orTags`. Same point-or-polygon-POI surface as search.\nKnown hours set `properties.openNow` true/false; unknown hours omit the field.\n\n`openNow=true` pages nearest hours-tagged rows until it has `limit`\nknown-open places (max 100), the radius is empty, or 10k hours-tagged rows\nhave been scanned. Missing or unparseable hours are dropped. `asOf`\nevaluates hours at that instant (default: now) and requires an OSM\n`opening_hours` tag. Naive `asOf` (no offset) is that local clock at\n`location`. Closed places with hours still return unless `openNow` is set.\nHours use each place's IANA zone from its coordinates.\n\nResponse: `{status, items: [{feature, distance_m}], estimated_units,\nevaluated_at}`. `openNow` also returns `input_count` and `kept_count`.","operationId":"places_nearby_v1_places_nearby_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NearbyRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/v1/places/{osm_type}/{osm_id}":{"get":{"tags":["Places API"],"summary":"One place by OSM id (same feature shape as search)","description":"Lookup one place by the `node/123` id that search or nearby returned.\n\nSame point-or-polygon-POI surface as search: a line-only way is not a\nplace. Hours are annotated at request time in the place's IANA timezone\nfrom its coordinates.\n\nResponse: `{status, feature, estimated_units, evaluated_at, timezone}`.\n`evaluated_at` is the UTC instant. `timezone` is the place's IANA zone.\nMissing or non-place id → 404 (still billed as one osm_ids lookup).\nBad type or non-positive id → 400.","operationId":"places_details_v1_places__osm_type___osm_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"osm_type","in":"path","required":true,"schema":{"type":"string","description":"OSM type from a search or nearby id: node, way, or relation.","title":"Osm Type"},"description":"OSM type from a search or nearby id: node, way, or relation."},{"name":"osm_id","in":"path","required":true,"schema":{"type":"string","description":"Positive OSM id from a search or nearby feature id (e.g. 123 from node/123).","title":"Osm Id"},"description":"Positive OSM id from a search or nearby feature id (e.g. 123 from node/123)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/routes/isochrone":{"post":{"tags":["Routing API"],"summary":"Network reach polygon (buffered union of reachable edges)","description":"Walk or bike reach polygon on the OSM network from `origin`.\n\nProvide exactly one of `max_distance_m` or `duration_s`. Duration converts\nwith WALK 5 km/h or BICYCLE 15 km/h. `travelMode`\ndefaults to WALK. Geometry is a buffered union of reachable edge stubs\n(city blocks remain holes).\n\nStatus is in the 200 body: `ok`, `area_too_large_for_tier`,\n`tile_too_dense`, `start_unreachable`, `no_path_within_area`.","operationId":"routes_isochrone_v1_routes_isochrone_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IsochroneRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/v1/routes/path":{"post":{"tags":["Routing API"],"summary":"Given-order walk or bicycle path along listed stops (no TSP)","description":"Dijkstra each consecutive pair of `stops` in listed order.\n\n2–250 points. Does not reorder stops or close a loop. Two stops is one\nDijkstra. Unordered search hits belong on `optimized_path`. To walk a\nknown sequence home, repeat home as the last stop.\n\nStatus is in the 200 body (`ok`, `area_too_large_for_tier`,\n`tile_too_dense`, `start_unreachable`, `end_unreachable`,\n`no_path_within_area`).","operationId":"routes_path_v1_routes_path_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoutePathRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/v1/routes/optimized_path":{"post":{"tags":["Routing API"],"summary":"TSP walk or bicycle tour from start; loop=true returns to start","description":"TSP from `start` through unordered `stops` (nearest-neighbour + 2-opt).\n\nDo not put start in `stops`. `loop=true` (default) scores a cycle and\nDijkstra includes the return; `loop=false` is an open path. Response\nincludes `ordered_stops` (start first). Do not invent an order and call\n`path`.\n\nStatus is in the 200 body (`ok`, `area_too_large_for_tier`,\n`tile_too_dense`, `start_unreachable`, `end_unreachable`,\n`no_path_within_area`).","operationId":"routes_optimized_path_v1_routes_optimized_path_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RouteOptimizedPathRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/v1/tiers":{"get":{"tags":["Account"],"summary":"List plan tiers and limits","description":"Public catalog of TIER_LIMITS. Order is free → pro → premium → enterprise.","operationId":"list_tiers_v1_tiers_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TiersResponse"}}}}}}},"/v1/usage":{"get":{"tags":["Usage"],"summary":"Get current credit budget usage","description":"Returns this month's credit consumption for the active API key.\n\nAccepts an API key or a Firebase ID token. Counters come from the same\nPostgres buckets used for enforcement, so they match what billed\nendpoints will check.","operationId":"get_usage_v1_usage_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsageSummaryResponse"}}}}},"security":[{"HTTPBearer":[]}]}},"/v1/usage/history":{"get":{"tags":["Usage"],"summary":"Get historical usage series","description":"Returns a zero-filled historical usage series for dashboard charts.\n\nUnlike `/v1/usage`, which reads the current enforcement bucket,\nthis endpoint aggregates the append-only `app.usage_ledger` so clients can\nrender usage-over-time graphs, optionally stacked by `api_surface`.","operationId":"get_usage_history_v1_usage_history_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"bucket","in":"query","required":false,"schema":{"type":"string","pattern":"^(second|minute|hour|day)$","description":"Time bucket for the returned series: second, minute, hour, or day.","default":"hour","title":"Bucket"},"description":"Time bucket for the returned series: second, minute, hour, or day."},{"name":"points","in":"query","required":false,"schema":{"type":"integer","maximum":720,"minimum":1,"description":"Number of time buckets to return, oldest to newest.","default":24,"title":"Points"},"description":"Number of time buckets to return, oldest to newest."},{"name":"api_surface","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(geojson|tile|routing|geocode)$"},{"type":"null"}],"description":"Optional product filter. Omit for all surfaces (one point per bucket × surface). Values: geojson, tile, routing, geocode.","title":"Api Surface"},"description":"Optional product filter. Omit for all surfaces (one point per bucket × surface). Values: geojson, tile, routing, geocode."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsageHistoryResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/usage/countries":{"get":{"tags":["Usage"],"summary":"Get query-country request counts","description":"Request and credit counts by query-centroid country for dashboard choropleth.\n\nSame time window as `/v1/usage/history`. Default surfaces are geojson,\nrouting, and geocode. Tiles have no query country. Ocean and osm_ids\nlookups are counted in `unknown_count` / `unknown_charged_units`.","operationId":"get_usage_countries_v1_usage_countries_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"bucket","in":"query","required":false,"schema":{"type":"string","pattern":"^(second|minute|hour|day)$","description":"Time bucket for the returned window: second, minute, hour, or day.","default":"hour","title":"Bucket"},"description":"Time bucket for the returned window: second, minute, hour, or day."},{"name":"points","in":"query","required":false,"schema":{"type":"integer","maximum":720,"minimum":1,"description":"Number of time buckets in the window, matching /v1/usage/history.","default":24,"title":"Points"},"description":"Number of time buckets in the window, matching /v1/usage/history."},{"name":"api_surface","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(geojson|tile|routing|geocode)$"},{"type":"null"}],"description":"Optional product filter. Omit for spatial surfaces (geojson, routing, geocode). Values: geojson, tile, routing, geocode.","title":"Api Surface"},"description":"Optional product filter. Omit for spatial surfaces (geojson, routing, geocode). Values: geojson, tile, routing, geocode."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsageCountriesResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"schemas":{"CostEstimateResponse":{"properties":{"estimated_credits":{"type":"integer","title":"Estimated Credits"},"tier_limits":{"additionalProperties":{"anyOf":[{"type":"integer"},{"type":"number"},{"type":"string"},{"additionalProperties":{"anyOf":[{"type":"integer"},{"type":"number"},{"type":"null"}]},"type":"object"},{"type":"null"}]},"type":"object","title":"Tier Limits"},"hints":{"items":{"type":"string"},"type":"array","title":"Hints"}},"type":"object","required":["estimated_credits","tier_limits","hints"],"title":"CostEstimateResponse","description":"Preflight credit estimate for a /v2/osm_features query shape."},"GeoJSONFeature":{"properties":{"type":{"type":"string","const":"Feature","title":"Type"},"id":{"type":"string","title":"Id"},"geometry":{"additionalProperties":true,"type":"object","title":"Geometry"},"properties":{"additionalProperties":true,"type":"object","title":"Properties"}},"type":"object","required":["type","id","geometry","properties"],"title":"GeoJSONFeature","description":"Minimal GeoJSON feature shape returned by /v2/osm_features."},"GeoJSONFeatureCollectionResponse":{"properties":{"type":{"type":"string","const":"FeatureCollection","title":"Type"},"features":{"items":{"$ref":"#/components/schemas/GeoJSONFeature"},"type":"array","title":"Features"}},"type":"object","required":["type","features"],"title":"GeoJSONFeatureCollectionResponse","description":"FeatureCollection returned by /v2/osm_features (Accept: application/geo+json).\n\nPagination is in response headers: X-Returned, X-Has-More, X-Next-Cursor."},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"IsochroneRequest":{"properties":{"origin":{"$ref":"#/components/schemas/LonLat","description":"Network start point."},"max_distance_m":{"anyOf":[{"type":"number","exclusiveMinimum":0.0},{"type":"null"}],"title":"Max Distance M","description":"Reach budget in metres. Provide this or duration_s, not both."},"duration_s":{"anyOf":[{"type":"number","exclusiveMinimum":0.0},{"type":"null"}],"title":"Duration S","description":"Reach budget in seconds. Converted with WALK 1.4 m/s or BICYCLE 4.2 m/s."},"search_buffer_m":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Search Buffer M","description":"Optional extra metres on the highway fetch corridor."},"travelMode":{"type":"string","enum":["WALK","BICYCLE"],"title":"Travelmode","description":"WALK (undirected) or BICYCLE (honors OSM oneway).","default":"WALK"}},"type":"object","required":["origin"],"title":"IsochroneRequest","description":"Request body for POST /v1/routes/isochrone.\n\nThe response geometry is a buffered union of network edges reachable\nwithin the budget (city blocks remain holes)."},"LatLng":{"properties":{"lat":{"type":"number","title":"Lat","description":"Latitude in WGS84 degrees."},"lng":{"type":"number","title":"Lng","description":"Longitude in WGS84 degrees."}},"type":"object","required":["lat","lng"],"title":"LatLng","description":"Point coordinate (Google-style lat/lng)."},"LonLat":{"properties":{"lon":{"type":"number","title":"Lon","description":"Longitude in WGS84 degrees."},"lat":{"type":"number","title":"Lat","description":"Latitude in WGS84 degrees."}},"type":"object","required":["lon","lat"],"title":"LonLat","description":"Point coordinate (lon, lat)."},"NearbyRequest":{"properties":{"location":{"$ref":"#/components/schemas/LatLng","description":"Center point to rank from."},"radius":{"type":"number","exclusiveMinimum":0.0,"title":"Radius","description":"Search radius in metres.","default":1000.0},"tags":{"items":{"type":"string"},"type":"array","title":"Tags","description":"AND OSM tag filters. `key` or `key=value`. Provide tags or orTags."},"orTags":{"items":{"type":"string"},"type":"array","title":"Ortags","description":"OR OSM tag filters. `key` or `key=value`. Provide tags or orTags."},"limit":{"type":"integer","maximum":10000.0,"minimum":1.0,"title":"Limit","description":"Max ranked items to return. With openNow, max is 100.","default":100},"openNow":{"type":"boolean","title":"Opennow","description":"If true, return only the nearest places known open at evaluation time (Google Places openNow semantics). Places with missing or unparseable opening_hours are excluded.","default":false},"asOf":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Asof","description":"Evaluate opening hours as of this instant (default: request time). Naive values (no offset) are that local clock at location. Also requires an OSM opening_hours tag."}},"type":"object","required":["location"],"title":"NearbyRequest","description":"Request body for POST /v1/places/nearby (ranked by straight-line distance)."},"PlacesSearchRequest":{"properties":{"bbox":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Bbox","description":"min_lon,min_lat,max_lon,max_lat. Mutually exclusive with location+radius.","examples":["18.05,59.31,18.10,59.33"]},"location":{"anyOf":[{"$ref":"#/components/schemas/LatLng"},{"type":"null"}],"description":"Center point. Requires radius. Mutually exclusive with bbox."},"radius":{"anyOf":[{"type":"number","exclusiveMinimum":0.0},{"type":"null"}],"title":"Radius","description":"Search radius in metres. Required when location is set."},"tags":{"items":{"type":"string"},"type":"array","title":"Tags","description":"AND OSM tag filters. `key` or `key=value`.","examples":[["amenity=cafe"]]},"orTags":{"items":{"type":"string"},"type":"array","title":"Ortags","description":"OR OSM tag filters. `key` or `key=value`.","examples":[["amenity=cafe","amenity=restaurant"]]},"limit":{"type":"integer","maximum":10000.0,"minimum":1.0,"title":"Limit","description":"Max features to return. With openNow, max is 100.","default":100},"openNow":{"type":"boolean","title":"Opennow","description":"If true, return only places known open at evaluation time (Google Places openNow semantics). Places with missing or unparseable opening_hours are excluded.","default":false},"asOf":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Asof","description":"Evaluate opening hours as of this instant (default: request time). Naive values (no offset) are that local clock at the search location or bbox center. Also requires an OSM opening_hours tag."}},"type":"object","title":"PlacesSearchRequest","description":"Request body for POST /v1/places/search (spatial + tag discovery)."},"RouteOptimizedPathRequest":{"properties":{"start":{"$ref":"#/components/schemas/LonLat","description":"Tour start. Do not repeat this point in stops."},"stops":{"items":{"$ref":"#/components/schemas/LonLat"},"type":"array","maxItems":249,"minItems":1,"title":"Stops","description":"Unordered visit points, not including start. 1–249."},"search_buffer_m":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Search Buffer M","description":"Optional extra metres on the highway fetch corridor."},"loop":{"type":"boolean","title":"Loop","description":"If true, score a cycle and Dijkstra includes the return to start.","default":true},"travelMode":{"type":"string","enum":["WALK","BICYCLE"],"title":"Travelmode","description":"WALK (undirected) or BICYCLE (honors OSM oneway).","default":"WALK"}},"type":"object","required":["start","stops"],"title":"RouteOptimizedPathRequest","description":"Request body for POST /v1/routes/optimized_path (TSP from start)."},"RoutePathRequest":{"properties":{"stops":{"items":{"$ref":"#/components/schemas/LonLat"},"type":"array","maxItems":250,"minItems":2,"title":"Stops","description":"Visit points in order. 2–250. Two stops is one Dijkstra."},"search_buffer_m":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Search Buffer M","description":"Optional extra metres on the highway fetch corridor."},"travelMode":{"type":"string","enum":["WALK","BICYCLE"],"title":"Travelmode","description":"WALK (undirected) or BICYCLE (honors OSM oneway).","default":"WALK"}},"type":"object","required":["stops"],"title":"RoutePathRequest","description":"Request body for POST /v1/routes/path (given-order, no TSP)."},"SurfaceRates":{"properties":{"geojson":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Geojson"},"tile":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Tile"},"routing":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Routing"},"geocode":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Geocode"}},"type":"object","required":["geojson","tile","routing","geocode"],"title":"SurfaceRates"},"TierLimits":{"properties":{"id":{"type":"string","title":"Id"},"label":{"type":"string","title":"Label"},"price_usd":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Price Usd"},"req_per_second":{"$ref":"#/components/schemas/SurfaceRates"},"req_per_second_burst":{"$ref":"#/components/schemas/SurfaceRates"},"units_per_month":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Units Per Month"},"max_units_per_request":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Units Per Request"},"max_bbox_area_tagged":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Max Bbox Area Tagged"},"max_bbox_area_tag_free":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Max Bbox Area Tag Free"},"max_around_radius_m_tagged":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Max Around Radius M Tagged"},"max_around_radius_m_tag_free":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Max Around Radius M Tag Free"}},"type":"object","required":["id","label","price_usd","req_per_second","req_per_second_burst","units_per_month","max_units_per_request","max_bbox_area_tagged","max_bbox_area_tag_free","max_around_radius_m_tagged","max_around_radius_m_tag_free"],"title":"TierLimits"},"TiersResponse":{"properties":{"tiers":{"items":{"$ref":"#/components/schemas/TierLimits"},"type":"array","title":"Tiers"}},"type":"object","required":["tiers"],"title":"TiersResponse"},"UsageCountriesResponse":{"properties":{"countries":{"items":{"$ref":"#/components/schemas/UsageCountryCount"},"type":"array","title":"Countries"},"unknown_count":{"type":"integer","title":"Unknown Count"},"unknown_charged_units":{"type":"integer","title":"Unknown Charged Units"},"api_surface":{"anyOf":[{"type":"string","enum":["geojson","tile","routing","geocode"]},{"type":"null"}],"title":"Api Surface"}},"type":"object","required":["countries","unknown_count","unknown_charged_units"],"title":"UsageCountriesResponse","description":"Per-country request and credit counts for a history-style time window."},"UsageCountryCount":{"properties":{"country_code":{"type":"string","title":"Country Code"},"request_count":{"type":"integer","title":"Request Count"},"charged_units":{"type":"integer","title":"Charged Units"}},"type":"object","required":["country_code","request_count","charged_units"],"title":"UsageCountryCount"},"UsageHistoryPoint":{"properties":{"bucket_start":{"type":"string","format":"date-time","title":"Bucket Start"},"api_surface":{"type":"string","enum":["geojson","tile","routing","geocode"],"title":"Api Surface"},"request_count":{"type":"integer","title":"Request Count"},"units":{"type":"integer","title":"Units"},"charged_units":{"type":"integer","title":"Charged Units"},"success_count":{"type":"integer","title":"Success Count"},"rate_limited_count":{"type":"integer","title":"Rate Limited Count"},"cache_hit_count":{"type":"integer","title":"Cache Hit Count"},"avg_latency_ms":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Avg Latency Ms"}},"type":"object","required":["bucket_start","api_surface","request_count","units","charged_units","success_count","rate_limited_count","cache_hit_count","avg_latency_ms"],"title":"UsageHistoryPoint","description":"One bucket × api_surface in the authenticated user's historical usage series."},"UsageHistoryResponse":{"properties":{"tier":{"type":"string","title":"Tier"},"api_key_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Key Id"},"bucket":{"type":"string","enum":["second","minute","hour","day"],"title":"Bucket"},"api_surface":{"anyOf":[{"type":"string","enum":["geojson","tile","routing","geocode"]},{"type":"null"}],"title":"Api Surface"},"points":{"items":{"$ref":"#/components/schemas/UsageHistoryPoint"},"type":"array","title":"Points"}},"type":"object","required":["tier","api_key_id","bucket","points"],"title":"UsageHistoryResponse","description":"Historical usage series for dashboard charts (one point per bucket × surface)."},"UsageSummaryResponse":{"properties":{"tier":{"type":"string","title":"Tier"},"api_key_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Key Id"},"units_per_month":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Units Per Month"},"usage_this_month":{"type":"integer","title":"Usage This Month"},"remaining_this_month":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Remaining This Month"}},"type":"object","required":["tier","api_key_id","units_per_month","usage_this_month","remaining_this_month"],"title":"UsageSummaryResponse","description":"Current unit-budget usage for the authenticated user's active API key."},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}},"securitySchemes":{"HTTPBearer":{"type":"http","scheme":"bearer"}}}}