TrailSplits API

Free, no-key HTTP API for outdoor maps and routing: global elevation, vector basemap tiles, contour and hiking-network overlays, outdoor POIs, place search, OpenStreetMap trail relations, snow forecasts, and trail-aware routing. These are the exact endpoints behind the TrailSplits Route Planner.

Overview

The TrailSplits API provides free access to global elevation data, vector tiles, place search, routing, snow forecasts, trail relations, and outdoor POIs. Everything is derived from open data — the Copernicus 30 m DEM for elevation and OpenStreetMap planet extracts for everything else — and served from a single origin with CDN caching and CORS enabled.

Usage policy: free for hobby, research, and non-commercial use. Please attribute TrailSplits and © OpenStreetMap contributors. For commercial use or higher quotas, reach out.

Developer preview — this API surface is evolving. Endpoints and schemas may change. Pin a release name if you need reproducibility.

Quick start

Three copy-paste calls to confirm connectivity and see the shape of the data:

# 1. Health check (plain-text "ok")
curl -sS https://api.trailsplits.com/healthz

# 2. List available dataset releases
curl -sS https://api.trailsplits.com/tiles/v1/catalog

# 3. Sample elevation (meters) for two points — returns JSON
curl -sS "https://api.trailsplits.com/tiles/v1/elevation/current/sample?points=46.5369,12.1352|46.5193,12.0106"

# 4. Fetch one TerrainRGB elevation tile (z9 over the Dolomites)
curl -sS -o terrain.png "https://api.trailsplits.com/tiles/v1/terrainrgb/current/9/271/186.png"

Base URL & releases

All endpoints are served from https://api.trailsplits.com over HTTPS, fronted by the Cloudflare CDN. Tile and style responses are edge-cached; dynamic JSON endpoints are computed per request.

Tile datasets are versioned by an immutable release name. Use current as a mutable alias that always points to the latest active release, or pin an explicit release for reproducibility.

Authentication, CORS & rate limits

  • No API key. Every endpoint is public — no token or sign-up required.
  • CORS enabled. Responses include permissive CORS headers, so you can call the API directly from browser JavaScript (the interactive demo below does exactly that).
  • Be a good neighbour. There is no hard quota, but please cache tiles client-side, keep bounding boxes and point lists reasonable, and avoid hammering dynamic JSON endpoints in tight loops.

Map style

Style JSON and assets for MapLibre GL clients. The planner fetches the style object on load and renders the basemap from the bundled PMTiles source.

MethodEndpoint
GET/styles/<release>/trailsplits_openmaptiles_style.json
GET/styles/<release>/glyphs/{fontstack}/{range}.pbf
GET/styles/<release>/sprites/trailsplits.json
GET/styles/<release>/sprites/trailsplits.png
curl -sS "https://api.trailsplits.com/styles/current/trailsplits_openmaptiles_style.json" | head -c 300

Vector basemap tiles (PMTiles)

A single PMTiles archive with global vector tiles in the OpenMapTiles schema. PMTiles uses HTTP Range requests, so clients fetch only the bytes they need — no per-tile server round-trips.

MethodEndpoint
GET/pmtiles/<release>/tiles/openmaptiles_planet.pmtiles
GET/pmtiles/current/tiles/openmaptiles_planet.pmtiles (alias)

Use it directly in MapLibre GL with the PMTiles protocol:

pmtiles://https://api.trailsplits.com/pmtiles/current/tiles/openmaptiles_planet.pmtiles

Validate that Range requests work (expect HTTP/2 206 Partial Content):

curl -sS -o /dev/null -D - -r 0-1023 \
  "https://api.trailsplits.com/pmtiles/current/tiles/openmaptiles_planet.pmtiles"

TerrainRGB elevation tiles

Global elevation encoded as Mapbox TerrainRGB PNG tiles. The planner uses these for hillshade rendering and 3D terrain exaggeration. Tiles are built to zoom 12; request higher zooms and the server overzooms from the parent tile.

MethodEndpointFormat
GET/tiles/v1/terrainrgb/<release>/{z}/{x}/{y}.pngimage/png
GET/tiles/v1/terrainrgb/<release>/metadata.jsonJSON
curl -sS -I "https://api.trailsplits.com/tiles/v1/terrainrgb/current/9/271/186.png"
curl -sS "https://api.trailsplits.com/tiles/v1/terrainrgb/current/metadata.json"

Minimal MapLibre style for hillshade + 3D terrain:

{
  "version": 8,
  "sources": {
    "dem": {
      "type": "raster-dem",
      "tiles": ["https://api.trailsplits.com/tiles/v1/terrainrgb/current/{z}/{x}/{y}.png"],
      "tileSize": 256,
      "encoding": "mapbox",
      "maxzoom": 12
    }
  },
  "terrain": { "source": "dem", "exaggeration": 1.2 },
  "layers": [
    { "id": "hillshade", "type": "hillshade", "source": "dem" }
  ]
}

Elevation sampling

Sample elevation (meters) for a list of coordinates from the same TerrainRGB data — no need to decode tiles yourself. The planner uses this to build elevation profiles and compute ascent/descent.

MethodEndpoint
GET/tiles/v1/elevation/<release>/sample

Query parameters:

  • points (required) — lat,lon|lat,lon|... (up to 2048 points)
  • z (optional, default 13) — sampling zoom; clamped to the dataset max zoom (12)
  • encoding (optional, default mapbox) — mapbox or terrarium
curl -sS "https://api.trailsplits.com/tiles/v1/elevation/current/sample?encoding=mapbox&points=46.5369,12.1352|46.5193,12.0106"

Response:

{
  "release": "current",
  "z": 12,
  "z_requested": 13,
  "encoding": "mapbox",
  "points": [
    { "lat": 46.5369, "lon": 12.1352, "elevation_m": 1193.3 },
    { "lat": 46.5193, "lon": 12.0106, "elevation_m": 2098.8 }
  ]
}

Try it

Calls api.trailsplits.com directly from your browser (CORS enabled).

(click Fetch)

Contour tiles

Vector contour lines (Mapbox Vector Tiles / Protobuf), generated from the Copernicus DEM. The planner renders these as styled line layers with labels at major (100 m) and minor intervals.

MethodEndpointFormat
GET/tiles/v1/contours/<release>/{z}/{x}/{y}.pbfapplication/x-protobuf
GET/tiles/v1/contours/<release>/metadata.jsonJSON
curl -sS "https://api.trailsplits.com/tiles/v1/contours/current/metadata.json"
curl -sS -I "https://api.trailsplits.com/tiles/v1/contours/current/13/4412/3076.pbf"

Hiking network tiles

Vector overlay of hiking and cycling trail networks extracted from OpenStreetMap route relations. Feature properties include the network tier (iwn/nwn/rwn/lwn), trail name, ref, operator, osmc_symbol, and surface tags where available.

MethodEndpointFormat
GET/tiles/v1/hiking-network/<release>/{z}/{x}/{y}.pbfapplication/x-protobuf
GET/tiles/v1/hiking-network/<release>/metadata.jsonJSON
curl -sS "https://api.trailsplits.com/tiles/v1/hiking-network/current/metadata.json"
curl -sS -I "https://api.trailsplits.com/tiles/v1/hiking-network/current/12/2206/1538.pbf"

Outdoor POIs

Points of interest for the outdoors — water sources, huts and shelters, parking, viewpoints, mountain passes, transit stops, and more — extracted from OpenStreetMap. Served as a vector tile overlay that the planner renders directly.

MethodEndpointFormat
GET/tiles/v1/outdoor-pois/<release>/{z}/{x}/{y}.pbfapplication/x-protobuf
GET/tiles/v1/outdoor-pois/<release>/metadata.jsonJSON
curl -sS "https://api.trailsplits.com/tiles/v1/outdoor-pois/current/metadata.json"
curl -sS -I "https://api.trailsplits.com/tiles/v1/outdoor-pois/current/12/2206/1538.pbf"

Bounding-box query (JSON)

Query POIs inside a rectangle and get JSON back — handy for server-side lookups when you don't want to decode vector tiles. Returns a GeoJSON-style FeatureCollection with kind, name, ele (metres, nullable), and lat/lon.

MethodEndpoint
GET/pois/v1/bbox

Query parameters (note: min_lon/max_lon, not lng):

  • min_lat, min_lon, max_lat, max_lon (required) — the bounding box
  • kind (optional) — comma-separated filter, e.g. water,hut,parking,shelter,viewpoint
  • limit (optional) — max features to return
curl -sS "https://api.trailsplits.com/pois/v1/bbox?min_lat=46.5&max_lat=46.55&min_lon=12.0&max_lon=12.05&kind=water,hut&limit=3"

Response:

{
  "type": "FeatureCollection",
  "count": 3,
  "features": [
    { "kind": "water", "name": null, "ele": null, "lat": 46.527856, "lon": 12.001694 },
    { "kind": "water", "name": null, "ele": null, "lat": 46.522098, "lon": 12.009054 },
    { "kind": "hut",   "name": "Rifugio", "ele": 2010, "lat": 46.523744, "lon": 12.037963 }
  ]
}

Satellite imagery

Aerial/satellite raster tiles (Esri World Imagery), fetched and cached at the origin. Useful as an alternate basemap or for verifying terrain against the vector map.

MethodEndpointFormat
GET/tiles/v1/satellite/arcgis/world-imagery/{z}/{x}/{y}.jpgimage/jpeg
curl -sS -o sat.jpg "https://api.trailsplits.com/tiles/v1/satellite/arcgis/world-imagery/12/2206/1538.jpg"

Dataset catalog

Discover the available releases for every dataset (TerrainRGB, contours, snow, hiking-network, outdoor POIs, and more). Use a listed release name to pin a dataset, or stick with current.

MethodEndpoint
GET/tiles/v1/catalog
GET/healthz — liveness check (plain-text ok)
curl -sS "https://api.trailsplits.com/tiles/v1/catalog"

Full-text place search and reverse geocoding over an OpenStreetMap-derived gazetteer (towns, villages, peaks, lakes, huts, stations, and other named features). This powers the search box in the planner.

MethodEndpointDescription
GET/search/v1/forward?q=<query>Forward search by name (prefix matching)
GET/search/v1/reverse?lat=&lon=Nearest named features to a point
GET/search/v1/healthzHealth check with index stats

Parameters (forward):

  • q (required) — search text
  • limit (optional, default 10)
  • bbox (optional) — minLon,minLat,maxLon,maxLat to restrict results
  • bias_bbox (optional) — soft spatial boost toward a box

Parameters (reverse): lat, lon (required), limit (optional).

# Forward search
curl -sS "https://api.trailsplits.com/search/v1/forward?q=Zermatt&limit=2"

# Reverse geocode (nearest named features)
curl -sS "https://api.trailsplits.com/search/v1/reverse?lat=46.0207&lon=7.7491&limit=2"

Forward response (center is [lon, lat]):

{
  "query": "Zermatt",
  "features": [
    { "id": 74378, "name": "Zermatt", "kind": "town", "center": [7.749254, 46.0212076] },
    { "id": 4290771, "name": "Zermatten", "kind": "locality", "center": [7.914665, 46.1865914] }
  ]
}

Trail relations

Named hiking and cycling trail relations from OpenStreetMap. Search returns lightweight results (with a representative point); fetch a single relation by OSM id to get the full geometry plus distance, ascent, network tier, and bounding box.

MethodEndpointDescription
GET/trails/v1/search?q=<query>Full-text search by name or ref
GET/trails/v1/relation/<osm_id>Single relation with full geometry
GET/trails/v1/bbox?min_lat=&min_lng=&max_lat=&max_lng=Trails intersecting a bounding box
GET/trails/v1/healthzHealth check with relation counts

Parameters (search): q (required), limit (default 20, max 100), type (hiking | bicycle).

Parameters (bbox): min_lat, min_lng, max_lat, max_lng (note: lng, not lon), plus optional limit and type.

# Search for a trail by name
curl -sS "https://api.trailsplits.com/trails/v1/search?q=Tour+du+Mont+Blanc&limit=1"

# Full geometry for one relation
curl -sS "https://api.trailsplits.com/trails/v1/relation/9678362" | head -c 400

# Trails intersecting a bounding box (note min_lng/max_lng)
curl -sS "https://api.trailsplits.com/trails/v1/bbox?min_lat=45.7&min_lng=6.7&max_lat=46.1&max_lng=7.1&type=hiking&limit=10"

Search response (GeoJSON; full geometry only via /relation/<id>):

{
  "type": "FeatureCollection",
  "query": "Tour du Mont Blanc",
  "features": [
    {
      "type": "Feature",
      "properties": {
        "osm_relation_id": 9678362,
        "route_type": "hiking",
        "name": "Tour du Mont Blanc - Itinéraire principal",
        "ref": "TMB-IP", "network": "rwn", "tier": 2,
        "distance_km": 166.05,
        "bbox": [6.706605, 45.696487, 7.12832, 46.058471]
      },
      "geometry": { "type": "Point", "coordinates": [6.917463, 45.877479] }
    }
  ]
}

Snow forecast

Snow-coverage tiles and route-level snow summaries, fusing satellite classification with weather-model data. Coverage is best at higher elevations during winter; outside snow conditions the summary reports unavailable rather than guessing.

Snow tiles

MethodEndpointFormat
GET/tiles/v1/snow/<release>/{z}/{x}/{y}.pngimage/png
GET/tiles/v1/snow/<release>/metadata.jsonJSON
curl -sS "https://api.trailsplits.com/tiles/v1/snow/current/metadata.json"

Route forecast

Compute a snow summary for a list of sampled route points.

MethodEndpoint
GET/tiles/v1/snow/route-forecast

Parameters: points (required, lat,lon|..., max 512), release (default current), z (default 13), encoding (default mapbox).

curl -sS "https://api.trailsplits.com/tiles/v1/snow/route-forecast?points=46.5369,12.1352|46.5193,12.0106"

Routing (Valhalla)

Trail-aware route calculation powered by Valhalla. The planner calls a SvelteKit server proxy at /api/route (which adds provider fallback and trail tuning); external clients can call the Valhalla origin directly.

MethodEndpoint
POST/route/v1

Supports Valhalla costings pedestrian, bicycle, and auto.

# Walking route in the Dolomites
curl -sS -X POST "https://api.trailsplits.com/route/v1" \
  -H "Content-Type: application/json" \
  --data-raw '{
    "locations": [
      {"lat": 46.5369, "lon": 12.1352},
      {"lat": 46.5193, "lon": 12.0106}
    ],
    "costing": "pedestrian",
    "directions_options": {"units": "kilometers", "language": "en", "format": "osrm"}
  }' | head -c 600; echo

Static map rendering

Server-rendered PNG map images over the TrailSplits basemap. Used for social/Open Graph cards and SEO previews; handy for any "snapshot of this area/route" use case.

MethodEndpoint
POST/static-map/render

Parameters (JSON body):

  • width, height — output size in pixels
  • bbox (optional) — [minLon, minLat, maxLon, maxLat]
  • center (optional) — [lon, lat] with zoom
  • pixelRatio (optional) — 1 or 2
  • geojson (optional) — a LineString or FeatureCollection to overlay
curl -sS -X POST "https://api.trailsplits.com/static-map/render" \
  -H "Content-Type: application/json" \
  --data-raw '{
    "width": 1200,
    "height": 630,
    "bbox": [11.9, 46.5, 12.2, 46.6],
    "pixelRatio": 2
  }' -o dolomites.png

FAQ

Is the TrailSplits API free to use?

Yes. All endpoints are free for hobby, research, and non-commercial projects. Please attribute TrailSplits and OpenStreetMap. For commercial use or higher quotas, get in touch.

Do I need an API key or authentication?

No. Every endpoint is public and requires no API key or token. Requests are served over HTTPS with permissive CORS headers, so you can call them directly from the browser.

Which data sources power the API?

Elevation comes from the Copernicus 30 m DEM, encoded as Mapbox TerrainRGB tiles. Vector tiles, trails, POIs, and search are derived from OpenStreetMap planet data. Routing is powered by self-hosted Valhalla.

How are datasets versioned?

Tile datasets are versioned by an immutable release name. Use the mutable alias "current" to always read the latest active release, or pin a specific release for reproducibility.

Are responses cached?

Tile and style responses are cached at the edge via Cloudflare CDN. Dynamic JSON endpoints (search, elevation sampling, routing, snow forecasts) are computed per request — keep payloads reasonable and cache on your side where possible.

Build with TrailSplits

These endpoints power the free route planner and our browser-based trail tools (GPX simplifier, elevation profiler, GAP calculator, and more). If you build something with the API, we'd love to hear about it — please attribute TrailSplits and © OpenStreetMap contributors.