Usage Guide

How to consume pilgrimage data in your application.

Via jsDelivr CDN

Read the catalog from @main — jsDelivr refreshes a branch ref on its own cycle — then pin every file you download to the tag that index’s release field names. The @v1 alias is no longer maintained: jsDelivr caches tag URLs permanently, so it still serves a March 2026 build.

https://cdn.jsdelivr.net/gh/walktalkmeditate/open-pilgrimages@main/index.json
https://cdn.jsdelivr.net/gh/walktalkmeditate/open-pilgrimages@v1.12.0/routes/camino-frances/route.geojson
https://cdn.jsdelivr.net/gh/walktalkmeditate/open-pilgrimages@v1.12.0/routes/camino-frances/stages.json
https://cdn.jsdelivr.net/gh/walktalkmeditate/open-pilgrimages@v1.12.0/routes/camino-frances/metadata.json
https://cdn.jsdelivr.net/gh/walktalkmeditate/open-pilgrimages@v1.12.0/routes/camino-frances/waypoints.geojson
https://cdn.jsdelivr.net/gh/walktalkmeditate/open-pilgrimages@v1.12.0/routes/camino-frances/stats.json
https://cdn.jsdelivr.net/gh/walktalkmeditate/open-pilgrimages@v1.12.0/routes/camino-frances/ways/route.json
https://cdn.jsdelivr.net/gh/walktalkmeditate/open-pilgrimages@v1.12.0/routes/camino-frances/ways/stage-00.json

A route with no ways entry in index.json has not cleared the length gate — its stages are not walkable yet, and apps hide it. A route whose entry says "sparse": true is walkable but thinly curated: fewer than half its stages carry a place beyond the day’s own start and end.

Live CDN Preview

This fetches index.json straight from jsDelivr in your browser, right now — proof that the URL above actually resolves.

Static example shown below. Enable JavaScript to fetch the live response from jsDelivr.

{
  "schemaVersion": "1.0.0",
  "generatedAt": "2026-08-19T15:31:51.448Z",
  "routes": [
    {
      "id": "camino-frances",
      "name": { "en": "Camino de Santiago (Frances)" },
      "region": "Europe",
      "country": "ES",
      "distanceKm": 764,
      "topology": "linear",
      "tradition": "christian",
      "path": "routes/camino-frances"
    }
    // ...6 more routes
  ]
}

JavaScript / TypeScript

const CATALOG = 'https://cdn.jsdelivr.net/gh/walktalkmeditate/open-pilgrimages@main';

// @main always serves the current catalog; index.release names the tag every
// route file below is pinned to
const index = await fetch(`${CATALOG}/index.json`).then(r => r.json());
const BASE = CATALOG.replace('@main', `@${index.release}`);

// Load route geometry for Mapbox GL JS, Leaflet, etc.
const route = await fetch(`${BASE}/routes/camino-frances/route.geojson`).then(r => r.json());
map.addSource('camino', { type: 'geojson', data: route });

// Load stage data
const stages = await fetch(`${BASE}/routes/camino-frances/stages.json`).then(r => r.json());
for (const stage of stages.stages) {
  console.log(`Stage ${stage.index}: ${stage.name.en} — ${stage.distanceKm} km`);
}

// Historical pilgrim counts
const stats = await fetch(`${BASE}/routes/camino-frances/stats.json`).then(r => r.json());
const trend = stats.annualPilgrims.trend; // [{year: 1985, count: 690}, ...]

Swift (iOS)

import MapKit

struct Catalog: Decodable { let release: String }

let catalogURL = "https://cdn.jsdelivr.net/gh/walktalkmeditate/open-pilgrimages@main"
let (catalogData, _) = try await URLSession.shared.data(from: URL(string: "\(catalogURL)/index.json")!)
let release = try JSONDecoder().decode(Catalog.self, from: catalogData).release
let base = catalogURL.replacingOccurrences(of: "@main", with: "@\(release)")

let url = URL(string: "\(base)/routes/camino-frances/route.geojson")!
let (data, _) = try await URLSession.shared.data(from: url)

// Decode as standard GeoJSON
let features = try MKGeoJSONDecoder().decode(data)

// Or decode with custom types
let route = try JSONDecoder().decode(GeoJSONFeatureCollection.self, from: data)

Python

import json
import urllib.request

CATALOG = "https://cdn.jsdelivr.net/gh/walktalkmeditate/open-pilgrimages@main"
index = json.loads(urllib.request.urlopen(f"{CATALOG}/index.json").read())
BASE = CATALOG.replace("@main", f"@{index['release']}")

# Load stages
url = f"{BASE}/routes/camino-frances/stages.json"
stages = json.loads(urllib.request.urlopen(url).read())

for stage in stages["stages"]:
    name = stage["name"]["en"]
    km = stage["distanceKm"]
    interior = stage.get("interior", {}).get("theme", {}).get("en", "—")
    print(f"  Stage {stage['index']:2d}: {name:45s} {km:5.1f} km  [{interior}]")

Attribution

When using this data, include:

Contains information from Open Pilgrimages
(https://github.com/walktalkmeditate/open-pilgrimages),
made available under the ODbL.

Route data © OpenStreetMap contributors.
https://www.openstreetmap.org/copyright

Contributing

See the contributing guide for how to add routes, fix data, and contribute interior journey content.