{"openapi": "3.1.0", "info": {"title": "Mundi.ai Developer API", "summary": "Mundi.ai has a developer API for creating, editing, and sharing maps and map data.", "description": "\nMundi is a customizable, open source web GIS and can be operated via API just like it can be used as a web app. You can [programatically create maps](/developer-api/operations/create_map/), [upload geospatial data](/developer-api/operations/upload_layer_to_map/) (vectors, raster, point clouds), and share map links or embed maps in other web applications.\n\nMundi's API is both available as a [hosted cloud service](https://mundi.ai) or\n[a self-hosted set of Docker images](https://github.com/buntinglabs/mundi.ai), open source under the AGPLv3 license.\n\nTo get started, create an account at [Mundi.ai](https://app.mundi.ai) and create a new API key [here](https://app.mundi.ai/settings/api-keys).\nWhen sending requests, set the `Authorization` header to `Bearer YOUR_API_KEY`. API keys start with `sk-...`. Never share your API keys.\n\n```py\n# 1. create a new map project\ncreated_map = httpx.post(\n \"https://app.mundi.ai/api/maps/create\",\n json={\"title\": \"US political boundaries\"},\n headers={\"Authorization\": f\"Bearer {os.environ[\"MUNDI_API_KEY\"]}\"},\n).json()\nmap_id, project_id = created_map[\"id\"], created_map[\"project_id\"]\n\n# 2. upload a GeoJSON file as a layer on that map\nwith open(\"counties.geojson\", \"rb\") as f:\n upload = httpx.post(\n f\"https://app.mundi.ai/api/maps/{map_id}/layers\",\n files={\"file\": (\"counties.geojson\", f, \"application/geo+json\")},\n data={\"layer_name\": \"US Counties\", \"add_layer_to_map\": True},\n headers={\"Authorization\": f\"Bearer {os.environ[\"MUNDI_API_KEY\"]}\"},\n ).json()\n\n# 3. link to view the map with the uploaded layer\nprint(f\"https://app.mundi.ai/project/{project_id}/{map_id}\")\n```\n", "termsOfService": "https://buntinglabs.com/legal/terms", "contact": {"name": "Bunting Labs", "url": "https://buntinglabs.com/"}, "version": "0.0.1"}, "paths": {"/api/maps/create": {"post": {"tags": ["Maps"], "summary": "Create a new map", "description": "Creates a new map project. Projects contain multiple map versions (\"maps\"),\nunattached layer data, and a history of changes to the project. Each edit will\ncreate a new map version.\n\nAccepts `title` in the request body. Returns overarching project id\n`project_id` and initial map version id `id`.\n\n```py\nresult = httpx.post(\n \"https://app.mundi.ai/api/maps/create\",\n json={\"title\": \"Brazilian catchment areas\"},\n headers={\"Authorization\": f\"Bearer {os.environ['MUNDI_API_KEY']}\"}\n).json()\n\nassert result == {\n \"title\": \"Brazilian catchment areas\",\n \"created_on\": \"2025-08-29T12:34:56.789Z\",\n \"map_link\": \"https://app.mundi.ai/project/PGJSkB1zj7fT\",\n \"id\": \"MWfqcRak59bo\",\n \"project_id\": \"PGJSkB1zj7fT\"\n}\n```", "operationId": "create_map", "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/MapCreateRequest"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/MapResponse"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/api/maps/{original_map_id}/layers": {"post": {"tags": ["Maps"], "summary": "Upload file as layer", "description": "Uploads spatial data, processes it, and adds it as a layer to the specified map.\n\nSupported formats:\n- Vector: Shapefile (as .zip), GeoJSON, GeoPackage, FlatGeobuf\n- Raster: GeoTIFF, DEM\n- [Point cloud](/guides/visualizing-point-clouds-las-laz/): LAZ, LAS\n\nOnce uploaded, Mundi transforms, reprojects, styles, and creates optimized formats for display in the browser.\nVector data is converted to [PMTiles](https://docs.protomaps.com/pmtiles/) while raster data is converted to\n[cloud-optimized GeoTIFFs](https://cogeo.org/). Point cloud data is compressed to LAZ 1.3.\n\nReturns the new layer details including its unique layer ID. The layer can optionally not be added to the map,\nbut will be faster to add to an existing map later.\n\n```py\nwith open(\"brazil_watersheds.gpkg\", \"rb\") as f:\n # project ID is PGJSkB1zj7fT, previous map ID is M4NzE8rk4FZS\n result = httpx.post(\n f\"https://app.mundi.ai/api/maps/M4NzE8rk4FZS/layers\",\n files={\"file\": (\"brazil_watersheds.gpkg\", f, \"application/octet-stream\")},\n data={\"layer_name\": \"Amazon Basin Watersheds\", \"add_layer_to_map\": True},\n headers={\"Authorization\": f\"Bearer {os.environ['MUNDI_API_KEY']}\"}\n ).json()\n\nassert result[\"name\"] == \"Amazon Basin Watersheds\"\nassert result[\"dag_child_map_id\"] == \"M4NzE8rk4FZS\"\n# use result[\"dag_child_map_id\"] as the new map id, and view this new uploaded layer\n# by navigating to https://app.mundi.ai/project/PGJSkB1zj7fT/M4NzE8rk4FZS\nsubprocess.run([\"open\", \"https://app.mundi.ai/project/PGJSkB1zj7fT/M4NzE8rk4FZS\"])\n```", "operationId": "upload_layer_to_map", "parameters": [{"name": "original_map_id", "in": "path", "required": true, "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"multipart/form-data": {"schema": {"$ref": "#/components/schemas/Body_upload_layer_to_map"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/LayerUploadResponse"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/api/maps/{map_id}/render.png": {"get": {"tags": ["Maps"], "summary": "Render a map as PNG", "description": "Renders a map as a static PNG image, including layers and their symbology.\n\nIf no `bbox` is provided, the extent defaults to the smallest extent that contains\nall layers with well-defined bounding boxes. `bbox` must be in the format `xmin,ymin,xmax,ymax` (EPSG:4326).\n\nWidth and height are in pixels.", "operationId": "render_map_to_png", "parameters": [{"name": "map_id", "in": "path", "required": true, "schema": {"type": "string"}}, {"name": "bbox", "in": "query", "required": false, "schema": {"anyOf": [{"type": "string"}, {"type": "null"}]}}, {"name": "width", "in": "query", "required": false, "schema": {"type": "integer", "default": 1024}}, {"name": "height", "in": "query", "required": false, "schema": {"type": "integer", "default": 600}}, {"name": "bgcolor", "in": "query", "required": false, "schema": {"type": "string", "default": "#ffffff"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/api/maps/{map_id}": {"patch": {"tags": ["Maps"], "summary": "Update map", "description": "Updates an existing map's properties. Currently supports updating\nthe map's basemap style.\n\nThe basemap determines the background map tiles displayed beneath your\ndata layers. Available basemap options for Mundi cloud are from MapTiler:\n- `hybrid` - Satellite imagery\n- `basic-v2` - Basic street map (default)\n- `dataviz` - Light basemap for data visualization\n- `dataviz-dark` - Dark basemap for data visualization\n- `outdoor-v2` - Outdoor/terrain map\n\n```py\nresult = httpx.patch(\n \"https://app.mundi.ai/api/maps/MWfqcRak59bo\",\n json={\"basemap\": \"hybrid\"},\n headers={\"Authorization\": f\"Bearer {os.environ['MUNDI_API_KEY']}\"}\n).json()\n\nassert result == {\n \"id\": \"MWfqcRak59bo\",\n \"basemap\": \"hybrid\",\n \"message\": \"Map updated successfully\"\n}\n```", "operationId": "update_map", "parameters": [{"name": "map_id", "in": "path", "required": true, "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/MapUpdateRequest"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/api/layers/{layer_id}/style": {"post": {"tags": ["Layers"], "summary": "Set layer style", "description": "Sets a layer's active style in the map to a MapLibre JSON layer list.\n\nThis operation will fail if the style is invalid according to the\n[style spec](https://maplibre.org/maplibre-style-spec/layers/) and the source\ndefinition.\n\nReturns the created style_id and confirmation that it has been applied.", "operationId": "set_layer_style", "parameters": [{"name": "layer_id", "in": "path", "required": true, "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/SetStyleRequest"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/SetStyleResponse"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/api/layer/{layer_id}": {"patch": {"tags": ["Layers"], "summary": "Update layer", "description": "Updates properties of an existing layer. Currently supports updating\nthe layer's display name.\n\n```py\nresult = httpx.patch(\n \"https://app.mundi.ai/api/layer/L4b2c3d4e5f6\",\n json={\"name\": \"New name in layer list\"},\n headers={\"Authorization\": f\"Bearer {os.environ['MUNDI_API_KEY']}\"}\n).json()\n\nassert result == {\n \"layer_id\": \"L4b2c3d4e5f6\",\n \"name\": \"New name in layer list\",\n \"message\": \"Layer updated successfully\"\n}\n```", "operationId": "update_layer", "parameters": [{"name": "layer_id", "in": "path", "required": true, "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/LayerUpdateRequest"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/LayerUpdateResponse"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/api/projects/{project_id}": {"delete": {"tags": ["Maps"], "summary": "Delete a map", "description": "Marks a map project as deleted (uses soft delete).", "operationId": "delete_project", "parameters": [{"name": "project_id", "in": "path", "required": true, "schema": {"type": "string"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}}, "components": {"schemas": {"Body_upload_layer_to_map": {"properties": {"file": {"type": "string", "format": "binary"}, "layer_name": {"type": "string"}, "add_layer_to_map": {"type": "boolean", "default": true}}, "type": "object", "required": ["file"], "title": "Body_upload_layer_to_map"}, "HTTPValidationError": {"properties": {"detail": {"items": {"$ref": "#/components/schemas/ValidationError"}, "type": "array"}}, "type": "object", "title": "HTTPValidationError"}, "LayerUpdateRequest": {"properties": {"name": {"type": "string", "description": "New name for the layer"}}, "type": "object", "required": ["name"], "title": "LayerUpdateRequest"}, "LayerUpdateResponse": {"properties": {"layer_id": {"type": "string", "description": "ID of the updated layer"}, "name": {"type": "string", "description": "New name of the layer"}}, "type": "object", "required": ["layer_id", "name"], "title": "LayerUpdateResponse"}, "LayerUploadResponse": {"properties": {"dag_child_map_id": {"type": "string", "description": "The ID of the new map created that contains the changes. Use this ID for further operations on the modified map."}, "dag_parent_map_id": {"type": "string", "description": "The ID of the original map which was copied to create the new map."}, "id": {"type": "string", "description": "Unique identifier for the newly uploaded layer"}, "name": {"type": "string", "description": "Display name of the layer as it appears in the map"}, "type": {"type": "string", "description": "Layer type (vector, raster, or point_cloud)"}, "url": {"type": "string", "description": "Direct URL to access the layer data (PMTiles for vector, COG for raster)"}, "message": {"type": "string", "description": "Status message confirming successful upload", "default": "Layer added successfully"}}, "type": "object", "required": ["dag_child_map_id", "dag_parent_map_id", "id", "name", "type", "url"], "title": "LayerUploadResponse"}, "MapCreateRequest": {"properties": {"title": {"type": "string", "description": "Display name for the new map", "default": "Untitled Map"}}, "type": "object", "title": "MapCreateRequest"}, "MapResponse": {"properties": {"id": {"type": "string", "description": "Unique identifier for the map"}, "project_id": {"type": "string", "description": "ID of the project containing this map. Projects can contain multiple related maps."}, "title": {"type": "string", "description": "Display name of the map"}, "created_on": {"type": "string", "description": "ISO timestamp when the map was created"}, "map_link": {"type": "string", "description": "URL to view the map project"}}, "type": "object", "required": ["id", "project_id", "title", "created_on", "map_link"], "title": "MapResponse"}, "MapUpdateRequest": {"properties": {"basemap": {"anyOf": [{"type": "string"}, {"type": "null"}], "description": "Basemap style name"}}, "type": "object", "title": "MapUpdateRequest"}, "SetStyleRequest": {"properties": {"maplibre_json_layers": {"items": {}, "type": "array", "description": "Array of MapLibre layer objects like fill, line, symbol [(style spec v8)](https://maplibre.org/maplibre-style-spec/)"}, "map_id": {"type": "string", "description": "Map ID where this new style will be applied"}}, "type": "object", "required": ["maplibre_json_layers", "map_id"], "title": "SetStyleRequest"}, "SetStyleResponse": {"properties": {"style_id": {"type": "string", "description": "ID of the created style"}, "layer_id": {"type": "string", "description": "ID of the layer the style was applied to"}}, "type": "object", "required": ["style_id", "layer_id"], "title": "SetStyleResponse"}, "ValidationError": {"properties": {"loc": {"items": {"anyOf": [{"type": "string"}, {"type": "integer"}]}, "type": "array", "title": "Location"}, "msg": {"type": "string", "title": "Message"}, "type": {"type": "string", "title": "Error Type"}}, "type": "object", "required": ["loc", "msg", "type"], "title": "ValidationError"}}}}