BUILD WITH TRAILSPLITS · DEVELOPER PREVIEW
TrailSplits API for outdoor maps and routes
Routes on signed trails, round trips from a start point, the weather along a route at the hour you get there, elevation profiles, GPX clean and simplify, place and trail search, outdoor places and maps, for apps about the outdoors. Try any call without a key.
Which call do I want?
| Where can I walk near here (for agents and chat) | POST /v1/outings |
| A route through points I pick | POST /v1/route |
| A loop of a given length from a start | POST /v1/roundtrips |
| Facts about a GPX I already have | POST /v1/route-analysis |
| The best day and start time this week | POST /v1/route-windows |
| Weather along it at the hour I get there | POST /v1/route-weather |
| Water, huts and stops along it | POST /v1/route-places |
| Where I can leave the trail | POST /v1/route-exits |
| How long it takes this person | POST /v1/time-model/fit, then pace_profile |
| All of that for one day, in one call | POST /v1/route-brief |
| An elevation chart, or race splits | POST /v1/elevation-profile, POST /v1/pace-plan |
| A card to print, or a PDF | POST /v1/route-card |
| Find a place or a trail by name | GET /search/v1/forward, GET /trails/v1/search |
| Show the outdoor map, with terrain | Style and tiles (no key, not counted) |
| I have code written for Valhalla or OSRM | POST /route/v1 |
Bodies and JSON answers use [lon, lat]; older query strings use lat,lon. Each reference page says which (coordinate order).
These capabilities also underpin the TrailSplits Planner. Its browser and server workflows can use different HTTP contracts and settings.
A useful first result
Get modelled elevation for two public points in the Dolomites. Coordinates here are latitude, longitude; heights are metres. No account, key or third-party geocoder is needed.
curl --fail-with-body --max-time 12 --max-filesize 1048576 -sS "https://api.trailsplits.com/tiles/v1/elevation/current/sample?points=46.5369,12.1352%7C46.5193,12.0106&z=12&encoding=mapbox" Verified on 1 October 2026: two finite samples, approximately 1182.8 m and 2101.2 m at z12. This is a dated check of a terrain model, not surveyed ground truth. Values may change with a new release.
Run the existing live elevation example, inspect map assets or request a short route. Requests below have time and transfer bounds.
Display an outdoor map
Use the served MapLibre style with the PMTiles basemap, then add terrain or trail overlays. The global OSM-derived map is useful across borders; mapped features and detail vary by place. See it in the Planner.
curl --fail-with-body --max-time 12 --max-filesize 1048576 -sS "https://api.trailsplits.com/styles/current/trailsplits_openmaptiles_style.json" -o style.json The style needs a MapLibre client and the PMTiles protocol. Read the artifact manifest to pin the basemap, style and glyphs for a session. A versioned style can still refer to mutable current dependencies; the site's existing release helpers resolve and rewrite those addresses.
Run the map and terrain example to inspect the pinned assets and download its client.
Map style, vector tiles and static image reference
Map style and assets
GET the style and its glyphs. The current style draws no icons, so it names no sprite (since 3 October 2026); older versioned styles still point at a 1×1 placeholder sprite.
| GET path | Response |
|---|---|
/styles/v/<release>/trailsplits_openmaptiles_style.json | MapLibre style JSON |
/styles/v/<release>/glyphs/{fontstack}/{range}.pbf | Noto Sans glyphs (Regular, Bold, Italic) |
/styles/current/… | Mutable alias |
Topographic outdoor style (topo-style/1)
A second style for hiking and running maps: relief, contours (thick every 100 m with heights, thin every 20 m), cliffs hatched on the drop side, ridges and arêtes, scree and glaciers, waymarked trails by difficulty, huts, water, passes and peaks. It uses only the sources above, credits each one, needs no sprite and only Noto Sans Regular and Bold. Its sources are pinned to release addresses, which caches well on phones, and it is re-pinned at every weekly release.
| GET path | Response |
|---|---|
/styles/current/topo.json | The current topographic style (follows each release) |
/styles/v/<release>/topo.json | A fixed version; the manifest names it in artifacts.styles.topo |
import maplibregl from 'maplibre-gl';
import { Protocol } from 'pmtiles';
maplibregl.addProtocol('pmtiles', new Protocol().tile);
new maplibregl.Map({
container: 'map',
style: 'https://api.trailsplits.com/styles/current/topo.json',
center: [7.75, 46.02], zoom: 13,
// Compact on phones: the credit stays one tap away instead of covering the map.
attributionControl: { compact: true },
}); Trails on waymarked routes carry an orange band; the line on top gives the difficulty (SAC scale): solid red for T1 hiking, long red dashes for T2 mountain trails, short dark red dashes for T3 demanding mountain trails, blue dots for T4–T6 alpine routes. Other paths are brown dashes. Layer ids are part of the contract: hide or restyle a layer by its id; ids are added, never renamed, within topo-style/1. Map styles and tiles are unmetered.
Open aerial imagery (imagery/1)
Official national orthophotos (Switzerland, France, Austria, Spain, Bavaria, the contiguous United States, South Tyrol, Slovenia, Trentino, Japan) and TrailSplits' own late-summer Sentinel-2 mosaic for 16 trail regions, as one tile layer. Each source is cut at its own border, and tiles are transparent where nothing covers the ground, so lay them over a map. The imagery stays under each source's open licence; we add no restriction to it. Show the credits of the sources in view: ask /tiles/v1/imagery/credits for the view, or use the TileJSON's full credit line. Map tiles are unmetered. Every new tile is fetched from the national services, so their fair use applies: no bulk export or offline packs.
| GET path | Response |
|---|---|
/tiles/v1/imagery/tilejson | TileJSON 3.0.0: the tile address (with its version), zooms 8–19, and each source's credit, licence link, terms and date |
/tiles/v1/imagery/{z}/{x}/{y}.webp?v=<version> | A tile (WebP); 204 where no imagery covers it |
/tiles/v1/imagery/credits?bbox=w,s,e,n&z= | The credits for a view, lowest source first |
/tiles/v1/imagery/coverage.geojson | Where each source covers, with its licence and date (about 1 km detail) |
const tj = await (await fetch('https://api.trailsplits.com/tiles/v1/imagery/tilejson')).json();
map.addSource('imagery', { type: 'raster', tiles: tj.tiles, tileSize: 256, minzoom: tj.minzoom, maxzoom: tj.maxzoom, attribution: tj.attribution });
map.addLayer({ id: 'imagery', type: 'raster', source: 'imagery' }, firstLabelLayerId); | Source | Area | Licence |
|---|---|---|
| swisstopo SWISSIMAGE | Switzerland | swisstopo OGD |
| IGN BD ORTHO | France with Corsica | Licence Ouverte 2.0 |
| basemap.at | Austria | CC BY 4.0 |
| IGN España PNOA | Spain (not Melilla) | CC BY 4.0 |
| Bayerische Vermessungsverwaltung DOP | Bavaria | CC BY 4.0 |
| USGS The National Map (NAIP) | Contiguous United States | Public domain |
| South Tyrol Orthofoto 2023 | South Tyrol | CC BY 4.0 |
| GURS DOF025 | Slovenia | CC BY 4.0 |
| Ortofoto Digitale 2015 - PAT | Trentino | CC BY 2.5 IT |
| GSI seamless photos | Japan | Public Data License 1.0 |
| TrailSplits Sentinel-2 summer mosaic | 16 trail regions (Aosta, Dolomites, Madeira, Iceland, northern Sweden, Scotland, Ireland, High Tatras, Jordan, New Zealand, Victoria, Lycian coast …) | Copernicus Sentinel data |
Vector basemap tiles (PMTiles)
OpenMapTiles-schema vectors in a planet PMTiles archive. A client fetches only the byte ranges it needs. Do not download the whole planet archive to display a local map. Register the PMTiles protocol in MapLibre, then use its pmtiles:// URL.
| GET path | Use |
|---|---|
/pmtiles/v/<release>/tiles/openmaptiles_planet.pmtiles | Versioned byte-range reads |
/pmtiles/current/tiles/openmaptiles_planet.pmtiles | Mutable archive; avoid mixing cached ranges across a replacement |
pmtiles://https://api.trailsplits.com/pmtiles/current/tiles/openmaptiles_planet.pmtiles curl --fail-with-body --max-time 12 --max-filesize 1048576 -sS --range 0-1023 -D - -o /dev/null "https://api.trailsplits.com/pmtiles/current/tiles/openmaptiles_planet.pmtiles" Expect 206 Partial Content and 1,024 bytes for this check. Pin a versioned archive before caching its ranges. See PMTiles documentation and MapLibre for client setup.
Static map rendering
POST /static-map/render for a PNG snapshot. Supply bbox=[minLon,minLat,maxLon,maxLat], with small width/height in pixels. Optional geojson overlays a line or feature collection; padding adjusts framing. Default output is 1200×630 px at ratio 1. Center/zoom alone is unsupported, and pixelRatio is not implemented.
curl --fail-with-body --max-time 12 --max-filesize 1048576 -sS -X POST "https://api.trailsplits.com/static-map/render" \
-H 'Content-Type: application/json' \
--data-raw '{"width":256,"height":256,"bbox":[12.13,46.53,12.14,46.54]}' -o map.png Missing bbox returns 400; an unavailable renderer can return 503. Keep render dimensions small. Images include OpenMapTiles/OpenStreetMap credits by default; retain applicable attribution where you display them.
Calculate an outdoor route
Walking, running and cycling use the TrailSplits outdoor router over OpenStreetMap. Choose explicit points and a difficulty cap; inspect defaults and warnings in the result. Car routing (auto) is unavailable and returns 400.
curl --fail-with-body --max-time 12 --max-filesize 1048576 -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.536,
"lon": 12.133
}
],
"costing": "pedestrian",
"costing_options": {
"pedestrian": {
"max_hiking_difficulty": 3
}
}
}' Run the outdoor routing example to inspect explicit settings, geometry and a documented refusal.
This short public Dolomites input returned a 395 m route in the 1 October check. Routes include an engine/tile stamp and defaulted settings; that provenance is not a request parameter to pin future calculations.
Routing profiles, units, compatibility and refusals
POST /route/v1, or GET with the same JSON encoded into ?json=. The gateway accepts a subset of Valhalla's request format; its path and request shape do not identify the engine that answered.
locations: 2–100 objects{lat,lon}, or arrays[lon,lat]. Body limit: 1,000,000 bytes.costing: pedestrianfor walking; the router also has a running profile viatrailsplits.profile: run.costing: bicyclefor cycling.- Walking
costing_options.pedestrian.max_hiking_difficulty: 1–6 (T1–T6); set it explicitly. The observed public walking default is T3. Bicycle preferences includebicycle_type,use_roadsanduse_ferry. - Response: OSRM shape, polyline6 geometry, distance in metres and duration in seconds, even when
directions_options.unitsorformatasks for something else. Duration is a routing cost estimate; named hiking-time models are separate summary fields. - Inspect
trailsplits.engine,summaryandwarnings. Marked-trail and paved-surface preferences are not exclusions. Temporary closure-watch notices and snow are not routing constraints.
Current routing capabilities names rules, supported options and the engines serving each profile. It is on trailsplits.com, separate from the API host, and currently lacks cross-origin browser CORS. Its prose mentions a T6 site default while its structured site fields and current code say T3; use an explicit cap. The site's /api/route uses a different body with coordinates, profile and options.
| Response | Meaning |
|---|---|
| 400 | Invalid inputs or unsupported costing (including auto) |
| 413 | Request body too large |
| 422 | No supported route, corridor too large or unsupported constraint |
| 503 | Required engine/data unavailable |
A refusal is not a straight-line route or a safety judgement. Review unknown tags, snapping and warnings, and check local conditions.
Ways and surfaces along a line
POST /valhalla/trace_attributes (or GET ?json=…) with shape points {lat,lon}, costing and shape_match: map_snap. Up to 2,000 points; the same body limit applies. The JSON exposes edges, match coverage, unmatched spans and engine provenance. Edge length is in kilometres. Some surfaces are inferred from road class: use trailsplits_surface_tagged when provided; an absent flag means provenance is unknown.
Analyse a route, get a loop, or the weather on the way
Three calls built on the same router as the Planner. All three answer with facts placed on the mapped network and say what is not known; none invents a line it did not find.
Analyse a route you already have
POST https://api.trailsplits.com/v1/route-analysis with a GPX document (gpx) or a GeoJSON line (geometry) and a profile (hike, run or bike). The answer says where the line runs on the mapped network (match, with unmatched stretches listed), on what surface, which OpenStreetMap difficulty grades are tagged, how much it climbs and by which method, how long a hike takes with the model's scope (time.outside_calibration), and every unknown in unknowns.
curl --fail-with-body 'https://api.trailsplits.com/v1/route-analysis' \
-H 'Content-Type: application/json' \
--data '{"geometry":{"type":"LineString","coordinates":[[12.1352,46.5369],[12.133,46.536]]},"profile":"hike"}' - Limits: body 1 MB, 5,000 points, 300 km along the line. Larger files are refused (413 or 422), never cut.
- The line you send is the authority: it is never simplified, reordered or joined across a gap. Positions are metres along your line.
- Untagged paths are unknown, not easy. A surface the map does not state is reported as a class default (
source: class_default). - Only
hikehas a time estimate (trailsplits-hike/2, moving time). Run and bike answertime: null. - No geometry is kept after the answer, and logs carry no coordinates.
Round trips from a start point
POST https://api.trailsplits.com/v1/roundtrips with start ([lon, lat]), distance_km, profile and up to three alternatives. Each loop reports its length against the target (target_error_pct), how much is walked twice (repeated_share), climb, the hike time and its surfaces, with the loop as polyline6 in shape.
curl --fail-with-body 'https://api.trailsplits.com/v1/roundtrips' \
-H 'Content-Type: application/json' \
--data '{"start":[7.7491,46.0207],"distance_km":12,"profile":"hike"}' status: completemeans within 10 % of the target with at most 20 % walked twice;roughis answered with areason. When no good loop exists the answer is 422no_loop, not a forced loop.- Limits: 0.5–40 km on foot, 0.5–60 km by bike (422
too_long). Hike and run keep to marked trails where they go and never use a tagged grade abovemax_sac(default T3).
Route through waypoints
POST https://api.trailsplits.com/v1/route with 2 to 100 waypoints as [lon, lat] and a profile: a walking, running or cycling route on signed trails by default, as a GeoJSON line with its legs, climb, hike time, surfaces, SAC grades, hazards and turn-by-turn steps. It is the same router as the Valhalla-compatible POST /route/v1, in the /v1 style. Reference and Try it.
Exits along a route
POST https://api.trailsplits.com/v1/route-exits with one day's line: every kilometre, the nearest mapped public road, stop, village and hut with walking minutes, and the longest stretch with no way out within an hour. A mapped way out, never a safe one. Reference and Try it · Recipe.
Day outings near a point
POST https://api.trailsplits.com/v1/outings with a lat, lon and your rules (hours, SAC grade, a stop at the start, water): an ordered list of curated outings (Switzerland and its borders so far) and round trips from your point, each with its facts and why it is listed; with a date, its go window. A rule leaves out what it cannot judge, never passes it as easy. Reference and Try it · Recipe.
Your own walking pace
POST https://api.trailsplits.com/v1/time-model/fit with 1 to 30 of a person's own timed walks: their pace against the TrailSplits hike time model on the flat, uphill and downhill, with its uncertainty, as pace_profile. Send it to route analysis, the route brief, route weather or go windows for their own time beside the model's. Nothing is kept. Reference · Recipe.
Route card to print
POST https://api.trailsplits.com/v1/route-card with a line, a name and your waypoints: one self-contained HTML page or a PDF on A4 or Letter, with the map, profile, legs, water and huts, the weather when you give a departure, and a QR code to your trailsplits.com link. Nothing is stored or published. Reference.
Downloads
GET https://api.trailsplits.com/v1/downloads/catalog lists prebuilt regional packages (offline map extracts in PMTiles, outdoor places packs); POST https://api.trailsplits.com/v1/downloads returns signed links valid 24 hours, charged once. Packages appear as they are built. Reference · Console.
Go windows: which day and start time
POST https://api.trailsplits.com/v1/route-windows with one day's line, days and your prefer order (no_thunder, dry, daylight, calm, warm, cool): every day × start time with its forecast facts at the hour you reach each point, ordered by your preferences, each saying why. An ordering of facts, never a safety score. Reference and Try it · Recipe.
Bulk jobs: many lines in one call
POST https://api.trailsplits.com/v1/jobs with up to 100 { op, body } items (route analysis, elevation profile, GPX clean or simplify, places along a route, pace plan; each body exactly as its own endpoint takes it), then poll GET /v1/jobs/{id} and read each answer at /items/{n}. The same answers, later: the worker gives way to interactive traffic, so a job may wait. Each item is charged as its own call when it runs; results are kept 72 hours. Secret keys only. Reference.
Watches: tell me when it changes
POST https://api.trailsplits.com/v1/watches with a line, a few dates and your webhook_url: TrailSplits checks it every few hours and calls your webhook, signed with your project's secret, when the best window or a thunderstorm signal changes. The route is kept only while the watch exists. Manage watches, the signing secret and the delivery log in the console. Reference.
Slope at the path (experimental)
POST https://api.trailsplits.com/v1/route-slopes with one line: the stretches where the terrain model at the path is 30° or steeper, in three classes, with the way each faces and links to the official avalanche bulletins for the areas the line touches. It is the slope at the path on a model of about 26 m: not the path's gradient, not the slopes above or beside it, not avalanche terrain and not a risk. Reference and Try it.
Route brief
POST https://api.trailsplits.com/v1/route-brief with one day's line and include: route analysis, weather at arrival time (with a departure), sun and shade, places along the route and snow evidence in one call, each the same answer as its own endpoint, plus notices: fact codes placed along the line, such as a ford, a stretch tagged T4 or a long stretch without mapped water. Notices are facts, never advice or a safety score. Reference and Try it.
Places along a route
POST https://api.trailsplits.com/v1/route-places with one line: mapped drinking water, springs, huts, shelters and transport stops within within_m of it, in order along it, with their OSM tags as tagged, and the longest stretch without confirmed drinking water. It says where water is mapped, never that it runs. Reference and Try it.
Pace plan for a course
POST https://api.trailsplits.com/v1/pace-plan with one line and a target_time (or flat_pace_s_per_km): splits every kilometre or mile, slower where the course climbs or descends steeply, each named by its terrain. The engine of the Pace & Split Calculator. Reference and Try it.
Elevation profile of a line
POST https://api.trailsplits.com/v1/elevation-profile with a GPX or GeoJSON line: the series a chart draws (distance, height, grade and position at each point) and the climb read from it. Reference and Try it.
Clean and simplify GPX files
POST https://api.trailsplits.com/v1/gpx/clean removes repeated and broken points, and on request times and sensor data; the file comes back as the file, everything else byte for byte. POST https://api.trailsplits.com/v1/gpx/simplify keeps the shape within a tolerance in metres (or a point budget), protects the climb and keeps stops; it answers like the GPX Simplifier. Files up to 5 MB. Clean · Simplify.
Weather along the route, at the hour you get there
POST https://api.trailsplits.com/v1/route-weather with one line (gpx or geometry), a departure with its time-zone offset and a profile. Samples every 2 km, plus the start, the end and the marked highs and lows, each get the forecast for the hour you reach them, at their own terrain height. Each hour names its model; sections give ranges between the marked points.
curl --fail-with-body 'https://api.trailsplits.com/v1/route-weather' \
-H 'Content-Type: application/json' \
--data '{"geometry":{"type":"LineString","coordinates":[[12.1352,46.5369],[12.133,46.536]]},"departure":"2026-10-03T08:00:00+02:00","profile":"hike"}'
# Set departure inside the next 16 days. - Hike times come from the calibrated
trailsplits-hike/2model (addpace_factorandstops); run and bike need your ownmin_per_kmorkmh. - An hour the forecast does not hold is
weather: nullwithmissing; a missing value isnull, never 0. There is no combined safety score. - Limits: one part, 5,000 points, 120 km, 1 MB; a departure from six hours ago to 16 days ahead. Show
forecast.provider.attributionwith the values.
Hazards on the line. Route analysis and every round trip list hazards[]: each ford, aided passage (ladders, fixed ropes) and via ferrata from OpenStreetMap, placed in metres along the line.
Errors from all three are {"error": {"code", "message"}}. Full references: route analysis, round trips and route weather, each with Try it on a map. The downloadable examples include analysis.mjs, roundtrip.mjs and routeweather.mjs.
Inspect terrain and elevation
Run the elevation profile example to inspect modelled heights and missing-data gaps.
Terrain, hillshade and contours use the Copernicus GLO-30 surface model. The served TerrainRGB layer reaches z12, with processing improvements in selected patches. Source resolution, delivered pixel size and measured accuracy are different things: overzoom does not recover detail absent from the source.
Elevation sampling
GET /tiles/v1/elevation/<release>/sample for supplied points in lat,lon|lat,lon order. Results are modelled metres; elevation_m:null means missing data. The sampler reads a terrain pixel at each point. It does not choose spacing along a line, smooth a profile or guarantee surveyed ascent.
Run a live elevation request
This page example allows 1–32 points, a sampling zoom of 0–12, an 8-second timeout and a 64 KiB response. The service's point limit is 2,048. Typing does not send requests.
Submit the form to make a live request. Terrain, elevation and contour reference
TerrainRGB tiles
| GET path | Response |
|---|---|
/tiles/v1/terrainrgb/<release>/{z}/{x}/{y}.png | 256 px Mapbox TerrainRGB PNG |
/tiles/v1/terrainrgb/<release>/metadata.json | Metadata / native zoom bounds |
curl --fail-with-body --max-time 12 --max-filesize 1048576 -sS "https://api.trailsplits.com/tiles/v1/terrainrgb/current/9/271/186.png" -o terrain.png Tile coordinates are XYZ Web Mercator. Higher zoom requests can overzoom a parent within the server's configured range; that does not produce higher source resolution. Read native bounds in metadata, and patches/source versions in the manifest.
{
"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,
"attribution": "Terrain and contours in Switzerland © swisstopo (swissALTI3D). Terrain and contours produced using Copernicus WorldDEM-30 © DLR e.V. 2010-2014 and © Airbus Defence and Space GmbH 2014-2018 provided under COPERNICUS by the European Union and ESA; all rights reserved"
}
},
"terrain": {
"source": "dem",
"exaggeration": 1.2
},
"layers": [
{
"id": "hillshade",
"type": "hillshade",
"source": "dem"
}
]
} Elevation parameters
pointsrequired; up to 2,048 points inlat,lonorder.zdefaults to 13, clamped to the dataset's zoom range (currently max 12).encodingdefaults tomapbox. The API also acceptsterrarium, but these served tiles use Mapbox encoding: keep the decoder aligned with the dataset.
Response includes release, z, z_requested, encoding and points. Missing points returns 400; a nonexistent release returns 404. Missing terrain samples return nullable elevations, which must not become zero climb or flat terrain claims.
Contour tiles
GET /tiles/v1/contours/<release>/{z}/{x}/{y}.pbf or metadata.json. The checked layer has a 20 m interval at z10–12, produced on a resampled grid. Requesting z13 directly can return an empty tile. New native-grid or regional models are not advertised here as live until promoted.
curl --fail-with-body --max-time 12 --max-filesize 1048576 -sS "https://api.trailsplits.com/tiles/v1/contours/current/12/2186/1448.pbf" -o contours.pbf Required DEM attribution: Terrain and contours in Switzerland © swisstopo (swissALTI3D). Terrain and contours produced using Copernicus WorldDEM-30 © DLR e.V. 2010-2014 and © Airbus Defence and Space GmbH 2014-2018 provided under COPERNICUS by the European Union and ESA; all rights reserved. Include this in terrain/contour source attribution wherever displayed; the terrain snippet above does so.
Discover trails and places
Search OSM-derived named places, retrieve outdoor POIs, or inspect hiking/cycling relations. These are source records with uneven completeness. A raw relation can contain disconnected or unordered members; it is not automatically a usable itinerary.
curl --fail-with-body --max-time 12 --max-filesize 1048576 -sS "https://api.trailsplits.com/search/v1/forward?q=Zermatt&limit=2" Search results give center: [lon,lat]. Empty results do not establish that a place, water source or trail is absent on the ground.
Place search, POIs and trail relation reference
Place search and reverse geocoding
| GET path | Parameters |
|---|---|
/search/v1/forward | q, optional limit, bbox, bias_bbox |
/search/v1/reverse | Required lat,lon; optional limit,radius_m,kinds |
/search/v1/healthz | Index health/statistics |
Forward/reverse limit defaults to 10 and is clamped to 1–50. Bbox order is minLon,minLat,maxLon,maxLat; bias_bbox is a soft spatial boost. Reverse radius defaults to 500 m, clamped to 50–50,000 m. Use positive numeric limits and valid boxes. The current database is not pinnable through a release query.
curl --fail-with-body --max-time 12 --max-filesize 1048576 -sS "https://api.trailsplits.com/search/v1/reverse?lat=46.0207&lon=7.7491&limit=2" Hiking and cycling network tiles
GET /tiles/v1/hiking-network/<release>/{z}/{x}/{y}.pbf or metadata.json. Current native range: z4–12. Network tiers iwn/nwn/rwn/lwn and relation tags appear where available. An empty 200 response can mean no tile at that coordinate/zoom.
curl --fail-with-body --max-time 12 --max-filesize 1048576 -sS "https://api.trailsplits.com/tiles/v1/hiking-network/current/12/2186/1448.pbf" -o network.pbf Outdoor POIs
Water sources, huts/shelters, parking, viewpoints, passes and transit features from OSM. GET /tiles/v1/outdoor-pois/<release>/{z}/{x}/{y}.pbf or metadata.json; current native range z6–14.
curl --fail-with-body --max-time 12 --max-filesize 1048576 -sS "https://api.trailsplits.com/tiles/v1/outdoor-pois/current/14/8744/5793.pbf" -o pois.pbf GET /pois/v1/bbox for flat JSON place records. Required: min_lat,min_lon,max_lat,max_lon (each side at most 2°). Optional: comma-separated kind, positive limit (default 2,000, max 10,000), release (default current). Start with a tiny box and limit. The object is named FeatureCollection, but its features are flat records, not GeoJSON Features; coordinates are lat/lon, elevation ele is nullable. artifact_version names the data used.
curl --fail-with-body --max-time 12 --max-filesize 1048576 -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" Trail relations
| GET path | Result |
|---|---|
/trails/v1/search?q=… | GeoJSON results with representative Points |
/trails/v1/relation/<osm_id> | Source geometry and properties |
/trails/v1/bbox | Relations intersecting a small box |
/trails/v1/healthz | Relation health/counts |
Search requires q; limit defaults to 20, max 100. Optional type=hiking|bicycle. Bbox requires min_lat,min_lng,max_lat,max_lng — lng here, lon for POIs — with optional type/limit (default 50, max 5,000). Keep the geographic footprint small. Relation geometry may be large; do not assume ordering, connectivity, access or current trail conditions.
curl --fail-with-body --max-time 12 --max-filesize 1048576 -sS "https://api.trailsplits.com/trails/v1/search?q=Tour+du+Mont+Blanc&limit=1"
curl --fail-with-body --max-time 12 --max-filesize 1048576 -sS "https://api.trailsplits.com/trails/v1/relation/9678362" -o relation.json
curl --fail-with-body --max-time 12 --max-filesize 1048576 -sS "https://api.trailsplits.com/trails/v1/bbox?min_lat=46.0&min_lng=7.74&max_lat=46.03&max_lng=7.77&type=hiking&limit=2" Attribute TrailSplits and © OpenStreetMap contributors, and follow the applicable source licence when redistributing derived data.
Access available datasets and source evidence
Use the manifest to discover served artifacts, and each product's metadata to understand its bounds and sources. Production liveness, build time, source date and freshness verdict are separate facts. A recent promotion does not make an old observation new.
Artifact manifest
curl --fail-with-body --max-time 12 --max-filesize 1048576 -sS "https://api.trailsplits.com/pmtiles/current/manifest.json" GET /pmtiles/current/manifest.json returns per-artifact versions, versioned/current addresses, sources and attribution. Use each artifact's version; the top-level release is not a shared release for every dataset.
Catalog, release behavior and snow evidence reference
Dataset catalog and health
GET /tiles/v1/catalog lists candidates for terrain, contours, snow, networks, POIs and other tile sets. It does not enumerate every artifact in the manifest or promise retention of every listed release. GET /healthz returns plain-text ok from the public gateway: it is a connectivity check, not a freshness or coverage check.
curl --fail-with-body --max-time 12 --max-filesize 1048576 -sS "https://api.trailsplits.com/healthz"
curl --fail-with-body --max-time 12 --max-filesize 1048576 -sS "https://api.trailsplits.com/tiles/v1/catalog" Snow observations and forecasts
GET /tiles/v1/snow/<release>/{z}/{x}/{y}.png or metadata.json for the observation overlay and regional product metadata. Regional coverage and cloud/shadow/forest gaps remain. A tile response alone does not certify current optical data at a point.
GET /tiles/v1/snow/route-forecast with sampled points=lat,lon|… (max 512), optional release=current,z=13,encoding=mapbox. Add evidence=1 for SNOW-1 route-snow-evidence/1, revision (1–64 letters/numbers or ._:-) to identify your geometry version, and segments for start indices of disconnected segments (default 0).
curl --fail-with-body --max-time 12 --max-filesize 1048576 -sS "https://api.trailsplits.com/tiles/v1/snow/route-forecast?points=46.5369,12.1352%7C46.5360,12.1330&evidence=1&revision=dx-public-example" evidence.valid_for: nowdescribes sampled current evidence, valid until the returned time (at most five minutes). Check revision, identity and expiry before displaying a late response.source: observeddiffers frommosaic_14d, an older observation mosaic. Per-pointobserved_onis null in v1; do not invent a date.coverage: full|partial|noneand snow/clear/unknown percentages apply to sampled points. Unknown is never clear. Narrow crossings between samples are not assessed.forecast_24h/forecast_48hare weather-model predictions with issued/valid times; available weather does not establish optical observation coverage. Carry the returned forecast attribution (the checked provider is ECMWF).- Outside observed coverage, a successful 200 can report
coverage: noneand 100% unknown. Bad inputs return 400; changing/unreadable evidence can return 503. Unknown snow does not imply a clear or safe route.
Weather forecasts
GET https://trailsplits.com/api/weather/forecast with p=lat,lon[,elevation];… (1 to 12 points), d=1–16 days (default 7), optional fl=1 for the hourly freezing level and optional h=1 for hourly values per point (temperature, precipitation, rain probability, wind, gusts, wind direction, weather code, each hour with its model; about 130 bytes per hour and point, so ask only for the days you need). The answer is weather-forecast/1: daily weather code, maximum and minimum temperature, precipitation, fresh snow (snowfall_cm) and rain (rain_mm), rain probability, wind and gusts, per point and local date. The snow/rain split follows the model's own terrain height: on global-model days in high mountains, read it with the freezing level (fl=1).
curl --fail-with-body --max-time 12 --max-filesize 1048576 -sS "https://trailsplits.com/api/weather/forecast?p=45.9763,7.6586,4478;46.0207,7.7491,1608&d=7" - Each day comes from the best model that covers it whole: MeteoSwiss ICON-CH2 in the Alps (about 2 km, 5 days), DWD ICON-D2 in Central Europe (2 km, 2 days), then ECMWF IFS 0.25° (15 days);
days[].modelnames it, andprobability_modelsays when the rain probability comes from another model.models=ecmwfasks for ECMWF alone. issued_atis the oldest model run used (each run is listed inprovider.runs);fetched_atis when it was asked andfresh_untilhow long to show it as current. Do not present the fetch time as the forecast's age.- Pass the height of each point. Without one, TrailSplits' terrain model supplies it (
elevation_source: terrain); only if that fails is the forecast for a model cell's mean height (provider_grid), which in the mountains can be a thousand metres off. - A missing value is
null, never 0; a point with fewer days or fields than asked isstate: partialwithreasons. The last forecast day is often partial. - When the forecast copy is down, the last good answer is served for up to 12 hours (its
fresh_untilhas passed), then 503forecast_unavailable. Answers may be cached for ten minutes. - Attribution: “Weather data by Open-Meteo.com (CC BY 4.0)”, returned in
provider.attribution. Full weather reference.
Base URL, releases and caching
The documented asset/calculation paths above use https://api.trailsplits.com. The linked capabilities document uses the main site. current is mutable; versioned asset content is immutable while retained. Retired tile releases can return 410 with successor information; indefinite hosting is not promised.
Pin related map assets together. Dynamic routes and current search do not share the tile-release pinning contract. Follow each response's Cache-Control: versioned basemap/style assets advertise immutable caching, while current aliases and computations differ. Some current tile responses include no-store. Cache expiry is not a source date.
Authentication, CORS and limits
The API-host examples were verified anonymously with permissive CORS on 1 October 2026. No key or sign-up is needed for these examples. The site's capabilities URL is a separate origin and lacks external browser CORS; read it from the same origin or your server.
Projects and keys. Signed in with your TrailSplits account, the API console creates a project, issues a server key (shown once) and shows what your calls use. Send it as Authorization: Bearer <key>. Without a key you get 500 credits a day per address; a key gives your project its plan's monthly credits and burst rate. Map tiles and styles need no key.
Limits. Calculated answers (routes, elevation samples, search, route analysis, round trips) are rate-limited. Without a key, each address may make 5 requests per second, in bursts of up to 60, and use 500 credits a UTC day. With a key, calls count against your project: its burst rate and monthly credits are shown in the console, where you also see usage per operation. Keyed answers carry X-TrailSplits-Credits-Remaining, and GET https://api.trailsplits.com/v1/usage with your key returns the same figures as the console. Map tiles, styles and the manifest are not counted. Cache where response headers allow it, keep inputs small and avoid repeated requests while typing. No SLA or paid service commitment is offered here.
| Answer | Meaning and what to do |
|---|---|
401 credential_invalid | The key is unknown, revoked or rotated. Issue a new one in the console. A rotated key stops within a minute. |
429 rate_limit | Too many requests per second; limit says burst (your key) or ip (no key). Wait the Retry-After seconds, then retry. |
429 concurrency_limit | A heavy request (round trip, route analysis, route weather) while your project, or your address without a key, already has its limit running: 1 on Free and without a key. Retry after Retry-After (2 s). |
429 rate_limit, limit: "operation" | A per-operation limit: static maps 2 requests a second, route analysis and route briefs 4 at a time per project. Wait Retry-After. |
429 rate_limit, limit: "operation_total" | An operation is at the ceiling all API users share (routes 50, search 80, elevation 80 requests a second). Wait Retry-After. |
429 bbox_daily_ceiling | A project's area lookups (outdoor places or trails in a box) passed their daily ceiling: three times the plan's daily average credits, 3,000 on Free. Resets at 00:00 UTC. |
403 origin_not_allowed | A browser key (ts_pub_…) used from an origin that is not on its list in the console. |
429 keyless_daily_credits_exhausted | Calls without a key have used this address's 500 credits for the UTC day; Retry-After counts to 00:00 UTC. A free key lifts this. |
429 monthly_credits_exhausted | The project's credits for this UTC month are used up. Retry-After counts the seconds to the next month. |
503 accounting_unavailable | A key could not be checked for a while. Retry after Retry-After (60 s); calls without a key go on. |
Gateway errors are JSON: {"error", "status", "message", "limit", "retry_after"}, where message is safe to show and limit and retry_after appear when they apply.
Attribution is part of integration: TrailSplits and © OpenStreetMap contributors for OSM-derived products, OpenMapTiles where applicable, the full DEM notice for terrain/contours, and source-specific notices for imagery/weather. Read the manifest and returned forecast metadata.
Plans and terms
The Free plan includes commercial use with attribution: 30,000 API credits a month, with the credit cost of each call published. Larger plans are planned and not yet available. Use follows the API terms: show the source credits, keep keys on your server, and do not resell the data as a dataset or tile service.
Need more than the Free plan, redistribution or a data delivery? Write to support@trailsplits.com with your use case, the capabilities and area you need, and your expected volume.
Questions before integrating
Is the TrailSplits API free to use?
Yes. The Free plan includes commercial use with attribution: 30,000 API credits a month at 5 requests per second, and map tiles are not counted at present. When the credits are used up, calls stop until the next month; nothing is charged. Larger plans are planned. See the plans page and the API terms.
Do I need an API key or authentication?
No, to try it: without a key you get 500 credits a day per address at 5 requests per second. A free key from the API console, sent as Authorization: Bearer, gives you 30,000 credits a month, a usage view and higher limits. Map tiles and styles need no key. The api.trailsplits.com examples allow cross-origin browser calls; the separate trailsplits.com routing-capabilities URL does not.
Where does the API work?
The basemap, terrain and OSM-derived search, trails and places are globally scoped, with uneven source coverage. Terrain uses the Copernicus GLO-30 surface model and selected processing patches. Snow observations have regional coverage; available weather forecasts do not establish observed snow coverage.
How are datasets versioned?
The manifest names each served artifact and its version. current is a mutable alias; versioned assets keep the same content while retained, but may later be retired. Pin map dependencies as well as the style document. Search and dynamic routes do not have the same release-pinning contract.
Are responses cached?
Read Cache-Control on the specific response. Versioned basemap and style assets advertise immutable caching; current aliases and computed responses differ. Some current tile responses include no-store. A cache lifetime is not a source observation date or retention promise.
Explore these capabilities in the Planner and browser tools. Public examples here demonstrate underlying products; an identical HTTP contract or route result across clients is not promised.