Public read API

The unauthenticated legacy endpoints for reading trail systems, trails and points, and the format of the published GeoJSON file.

The public read API is the original TrailHUB API. It is read-only and unauthenticated: anyone who knows a trail system's ID can fetch its data. It is what TrailHUB's own embeds use, and it remains available alongside the newer Management API v1.

Base URL: https://trailhub.org/api

A trail system's ID is the string in its TrailHUB URL, for example https://trailhub.org/ts/<ID>.

#Before you start

  • CORS. Browser calls to these endpoints are only allowed from an allowlist of origins (TrailHUB's own domains and a few partner sites). A fetch() from your website will fail with a CORS error unless your origin has been added. Server-side calls (curl, Node, PHP, Python, a serverless function) work from anywhere, because the allowlist only applies when the browser sends an Origin header. If you need browser access from your domain, ask us to add it, or proxy the call through your own server. The Management API v1 does not have this restriction.
  • No plan check is enforced in code, although developer access was historically sold as a top-tier (Double Diamond / Star) feature.
  • No rate limit is enforced. Please poll no more often than every few minutes and cache the GeoJSON file.
  • Dates in Firestore documents come back as { "_seconds", "_nanoseconds" } objects and coordinates as { "_latitude", "_longitude" }, not as ISO strings. (The Management API v1 normalizes these.)
  • Errors return an empty body with HTTP status 500, including for IDs that do not exist.

#Endpoints

#GET /api/trail-systems

All active trail systems as a GeoJSON FeatureCollection of Point features, one per system, centered on each system's geolocation. The file is rebuilt every 3 hours. Inactive and blocked systems are omitted.

Feature properties:

PropertyTypeDescription
namestringTrail system name
tsIdstringTrail system ID, for use with the other endpoints
openTrailsnumberTrails currently Open
cautionTrailsnumberTrails currently Caution
closedTrailsnumberTrails currently Closed
activitiesarrayActivity summaries (see Activity objects)
hiddenActivitiesarrayActivities the manager has hidden from public lists (may be absent)
curl https://trailhub.org/api/trail-systems

#GET /api/ts/:id

One trail system document. Internal fields (users, permissions, ownerId, createdBy, updatedBy, geoJsonPath) are removed, and geoJsonUrl is added.

FieldTypeDescription
namestringName
descriptionstringPublic description
phoneNumber, websitestringPublic contact details
languagestringen or fr
inactivebooleanHidden from the public map when true
geolocationobject{ _latitude, _longitude }
depthMeasurementstringinches or centimeters
lengthMeasurementstringmiles or kilometers
tempMeasurementstringfahrenheit or celsius
totalTrails, openTrails, cautionTrails, closedTrailsnumberTrail counts, excluding lifts and hidden trails
totalDistance, openDistancenumberKilometers; openDistance includes Caution trails
activitiesarrayActivity summaries (see below)
updatesobjectActive system-level updates keyed by type: notice, snowReport (see Updates)
weatherobjectHourly weather snapshot (paid tiers only; may be absent)
geoJsonUrlstringPublic URL of the compiled GeoJSON file (see Published GeoJSON)
createdOn, updatedOnobjectFirestore timestamps
relatedTrailSystemsarray{ tsId, name } objects, if configured
iconPathstring or falseIcon URL, if uploaded
curl https://trailhub.org/api/ts/TS_ID

The geoJsonUrl changes on every recompile (the file name includes a date and a random suffix), so fetch /api/ts/:id first rather than caching the GeoJSON URL for long.

#Activity objects

Each entry of activities (on a trail system) looks like:

PropertyDescription
valueMachine key, e.g. xcSkiing, hiking, fatBiking
labelDisplay label
iconClassCSS icon class used by the web app
totalTrails, openTrails, cautionTrails, closedTrailsTrail counts for this activity
totalDistance, openDistance, cautionDistance, closedDistanceKilometers

On a trail, each activity carries value, label, iconClass, status (Open, Caution, Closed) and optionally difficulty.

#GET /api/trails/:id

All trails for trail system :id, as a JSON array ordered by order ascending. createdBy and updatedBy are removed; id is added.

FieldTypeDescription
idstringTrail ID
trailSystemIdstringOwning system
name, descriptionstring
statusstringOpen, Caution, Closed, or None (derive from activities)
difficultystringcircle, square, diamond, doubleDiamond, terrainPark, notRated
typestringStandard or Lift
lineStylestringSolid or Dashed
distancenumberKilometers
hidden, oneWay, oneWayReversedboolean
ordernumberSort order; lower first
categoriesarray of stringsManager-defined groupings
activitiesarrayPer-activity status (see above)
geoJsonstringStringified GeoJSON FeatureCollection of the trail's line(s); parse it with JSON.parse
geolocationobjectFirst coordinate of the line
elevationsarray or false50 evenly spaced elevations in meters, or false if not yet computed
createdOn, updatedOnobjectFirestore timestamps
curl https://trailhub.org/api/trails/TS_ID

#GET /api/trail/:id

One trail document by trail ID, same fields as above (without id).

#GET /api/point/:id

One point of interest by point ID. Fields: name, description, markerClass, status, hidden, waitTime, webCamUrl, diningOptions, geolocation, trailSystemId, createdOn, updatedOn. See the Management API for the markerClass values.

There is no public "list points for a system" endpoint; points are included in the published GeoJSON instead.

#GET /api/weather/:id

Intended to return a live forecast for the trail system's location as a one-element array. This endpoint currently depends on OpenWeather's One Call 2.5 API, which OpenWeather has retired, so it is likely to return 500. Use the weather field on /api/ts/:id (hourly snapshot, paid tiers) or your own weather provider instead.

#Published GeoJSON

The file at geoJsonUrl is a GeoJSON FeatureCollection containing every trail line and every point of interest for the system, with enough properties to draw a complete status map. It is rebuilt within seconds of any change made in the web app, through the Management API or by the Grooming Tracker. The URL is public and needs no credentials, and it is not subject to the CORS allowlist, so you can load it directly in a browser map library such as MapLibre, Leaflet or Mapbox GL.

#Trail features

Geometry: LineString (a trail stored as a MultiLineString or multi-feature collection appears as several features with the same trailId).

PropertyTypeDescription
trailIdstringTrail ID
namestring
descriptionstring
statusstringOpen, Caution or Closed. A trail whose stored status is None is resolved here: Open if any activity is Open, else Caution if any is Caution, else Closed
difficultystringcircle, square, diamond, doubleDiamond, terrainPark, notRated or ""
typestringStandard or Lift
lineStylestringSolid or Dashed
distancenumberKilometers
hiddenbooleanManager has hidden the trail; filter these out for public maps
oneWay, oneWayReversedbooleanDirection arrows
ordernumberSort order
categoriesarray of strings
activitiesarrayActivity objects ordered Open, Caution, Closed, other
showInListsbooleantrue only on the first feature of a trail, so a multi-segment trail is listed once
updatesobjectActive trail-level updates keyed by type, e.g. { "surfaceConditions": [ … ] } (see below)

#Point features

Geometry: Point ([lng, lat]).

PropertyTypeDescription
pointIdstringPoint ID
namestring
markerClassstringIcon type, e.g. parking, trailhead, lodge, hazard, web-cam
statusstringOpen, Caution or Closed
hiddenboolean
waitTimenumberMinutes (for wait-time points)
diningOptionsboolean or objectDining details, when configured

Point descriptions and webcam URLs are not in the file; fetch /api/point/:id for those.

#Updates

Updates are the notices, surface-condition reports and snow reports managers post. Only unexpired updates are published.

  • System-level updates (notice, snowReport) appear on the trail system document under updates.<type>[], newest first. For example the current snow report is updates.snowReport[0].
  • Trail-level updates (surfaceConditions, and notices attached to specific trails) appear on each affected trail feature under properties.updates.<type>[].

Useful fields on an update: description, severity (info, warning, danger) and linkUrl for notices; conditions (array of { value, text, en, fr, iconClass }) for surface conditions; the snow fields (baseDepth, twentyFourHours, fourtyEightHours, sevenDays, seasonTotal, lastSnowAmount, lastSnowDate, lastSnowTime, upperElevationDepth, lastSnowMakingDate) for snow reports; plus expirationDate and updatedOn. See the Management API for the full value lists.

#Example: a minimal status map

Server side, or from an allowlisted origin, fetch the system to get the GeoJSON URL; the GeoJSON itself can be fetched from any origin.

const ts = await fetch('https://trailhub.org/api/ts/TS_ID').then(r => r.json())
const geo = await fetch(ts.geoJsonUrl).then(r => r.json())

const colors = { Open: '#2e7d32', Caution: '#f9a825', Closed: '#c62828' }
const trails = geo.features.filter(f =>
  f.geometry.type === 'LineString' && !f.properties.hidden && f.properties.type !== 'Lift')

for (const f of trails) {
  console.log(f.properties.name, f.properties.status, colors[f.properties.status])
}

If you need to change data rather than read it, use the Management API v1.