{"openapi":"3.1.0","info":{"title":"Map Tile Server API","version":"1.0.0","description":"Self-hosted map tile server serving PMTiles archives from Cloudflare R2.\n\nProvides vector and raster tiles for OpenStreetMap basemaps, Natural Earth reference data, Overture Maps overlays, and terrain elevation data.\n\n**Tile URL pattern:** `GET /api/v1/tiles/{layerId}/{z}/{x}/{y}.{ext}`\n\n**Quick start:** Use `/api/v1/layers` to discover available layers and their tile URLs.\n\n**Built-in styles:** Use `/api/v1/styles/light.json`, `/api/v1/styles/dark.json`, `/api/v1/styles/satellite.json`, `/api/v1/styles/hybrid.json` (satellite + streets), or `/api/v1/styles/topo.json` (USGS topographic, US) as MapLibre GL JS style URLs for instant map rendering.\n\nAll tile data is self-hosted on Cloudflare R2 — zero external API calls at query time. Tiles are cached at Cloudflare's global CDN edge.","contact":{"name":"Cooperative Computing Alliance","url":"https://coopalliance.org"}},"servers":[{"url":"https://maps.datasourceapi.com","description":"Production"}],"tags":[{"name":"Layers","description":"Layer catalog — discover available map layers and their metadata"},{"name":"Tiles","description":"Map tile serving — Z/X/Y tile endpoints for each layer"},{"name":"Styles","description":"Built-in MapLibre GL JS map styles"},{"name":"Search","description":"Unified POI search and direct OpenStreetMap geocoding"},{"name":"Meta","description":"API metadata, statistics, and health checks"}],"paths":{"/health":{"get":{"summary":"Health check","tags":["Meta"],"description":"Returns service status and layer counts by category.","responses":{"200":{"description":"Service is healthy","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"ok"},"service":{"type":"string","example":"cca-maps-api"},"layers":{"type":"integer","example":21}}}}}}}}},"/api/v1/search":{"get":{"summary":"Search landmarks, schools, churches, and nonprofits","tags":["Search"],"description":"Unified point-of-interest search powered by `cca-geocode`. Results may come from OpenStreetMap/Nominatim, CCA Schools, or CCA Exempt Organizations.\n\nThis public browser-facing endpoint calls `cca-geocode` through a Cloudflare service binding. The separate `/api/v1/geocode` endpoint remains the direct Nominatim source used by `cca-geocode`, preventing request recursion.","parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string","minLength":2,"maxLength":160},"description":"Free-form POI name or place query","examples":{"landmark":{"value":"Statue of Liberty"},"school":{"value":"Stanford University"},"church":{"value":"Grace Cathedral"}}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":20,"default":10}},{"name":"cc","in":"query","required":false,"schema":{"type":"string","pattern":"^[A-Za-z]{2}$"},"description":"Optional ISO 3166-1 alpha-2 country code"},{"name":"types","in":"query","required":false,"schema":{"type":"string"},"description":"Optional comma-separated result categories: landmark, school, university, church, nonprofit","example":"school,university"}],"responses":{"200":{"description":"Unified Fleet POI search response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnifiedPOISearchResponse"}}}},"400":{"description":"Invalid search parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnifiedPOISearchError"},"example":{"request_status":"error","error_code":"ERROR_INVALID_INPUT","message":"Query parameter ?q= must be between 2 and 160 characters","data":null}}}},"503":{"description":"Unified POI service binding unavailable"}}}},"/api/v1/geocode":{"get":{"summary":"Search OpenStreetMap places directly","tags":["Search"],"description":"Direct, cached Nominatim free-form search. This endpoint is preserved for the `cca-geocode` `MAPS` service binding and does not call the unified search service. Named landmarks such as \"Statue of Liberty\" and \"Paris Las Vegas\" are returned through this path. Each OSM-backed row preserves `osm_type` and `osm_id` so callers can build the stable identifier `osm:{osm_type}:{osm_id}` while the legacy Nominatim place ID remains available as `id`.","parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string","minLength":1},"example":"Statue of Liberty"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":10,"default":5}},{"name":"cc","in":"query","required":false,"schema":{"type":"string","pattern":"^[A-Za-z]{2}$"},"description":"Optional ISO 3166-1 alpha-2 country filter"}],"responses":{"200":{"description":"Direct Nominatim results in the maps API envelope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DirectGeocodeResponse"}}}},"400":{"description":"Invalid query parameters"},"502":{"description":"Nominatim unavailable or returned an invalid response"}}}},"/api/v1/geocode/reverse":{"get":{"summary":"Reverse geocode a coordinate with OpenStreetMap","tags":["Search"],"parameters":[{"name":"lat","in":"query","required":true,"schema":{"type":"number","minimum":-90,"maximum":90}},{"name":"lon","in":"query","required":true,"schema":{"type":"number","minimum":-180,"maximum":180}}],"responses":{"200":{"description":"Reverse-geocoded display name and address details"},"400":{"description":"Missing or invalid coordinate"},"502":{"description":"Nominatim unavailable"}}}},"/api/v1/layers":{"get":{"summary":"List all available map layers","tags":["Layers"],"description":"Returns the full layer catalog with metadata. Optionally filter by category.\n\nEach layer includes its tile URL template and TileJSON endpoint.","parameters":[{"name":"category","in":"query","required":false,"schema":{"type":"string","enum":["basemap","overlay","terrain","reference"]},"description":"Filter layers by category"}],"responses":{"200":{"description":"Layer catalog","content":{"application/json":{"schema":{"type":"object","properties":{"request_status":{"type":"string","example":"success"},"message":{"type":"string","example":"OK"},"data":{"type":"object","properties":{"count":{"type":"integer"},"layers":{"type":"array","items":{"$ref":"#/components/schemas/LayerSummary"}}}}}}}}}}}},"/api/v1/layers/{id}":{"get":{"summary":"Get layer details","tags":["Layers"],"description":"Returns full metadata for a specific layer including tile URL template, bounds, zoom range, vector layer definitions, and attribution.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Layer ID","example":"osm-planet"}],"responses":{"200":{"description":"Layer details"},"404":{"description":"Layer not found"}}}},"/api/v1/layers/{id}/tilejson.json":{"get":{"summary":"TileJSON metadata for a layer","tags":["Layers"],"description":"Returns a TileJSON 3.0.0 object for the specified layer.\n\nUse this URL directly as the `url` property in a MapLibre GL JS vector source:\n\n```javascript\nmap.addSource(\"mySource\", {\n  type: \"vector\",\n  url: \"https://maps.datasourceapi.com/api/v1/layers/osm-planet/tilejson.json\"\n});\n```","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Layer ID","example":"osm-planet"}],"responses":{"200":{"description":"TileJSON 3.0.0","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TileJSON"}}}},"404":{"description":"Layer not found"}}}},"/api/v1/tiles/{layerId}/{z}/{x}/{y}.{ext}":{"get":{"summary":"Serve a map tile","tags":["Tiles"],"description":"Serves a single map tile from a PMTiles archive stored in Cloudflare R2.\n\nTiles are addressed by zoom level (z), column (x), and row (y) in XYZ/slippy map convention.\n\n**Caching:** Tiles are cached at Cloudflare's CDN edge for 24 hours. First request hits R2 directly (~500ms), subsequent requests are served from edge cache (<10ms).\n\n**Empty tiles:** Returns HTTP 204 (No Content) for tiles with no data (e.g., ocean areas in a buildings layer).","parameters":[{"name":"layerId","in":"path","required":true,"schema":{"type":"string"},"description":"Layer ID","example":"osm-planet"},{"name":"z","in":"path","required":true,"schema":{"type":"integer","minimum":0,"maximum":22},"description":"Zoom level"},{"name":"x","in":"path","required":true,"schema":{"type":"integer","minimum":0},"description":"Tile column"},{"name":"y","in":"path","required":true,"schema":{"type":"integer","minimum":0},"description":"Tile row"},{"name":"ext","in":"path","required":true,"schema":{"type":"string","enum":["pbf","mvt","png","jpg","webp","avif"]},"description":"Tile format extension"}],"responses":{"200":{"description":"Tile data","content":{"application/x-protobuf":{"schema":{"type":"string","format":"binary"}},"image/png":{"schema":{"type":"string","format":"binary"}}}},"204":{"description":"No tile data for this coordinate (empty area)"},"400":{"description":"Invalid tile coordinates or format mismatch"},"404":{"description":"Layer or archive not found"}}}},"/api/v1/styles/light.json":{"get":{"summary":"Light map style (MapLibre GL JS)","tags":["Styles"],"description":"Returns a complete MapLibre GL JS style JSON for light-themed map rendering.\n\nUse directly as a style URL:\n\n```javascript\nconst map = new maplibregl.Map({\n  container: \"map\",\n  style: \"https://maps.datasourceapi.com/api/v1/styles/light.json\"\n});\n```","responses":{"200":{"description":"MapLibre GL JS style JSON"}}}},"/api/v1/styles/dark.json":{"get":{"summary":"Dark map style (MapLibre GL JS)","tags":["Styles"],"description":"Returns a complete MapLibre GL JS style JSON for dark-themed map rendering.","responses":{"200":{"description":"MapLibre GL JS style JSON"}}}},"/api/v1/styles/satellite.json":{"get":{"summary":"Satellite imagery style (MapLibre GL JS)","tags":["Styles"],"description":"Returns a complete MapLibre GL JS style JSON for satellite imagery rendering.\n\nRaster tiles are processed locally, stored as PMTiles in R2, and served entirely from this domain — no external imagery services are called at runtime. The imagery stack layers global NASA Blue Marble (z0–8) with high-resolution USGS orthoimagery across the United States: CONUS (z9–15, staged nationwide rollout), Alaska (z9–13), Hawaii (z9–15), and Puerto Rico & USVI (z9–15). Includes the Overture addresses overlay restyled for readability on imagery.","responses":{"200":{"description":"MapLibre GL JS style JSON"}}}},"/api/v1/styles/hybrid.json":{"get":{"summary":"Hybrid style — satellite + streets (MapLibre GL JS)","tags":["Styles"],"description":"Returns a complete MapLibre GL JS style JSON combining the satellite imagery base with the OSM street network, boundaries, and place/road labels overlaid on top (restyled for readability on imagery).\n\nAll raster and vector tiles are served from this domain — no external tile services are called at runtime.","responses":{"200":{"description":"MapLibre GL JS style JSON"}}}},"/api/v1/styles/topo.json":{"get":{"summary":"USGS Topo style (MapLibre GL JS)","tags":["Styles"],"description":"Returns a complete MapLibre GL JS style JSON for the classic USGS topographic basemap (contour lines, hydrography, place names). United States coverage only (CONUS, Alaska, Hawaii, Puerto Rico); a neutral background shows elsewhere.\n\nTiles are processed locally from USGS The National Map (public domain) and served from this domain — no external tile services are called at runtime.","responses":{"200":{"description":"MapLibre GL JS style JSON"}}}},"/api/v1/stats":{"get":{"summary":"Dataset statistics","tags":["Meta"],"description":"Returns statistics about the tile server: layer counts by category, supported tile formats, data sources, and infrastructure details.","responses":{"200":{"description":"Statistics"}}}}},"components":{"schemas":{"LayerSummary":{"type":"object","properties":{"id":{"type":"string","example":"osm-planet"},"name":{"type":"string","example":"OpenStreetMap Planet"},"description":{"type":"string"},"category":{"type":"string","enum":["basemap","overlay","terrain","reference"]},"tileType":{"type":"string","enum":["mvt","png","jpg","webp","avif"]},"minZoom":{"type":"integer"},"maxZoom":{"type":"integer"},"fileSize":{"type":"string","example":"~120 GB"},"lastUpdated":{"type":"string","format":"date"},"tags":{"type":"array","items":{"type":"string"}},"tiles_url":{"type":"string","format":"uri"},"tilejson_url":{"type":"string","format":"uri"}}},"TileJSON":{"type":"object","properties":{"tilejson":{"type":"string","example":"3.0.0"},"name":{"type":"string"},"description":{"type":"string"},"version":{"type":"string"},"attribution":{"type":"string"},"scheme":{"type":"string","example":"xyz"},"tiles":{"type":"array","items":{"type":"string","format":"uri"}},"minzoom":{"type":"integer"},"maxzoom":{"type":"integer"},"bounds":{"type":"array","items":{"type":"number"},"minItems":4,"maxItems":4},"center":{"type":"array","items":{"type":"number"},"minItems":3,"maxItems":3},"vector_layers":{"type":"array","items":{"$ref":"#/components/schemas/VectorLayer"}}}},"VectorLayer":{"type":"object","properties":{"id":{"type":"string"},"description":{"type":"string"},"minzoom":{"type":"integer"},"maxzoom":{"type":"integer"},"fields":{"type":"object","additionalProperties":{"type":"string"}}}},"UnifiedPOISearchResponse":{"type":"object","required":["request_status","message","data","meta"],"properties":{"request_status":{"type":"string","example":"success"},"message":{"type":"string","example":"OK"},"data":{"type":"object","required":["query","result_count","results"],"properties":{"query":{"type":"object","required":["q","normalized_query","cc","types","limit"],"properties":{"q":{"type":"string"},"normalized_query":{"type":"string"},"cc":{"type":["string","null"]},"types":{"type":"array","items":{"type":"string","enum":["landmark","school","university","church","nonprofit"]}},"limit":{"type":"integer"}}},"result_count":{"type":"integer"},"results":{"type":"array","items":{"$ref":"#/components/schemas/UnifiedPOIResult"}}}},"meta":{"$ref":"#/components/schemas/UnifiedPOISearchMeta"}}},"UnifiedPOISearchError":{"type":"object","required":["request_status","error_code","message","data"],"properties":{"request_status":{"type":"string","const":"error"},"error_code":{"type":"string","const":"ERROR_INVALID_INPUT"},"message":{"type":"string"},"data":{"type":"null"}}},"UnifiedPOIResult":{"type":"object","required":["poi_id","rank","name","label","result_kind","category","subtype","address","lat","lon","bbox","source","provider","score","confidence","distance_km","detail_url","provenance"],"properties":{"poi_id":{"type":"string"},"rank":{"type":"integer"},"name":{"type":"string"},"label":{"type":"string"},"result_kind":{"type":"string","const":"poi"},"category":{"type":"string","enum":["landmark","school","university","church","nonprofit"]},"subtype":{"type":["string","null"]},"address":{"type":"object","required":["address1","address2","locality","region","postal","country_code","formatted"],"properties":{"address1":{"type":["string","null"]},"address2":{"type":["string","null"]},"locality":{"type":["string","null"]},"region":{"type":["string","null"]},"postal":{"type":["string","null"]},"country_code":{"type":["string","null"]},"formatted":{"type":["string","null"]}}},"lat":{"type":"number"},"lon":{"type":"number"},"bbox":{"oneOf":[{"type":"array","items":{"type":"number"},"minItems":4,"maxItems":4,"description":"[west, south, east, north]"},{"type":"null"}]},"source":{"type":"string","enum":["osm-nominatim","cca-schools","cca-exemptorgs"]},"provider":{"type":"string"},"score":{"type":"number","minimum":0,"maximum":1},"confidence":{"type":"string","enum":["low","medium","high"]},"distance_km":{"type":["number","null"]},"detail_url":{"type":["string","null"],"format":"uri"},"provenance":{"type":"object","additionalProperties":true}}},"UnifiedPOISearchMeta":{"type":"object","required":["sources","partial","upstreams","attribution","response_time_ms"],"properties":{"sources":{"type":"array","items":{"type":"string"}},"partial":{"type":"boolean"},"upstreams":{"type":"object","properties":{"maps":{"$ref":"#/components/schemas/POIUpstreamStatus"},"schools":{"$ref":"#/components/schemas/POIUpstreamStatus"},"exemptorgs":{"$ref":"#/components/schemas/POIUpstreamStatus"}}},"attribution":{"type":"array","items":{"type":"string"}},"response_time_ms":{"type":"number"}}},"POIUpstreamStatus":{"type":"object","required":["status","result_count"],"properties":{"status":{"type":"string","enum":["ok","error","timeout","unavailable","skipped"],"description":"`skipped` means the requested POI type did not require that upstream."},"result_count":{"type":"integer"}}},"DirectGeocodeResponse":{"type":"object","properties":{"request_status":{"type":"string","example":"success"},"message":{"type":"string","example":"OK"},"data":{"type":"object","properties":{"query":{"type":"string"},"count":{"type":"integer"},"attribution":{"type":"string","example":"OpenStreetMap contributors (ODbL)"},"results":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","example":"nominatim-123"},"osm_type":{"type":"string","enum":["node","way","relation"],"description":"Stable OpenStreetMap object type"},"osm_id":{"type":"integer","description":"Stable OpenStreetMap object ID"},"name":{"type":"string"},"lat":{"type":"number"},"lon":{"type":"number"},"bbox":{"type":"array","items":{"type":"number"},"minItems":4,"maxItems":4},"type":{"type":"string"},"class":{"type":"string"},"importance":{"type":"number"},"address":{"type":"object","additionalProperties":{"type":"string"}}}}}}}}}}}}