The TrailSplits Open API provides free access to global elevation data (TerrainRGB), vector tiles (PMTiles), and snow forecasts. We built this to power our Route Planner, and we’re opening it to the community to support other GIS and mapping projects.
Usage policy: Free for hobby projects and non-commercial use. Please attribute TrailSplits. If you need commercial use or higher quotas, reach out.
Copy/paste examples to verify connectivity and get a feel for the API.
curl -sS https://api.trailsplits.com/healthz curl -sS -I "https://api.trailsplits.com/tiles/v1/terrainrgb/current/metadata.json"
curl -sS -o /tmp/terrainrgb-z0.png "https://api.trailsplits.com/tiles/v1/terrainrgb/current/0/0/0.png" curl -sS -o /dev/null -D - -r 0-1023 \
"https://api.trailsplits.com/pmtiles/current/tiles/openmaptiles_planet.pmtiles" | head {
"version": 8,
"sources": {
"dem": {
"type": "raster-dem",
"tiles": ["https://api.trailsplits.com/tiles/v1/terrainrgb/current/{z}/{x}/{y}.png"],
"tileSize": 256,
"encoding": "mapbox"
}
},
"terrain": { "source": "dem", "exaggeration": 1.2 },
"layers": [
{ "id": "bg", "type": "background", "paint": { "background-color": "#0b1020" } },
{ "id": "hillshade", "type": "hillshade", "source": "dem" }
]
}
This page is documentation only. The APIs themselves are served from https://api.trailsplits.com/
(Cloudflare in front for caching/CDN).
/tiles/v1/... (TerrainRGB, terrain-intel, contours, hiking-network overlay, outdoor POIs overlay, snow, elevation sampling)/styles/<release>/... (style JSON + glyphs + sprites)/search/v1/... (Switzerland-first)/route/v1 (Valhalla; POST)/static-map/render (server-rendered PNG images)/pmtiles/<release>/... (recommended) and /tiles/v1/vector/... (legacy)
High-volume tiles are served from https://api.trailsplits.com as standard z/x/y URLs.
These endpoints are designed to sit behind CDN caching (Cloudflare) without a Worker in the hot path.
Endpoints:
/tiles/v1/terrainrgb/<release>/{z}/{x}/{y}.png/tiles/v1/terrainrgb/current/{z}/{x}/{y}.png (alias for active release)/tiles/v1/terrainrgb/<release>/metadata.json/tiles/v1/elevation/<release>/sample (elevation sampling; query-driven)/tiles/v1/terrain-intel/<kind>/<release>/{z}/{x}/{y}.png (slope/aspect/hillshade overlays)/tiles/v1/terrain-intel/<kind>/<release>/metadata.json/tiles/v1/contours/<release>/{z}/{x}/{y}.pbf (vector contours)/tiles/v1/contours/<release>/metadata.json/tiles/v1/hiking-network/<release>/{z}/{x}/{y}.pbf (hiking network overlay; vector tiles)/tiles/v1/hiking-network/<release>/metadata.json/tiles/v1/outdoor-pois/<release>/{z}/{x}/{y}.pbf (outdoor POIs overlay; vector tiles)/tiles/v1/outdoor-pois/<release>/metadata.json/tiles/v1/snow/<release>/{z}/{x}/{y}.png (snow overlay)/tiles/v1/snow/<release>/metadata.json/tiles/v1/snow/route-forecast?release=<release>&z=13&encoding=mapbox&points=lat,lon|.../tiles/v1/snow/report (optional, rate-limited; may require a token)/tiles/v1/snow/report-route (optional, rate-limited; may require a token)/tiles/v1/catalog/healthzNotes:
<release> is a release name (immutable), or current.
<kind> is one of slope, aspect, hillshade, ruggedness.
image/png; vector contours are returned as application/x-protobuf. CORS headers are included so browser clients can fetch them.
Quick validation:
curl.exe -s -S "https://api.trailsplits.com/healthz"
curl.exe -s -S "https://api.trailsplits.com/tiles/v1/catalog"
Note: /tiles/v1/catalog lists available releases per dataset (including snow when published).
Fetch a single tile (example z=14):
curl.exe -s -S -I "https://api.trailsplits.com/tiles/v1/terrainrgb/current/14/8622/5836.png" Fetch a terrain-intel overlay tile (example z=14):
curl.exe -s -S -I "https://api.trailsplits.com/tiles/v1/terrain-intel/slope/current/metadata.json"
curl.exe -s -S -I "https://api.trailsplits.com/tiles/v1/terrain-intel/slope/current/12/2167/2668.png" Fetch a ruggedness overlay tile (example z=14):
curl.exe -s -S -I "https://api.trailsplits.com/tiles/v1/terrain-intel/ruggedness/current/metadata.json"
curl.exe -s -S -I "https://api.trailsplits.com/tiles/v1/terrain-intel/ruggedness/current/14/8622/5836.png" Fetch a hiking-network overlay tile (vector, PBF):
curl -sS -I "https://api.trailsplits.com/tiles/v1/hiking-network/current/metadata.json"
curl -sS -I "https://api.trailsplits.com/tiles/v1/hiking-network/current/12/2145/1435.pbf" Fetch an outdoor-pois overlay tile (vector, PBF):
curl -sS -I "https://api.trailsplits.com/tiles/v1/outdoor-pois/current/metadata.json"
curl -sS -I "https://api.trailsplits.com/tiles/v1/outdoor-pois/current/12/2145/1434.pbf" Flutter web configuration:
TERRAIN_DEM_TILE_URL_TEMPLATE=https://api.trailsplits.com/tiles/v1/terrainrgb/current/{z}/{x}/{y}.pngTERRAIN_DEM_ENCODING=mapboxSNOW_TILE_URL_TEMPLATE=https://api.trailsplits.com/tiles/v1/snow/current/{z}/{x}/{y}.png
Snow overlay tiles are served as PNG rasters. If current hasn’t been published yet, these may return 404.
GET /tiles/v1/snow/<release>/metadata.json
curl -sS "https://api.trailsplits.com/tiles/v1/snow/current/metadata.json" GET /tiles/v1/snow/<release>/{z}/{x}/{y}.png
Tip: use the catalog to find a published release, then fetch a tile at your target z/x/y.
Sample elevations (meters) for one or more lat,lon points using the same TerrainRGB data served by
/tiles/v1/terrainrgb/.... This is intended for route elevation charts/stats.
GET /tiles/v1/elevation/<release>/sample (host: https://api.trailsplits.com)
Query params:
points (required): lat,lon|lat,lon|...z (optional, default 13)encoding (optional, default mapbox): mapbox or terrariumExample (macOS/Linux):
curl -sS "https://api.trailsplits.com/tiles/v1/elevation/current/sample?z=13&encoding=mapbox&points=46.948090,7.447440|46.950000,7.450000" Example (Windows, curl.exe):
curl.exe -s -S "https://api.trailsplits.com/tiles/v1/elevation/current/sample?z=13&encoding=mapbox&points=46.948090,7.447440|46.950000,7.450000" Try it out:
This calls https://api.trailsplits.com directly from your browser.
(click Fetch) Compute a route-level snow summary for a list of sampled points. This is the same underlying capability used by snow evaluation scripts.
GET /tiles/v1/snow/route-forecast
Query params:
release (optional, default current)points (required): lat,lon|lat,lon|... (max 512)z (optional, default 13)encoding (optional, default mapbox)Example:
curl -sS "https://api.trailsplits.com/tiles/v1/snow/route-forecast?release=current&z=13&encoding=mapbox&points=46.8210,8.4017|46.8185,8.4190|46.8160,8.4365" | head -c 800; echo
Response is JSON and includes observed, fused, and a coarse runner feasibility summary.
This endpoint is intentionally Cache-Control: no-store.
These endpoints are intended for evaluation/QA. They may be disabled or protected with a shared secret, and they are rate-limited.
/tiles/v1/snow/report — point report ({ lat, lon, obs, release?, z?, encoding?, id? })/tiles/v1/snow/report-route — route report ({ points: [{lat,lon}], obs, release?, z?, encoding?, route_id? })Search endpoints intended for place/POI discovery in Switzerland (forward search + reverse lookup). Status: In development (coverage and ranking will improve over time).
Responses include a features array with objects like:
{ id, name, kind, center: [lon, lat] }.
Endpoints:
/search/v1/forward?q=<query> (optional: limit, bbox)/search/v1/reverse?lat=<lat>&lon=<lon> (optional: limit, radius_m)/search/v1/healthzNotes:
bbox format: minLon,minLat,maxLon,maxLat (WGS84)Examples:
curl -sS "https://api.trailsplits.com/search/v1/forward?q=Z%C3%BCrich&limit=5"
curl -sS "https://api.trailsplits.com/search/v1/reverse?lat=47.3769&lon=8.5417&radius_m=2000&limit=5" Download a single PMTiles archive containing vector tiles (OpenMapTiles profile). PMTiles is optimized for HTTP Range requests (clients fetch only the bytes they need).
Endpoints:
/pmtiles/<release>/tiles/openmaptiles_planet.pmtiles (recommended)/pmtiles/current/tiles/openmaptiles_planet.pmtiles (recommended alias for active release)/tiles/v1/vector/<release>/openmaptiles_planet.pmtiles/tiles/v1/vector/current/openmaptiles_planet.pmtiles (alias for active release)Notes:
<release> is immutable; current is mutable (short TTL).Range headers; server must return 206 for partial reads.Quick validation (Range):
curl -sS -o /dev/null -D - -r 0-1023 \
"https://api.trailsplits.com/tiles/v1/vector/current/openmaptiles_planet.pmtiles" | head
MapLibre usage typically wraps the URL with the PMTiles protocol, for example:
pmtiles://https://api.trailsplits.com/pmtiles/current/tiles/openmaptiles_planet.pmtiles
Map presentation assets are hosted on https://api.trailsplits.com and are intended to be fetched directly by MapLibre clients.
Endpoints:
/styles/<release>/trailsplits_openmaptiles_style.json/styles/<release>/glyphs/{fontstack}/{range}.pbf/styles/<release>/sprites/trailsplits.json/styles/<release>/sprites/trailsplits.png/styles/current/... (mutable alias for active release)Examples:
curl -sS "https://api.trailsplits.com/styles/current/trailsplits_openmaptiles_style.json" | head -c 800; echo
curl -sS -I "https://api.trailsplits.com/styles/current/glyphs/Noto%20Sans%20Regular/0-255.pbf" | head
curl -sS "https://api.trailsplits.com/styles/current/sprites/trailsplits.json"
Note: the style file may reference PMTiles via /tiles/v1/vector/... for compatibility. PMTiles are also available at
/pmtiles/<release>/tiles/....
Native/mobile clients can call Valhalla directly on https://api.trailsplits.com. Web builds typically use the Worker proxy
(https://trailsplits.com/api/route) to avoid CORS/CSP and to keep provider keys off the client.
POST /route/v1
Example:
curl -sS -X POST "https://api.trailsplits.com/route/v1" \
-H "Content-Type: application/json" \
--data-raw "{\
\"locations\": [\
{\"lat\": 47.3769, \"lon\": 8.5417},\
{\"lat\": 47.3700, \"lon\": 8.5500}\
],\
\"costing\": \"pedestrian\",\
\"directions_options\": {\"units\": \"kilometers\", \"language\": \"en\", \"format\": \"osrm\"}\
}" | head -c 1200; echo
Response is Valhalla JSON with an OSRM-like routes payload (distance/duration + encoded polyline geometry).
This endpoint renders a single PNG map image backed by TrailSplits’ in-house vector basemap (PMTiles + MapLibre style). It exists primarily for OG cards and SEO pages, where the server needs a deterministic image.
POST /static-map/render (host: https://api.trailsplits.com)
Request body (JSON):
width, height (required): output image size in pixelsbbox (optional): [minLon, minLat, maxLon, maxLat] (WGS84); if omitted, use center + zoomcenter (optional): [lon, lat]zoom (optional): numeric zoompixelRatio (optional): device pixel ratio; typical values 1 or 2geojson (optional): a GeoJSON FeatureCollection to overlay (e.g., a route LineString)Example:
curl -sS -X POST "https://api.trailsplits.com/static-map/render" \
-H "Content-Type: application/json" \
--data-raw "{\
\"width\": 1200,\
\"height\": 630,\
\"bbox\": [8.50, 47.34, 8.62, 47.43],\
\"pixelRatio\": 2\
}" \
-o out.png
Note: this endpoint is served from the origin (not the Worker). The Worker may call it internally to generate /og/route/<id>/png.
The TrailSplits website is fronted by a Cloudflare Worker that provides a small set of browser-friendly endpoints for tools and routing proxying.
https://trailsplits.com/api/route (routing proxy)https://trailsplits.com/api/elevation (single-point elevation lookup; cached)https://trailsplits.com/api/elevation/profile (multi-point elevation sampling; cached; Terrarium-backed)https://trailsplits.com/terrain-intel/<kind>/{z}/{x}/{y}.png (derived overlays; kind: slope, aspect)Examples:
curl -sS "https://trailsplits.com/api/elevation?lat=47.6205&lon=-122.3493&z=14"
curl -sS -X POST "https://trailsplits.com/api/elevation/profile" \
-H "Content-Type: application/json" \
--data-raw "{\
\"z\": 14,\
\"points\": [\
{\"lat\": 47.6205, \"lon\": -122.3493},\
{\"lat\": 47.6210, \"lon\": -122.3500}\
]\
}"
curl -sS -I "https://trailsplits.com/terrain-intel/slope/12/2167/2668.png" | head
For elevation charts and map rendering, prefer https://api.trailsplits.com/tiles/v1/... where possible.