Downloads v1

GET · POST · PATCH · DELETE https://api.trailsplits.com/v1

Key optional · 500 credits a day without one

Prebuilt regional packages, offline map extracts (PMTiles) and outdoor places packs, as signed links valid 24 hours. Credits are charged once per request; links need no key and support range requests. Request them in the API console or with your key.

Contract

What you get

Kinds of package:

  • a PMTiles extract of the basemap for a region, for an offline map in your app;
  • a places pack: TrailSplits' outdoor places per country, as GeoParquet, GeoPackage and GeoJSON.

Licence: every package carries LICENSE-AND-CREDITS.txt. The data is an ODbL 1.0 derivative of OpenStreetMap. What we sell is the service and the convenience, never exclusivity. A product made from a package credits "© OpenStreetMap contributors", and a database derived from it stays under ODbL.

Authentication

As watches-v1:

  • a secret API key (publishable keys get 403 scope_denied);
  • or the owner's JWT with ?project=prj_….

The download links themselves need no key. They are signed and expire.

Endpoints

Method and pathBodyAnswer
GET /v1/downloads/catalog—200 {packages: Package[]}: the finished packages, newest first
POST /v1/downloads{package: "<id>"}201 Download: credits charged once; links valid 24 h
GET /v1/downloads—200 {downloads: Download[], month: {bytes, limit_bytes}}, this UTC month
GET /v1/downloads/<package id>/<file>?dl=…&exp=…&sig=…—the file. Range requests work; the answer has an ETag, and the file's sha256 is in the catalog
{
  "id": "pmtiles_extract-alps-osm_weekly_20261004",
  "kind": "pmtiles_extract",
  "region": "alps",
  "bbox": [4.2, 43.0, 17.2, 48.9],
  "area_km2": 512340,
  "release": "osm_weekly_20261004",
  "source_ts": "2026-10-04T00:00:00Z",
  "max_zoom": 14,
  "formats": ["pmtiles"],
  "size_bytes": 1234568124,
  "files": [{ "name": "alps.pmtiles", "format": "pmtiles", "bytes": 1234567890, "sha256": "…" }],
  "license": "ODbL-1.0",
  "attribution": "© OpenStreetMap contributors, © OpenMapTiles",
  "credits": 537
}

Download

{
  "id": "dl_…",
  "package": "pmtiles_extract-alps-osm_weekly_20261004",
  "credits": 537,
  "bytes": 1234568124,
  "created_at": "…Z",
  "expires_at": "…Z",
  "links": [{ "file": "alps.pmtiles", "url": "https://api.trailsplits.com/v1/downloads/…?dl=…&exp=…&sig=…", "bytes": 1234567890, "sha256": "…" }]
}
  • Links stay valid until expires_at, 24 h after the download was made, for any number of range requests.
  • They are bound to the package and file, never to a key, so share them only as you would the file.
  • GET /v1/downloads lists this month's downloads without the links.

Credits and caps

Provisional: the owner sets pricing numbers. These follow the pricing target, which shows offline packs as an add-on.

  • PMTiles extract: 1 credit per 1,000 km² plus 1 per 100 MB.
  • Places pack: 10 credits plus 1 per 10 MB.
  • Charged once, at POST /v1/downloads, never per request: PMTiles clients read a file in many ranges.

Downloads per UTC month, in bytes:

FreeStarterProBusiness
2 GB25 GB250 GB1 TB

Free's 2 GB is sized for trying a pack, as the pricing target's "sample pack". The paid plans aren't on sale yet.

Errors

The shape is the one from api-keys-usage-v1.

StatuserrorWhen
400invalid_requestan unknown field, or no package
403scope_denieda publishable key
403download_link_invalida link with a wrong signature, or for another file
403download_link_expireda link past exp
429monthly_credits_exhaustedthe credits would pass the month's allowance
429download_cap (limit: "monthly_bytes")the bytes would pass the plan's monthly cap