Conventions
What is the same across every call: where to send it, how to authenticate, which way round coordinates go, the units, and what an error looks like.
Hosts
https://api.trailsplits.com: everything except weather: maps and tiles, terrain, routing, route analysis, round trips, route weather, search, trails, outdoor places, static maps, map matching and snow.https://trailsplits.com/api/weather/forecast: weather forecasts.
Both answer cross-origin requests from browsers (CORS). Everything runs in one region (Helsinki, Finland).
Keys and limits
- No key: 500 credits a day per address, 5 requests per second. Good for trying things and for small demos.
- With a key (free, from the API console): send
Authorization: Bearer <key>. Calls count against your project's monthly credits and burst rate; usage per operation is in the console and atGET /v1/usage. - Heavy requests (round trips, route analysis, route weather) also have a limit on how many run at once: 1 without a key or on Free, then 2, 4 and 8 on larger plans. Over it:
429 concurrency_limit,Retry-After: 2. - Area lookups (
/pois/v1/bbox,/trails/v1/bbox) have a daily ceiling per project: three times the plan's daily average credits (3,000 on Free), then429 bbox_daily_ceilinguntil the next UTC day. - Browser keys (
ts_pub_…) work only from the web origins you list in the console, at 2 requests a second per visitor; elsewhere403 origin_not_allowed. - Secret keys (
ts_live_…) are for servers: don't put one in an app or a web page. A web page uses a browser key or the keyless allowance. - Map tiles, styles and the manifest need no key and are not counted. What each call costs: plans and limits.
Coordinates
WGS84 degrees. The newer /v1 calls and all JSON results use [lon, lat], as GeoJSON does. Query strings that predate them use lat,lon. Per call:
| Where | Order | Example |
|---|---|---|
| Route analysis, round trips, route weather (bodies) | [lon, lat] (GeoJSON) | "start": [7.7491, 46.0207] |
| Search and reverse results | center: [lon, lat] | "center": [7.6586, 45.9764] |
| Elevation sample (query) | points=lat,lon|lat,lon | points=46.5369,12.1352|46.536,12.133 |
| Weather (query) | p=lat,lon[,elevation];… | p=46.0207,7.7491,1608 |
| Snow along a route (query) | points=lat,lon|… | points=46.0207,7.7491|46,7.7 |
| Reverse search (query) | lat=…&lon=… | lat=46.0207&lon=7.7491 |
| Outdoor places (query and records) | min_lat, min_lon, max_lat, max_lon; records have lat, lon | min_lat=46&min_lon=7.7&max_lat=46.05&max_lon=7.8 |
| Trail bbox (query) | min_lat, min_lng, max_lat, max_lng (lng, not lon) | min_lat=46&min_lng=7.7&… |
| Legacy route and trace attributes (bodies) | {"lat": …, "lon": …} objects | {"lat": 46.5369, "lon": 12.1352} |
| bbox and bias_bbox (search, static map) | minLon,minLat,maxLon,maxLat | bias_bbox=7.5,45.9,7.9,46.1 |
Lines in answers (shape, geometry of routes) are polyline6: the encoded string holds lat,lon pairs at 1e-6 precision; decode to [lon, lat] for GeoJSON. The input line you send is never rewritten.
Units and time
- Distances and heights in metres, durations in seconds or named minutes (
minutes). One exception: trace attribute edgelengthis in kilometres, as in Valhalla. - Temperature °C, precipitation mm, wind km/h, probabilities %.
- Times are ISO 8601. Inputs need an offset (
2026-10-03T08:00:00+02:00or…Z). Weather dates are local to each point; usage periods are UTC months. - A value that is not known is
null, never 0. Unknowns are listed rather than guessed (for exampleunknowns[]in route analysis).
Errors
Every error is JSON with a stable code and a message you can show. There are three shapes, by family:
// The gateway (keys, limits): every API-host call can answer this way
{ "error": "monthly_credits_exhausted", "status": 429, "message": "This project has used its 30,000 credits for October.", "limit": "monthly_credits", "retry_after": 1234567 }
// The /v1 operations (route analysis, round trips, route weather) and weather
{ "error": { "code": "too_long", "message": "a hike round trip may be at most 40 km here; ask for a shorter one" } }
// The legacy route call (Valhalla-style)
{ "error": "…", "status_code": 400, "trailsplits": { "code": "unsupported_costing", "message": "…" } } | Status | Meaning | Credits |
|---|---|---|
| 400 | The request is malformed or outside the documented options. | none |
| 401 | credential_invalid: unknown, revoked or rotated key. | none |
| 403 | origin_not_allowed: a browser key used from an origin not on its list; scope_denied: a key used where a signed-in account is needed. | none |
| 413 | Body or point count over the limit. | none |
| 422 | Understood but not answerable: no route, no loop, line too long, outside coverage. | counted where work was done (for example "no route") |
| 429 | rate_limit, concurrency_limit, bbox_daily_ceiling, keyless_daily_credits_exhausted or monthly_credits_exhausted; wait Retry-After seconds. | none |
| 502 / 503 | A data source or the service is briefly unavailable (forecast_unavailable, overloaded, accounting_unavailable); retry after Retry-After. | none |
Versions and changes
- Answers name their schema (
route-analysis/1,weather-forecast/1, …). New fields can appear at any time; ignore what you don't use. - A breaking change (removing a field or endpoint, or changing its meaning) gets a new schema version and is announced on the changelog, normally 30 days ahead, as the API terms say.
- Data is refreshed weekly. The same request can give a better answer next week; answers carry the release they used (
engine.tiles.release,artifact_version).
Caching
Follow each answer's Cache-Control. Versioned map assets are immutable; computed answers vary. You may keep computed answers for up to 30 days to serve your users (terms).
The legacy route call
POST /route/v1 takes a subset of the Valhalla request format (locations as {lat, lon}, costing pedestrian or bicycle) and answers in the OSRM shape (polyline6, metres, seconds), with TrailSplits provenance in trailsplits. It does not follow the /v1 house style; the route reference covers it in full.
Attribution
Show “© TrailSplits” and the credits that come with the data (OpenStreetMap contributors, and the source of terrain, imagery or weather) wherever you show it. See the attribution and ODbL guide.