Introduction

Farun API Reference

This is the complete reference for the Farun self-hosted server. It exposes vector tile serving, address and place search powered by Elasticsearch, and a full routing engine backed by Valhalla - covering turn-by-turn directions, isochrones, elevation profiles, time/distance matrices, optimised routes, and map matching.

Self-hosted. This server runs on your own infrastructure. Replace your-host throughout this reference with your actual server address or IP. Interactive API docs are available at http://your-host/docs (Swagger) and http://your-host/redoc.

Endpoint index

GET /health
Server health check
GET /sources
List all loaded tile sources
GET /tiles/{source}
TileJSON metadata
GET /tiles/{source}/{z}/{x}/{y}
Fetch vector tile (MVT/PBF)
POST /static
Look up pre-rendered static map (JPEG)
GET /search
Address / place / POI autocomplete
GET /search/reverse
Reverse geocode - coordinate → nearest address
GET /search/detail/{doc_id}
Full place record with GeoJSON geometry
GET /costings
List available transport modes
GET /costing/options/{costing}
Options schema for a transport mode
POST /route
Turn-by-turn directions
POST /isochrone
Reachability isochrone polygons
POST /elevation
Elevation profile along a path
POST /matrix
Many-to-many time/distance matrix
POST /optimised_route
Optimised stop order (TSP)
POST /map_match
Snap GPS trace to road network
POST /map_match/attributes
Map match with edge attributes
Setup

Base URL & Configuration

All endpoints are relative to the server root. No path prefix. Default port is 3000, configurable via the PORT environment variable.

HTTP
# Self-hosted Farun Tile + Routing Server
http://your-host          # replace with your actual host

# Default port (configurable via PORT env var)
http://your-host:3000

# FastAPI auto-generated interactive docs also available at:
http://your-host/docs     # Swagger UI
http://your-host/redoc    # ReDoc

Environment variables

PORT 3000
Server listen port
HOST 0.0.0.0
Server bind address
MBTILES_DIR ./mbtiles
Directory containing .mbtiles files
TILE_CACHE_MAX_AGE 86400
Tile Cache-Control max-age in seconds
STATIC_CACHE_DIR /app/static_cache
Directory of pre-rendered static map JPEGs
ES_URL http://localhost:9200
Elasticsearch URL for search
ES_INDEX sa_addresses
Elasticsearch index name
ES_TIMEOUT 5
Elasticsearch request timeout (seconds)
VALHALLA_URL http://localhost:8002
Valhalla routing engine URL
VALHALLA_TIMEOUT 30
Valhalla request timeout (seconds)
Errors

Status Codes

200
Success
204
Valid tile coordinate but no data at this location - treat as empty/transparent
400
Bad request - parameter out of valid range, or Valhalla routing error
404
Source, document, or pre-rendered image not found
422
Request body validation failed - required field missing or value out of range
500
Internal server error
503
Upstream service not reachable (Elasticsearch or Valhalla)
504
Valhalla routing engine timed out
Server
GET /health

Server liveness check. No parameters. Suitable for load-balancer health probes.

Shell
curl http://your-host/health

Response (200 OK)

status string
Always "ok" when server is running
uptime string
CPU process time since startup
JSON200 OK
{ "status": "ok", "uptime": "142s" }
GET /sources

Lists all .mbtiles files loaded from MBTILES_DIR, with metadata and tile URL templates.

Shell
curl http://your-host/sources

Source object fields

id string
Source ID - use in /tiles/{source} URLs. Derived from filename without .mbtiles
name string
Display name from MBTiles metadata
description string
Optional description from MBTiles metadata
format string
"pbf" (vector), "png", or "jpg" (raster)
minzoom integer
Minimum zoom level
maxzoom integer
Maximum zoom level
bounds string
"west,south,east,north" in WGS84. Null if not in metadata.
center string
"lon,lat,zoom" suggested center. Null if not in metadata.
tiles string
Tile URL template with {z}/{x}/{y} placeholders
tilejson string
URL to the TileJSON endpoint for this source
JSON200 OK
{
  "sources": [{
    "id":          "south_africa",
    "name":        "South Africa Streets",
    "description": "OSM extract",
    "format":      "pbf",
    "minzoom":     0,
    "maxzoom":     14,
    "bounds":      "16.3,-34.9,32.9,-22.1",
    "center":      "25.0,-29.0,6",
    "tiles":       "http://your-host/tiles/south_africa/{z}/{x}/{y}",
    "tilejson":    "http://your-host/tiles/south_africa"
  }]
}
Tiles
GET /tiles/{source}

Returns a TileJSON 3.0.0 document. The recommended way to integrate with MapLibre GL or Mapbox GL - pass this URL as the source url and the renderer auto-configures itself.

Path parameters

sourcestring · required
MBTiles source ID (see /sources). Returns 404 if not found.
Shell
curl http://your-host/tiles/south_africa

Additional response fields vs /sources

tilejson string
"3.0.0"
scheme string
"xyz" - always
attribution string
From MBTiles metadata. Default: © OpenStreetMap contributors
version string
From MBTiles metadata. Default: 1.0.0
vector_layers object[]
Layer descriptors from metadata JSON field. Empty array if absent.
GET /tiles/{source}/{z}/{x}/{y}

Fetches a single Mapbox Vector Tile (PBF). XYZ convention - TMS y-flip is handled internally. In-memory LRU cache of 200,000 tiles.

Path parameters

source string · required
MBTiles source ID
z integer · required
Zoom level. Range: 0 – 22
x integer · required
Tile column. Range: 0 – (2^z − 1)
y integer · required
Tile row (XYZ, top-left = 0). Range: 0 – (2^z − 1). Converted internally to TMS.
Shell
curl -o tile.pbf http://your-host/tiles/south_africa/10/567/432

Response headers (200)

Content-Type
application/x-protobuf
Content-Encoding
gzip - Only present when tile data is already gzip-compressed in MBTiles
Cache-Control
public, max-age=86400 - Controlled by TILE_CACHE_MAX_AGE env var
Access-Control-Allow-Origin
* - CORS open for all origins
Vary
Accept-Encoding

Status codes

200
Tile data in body
204
Valid coordinate but no data - empty tile, treat as transparent
400
z/x/y out of valid range
404
Source not found
POST /static

Looks up and returns a pre-rendered JPEG from the static cache directory. Cache lookup only - does not render on demand. Returns 404 if no matching image exists.

Cache key logic. The request coordinates are snapped to the nearest tile centre before computing the cache key. This means slightly different coordinates that fall within the same tile will resolve to the same cached image.

Request body (JSON)

center float[2] · required
[longitude, latitude]. Snapped to nearest tile centre for cache lookup.
zoom float · required
Zoom level. Range: 0 - 22.
tile_source string · required
Source ID - must match a loaded source.
width integer · optional
Output width in pixels. Range: 64 - 4096. Default: 800.
height integer · optional
Output height in pixels. Range: 64 - 4096. Default: 600.
bearing float · optional
Rotation degrees clockwise from north. Default: 0. Note: currently fixed to 0 in cache key.
Shell
curl -X POST http://your-host/static \
  -H 'Content-Type: application/json' \
  -d '{"center":[18.4241,-33.9249],"zoom":12,"tile_source":"south_africa"}' \
  --output map.jpg

Status codes

200
JPEG image in body. Header X-Cache: HIT confirms cache hit.
404
No pre-rendered image for these parameters
422
Validation error - missing field or value out of range
Search
GET /search/reverse

Finds the nearest addresses or places to a coordinate. Only documents within 2 km are considered. Results sorted by distance ascending.

Query parameters

lat float · required
Query latitude (WGS84)
lon float · required
Query longitude (WGS84)
size integer · optional
Max results. Range: 1 – 10. Default: 5.
types string · optional
Comma-separated doc_type filter: address, street, place, poi
Shell
curl 'http://your-host/search/reverse?lat=-33.9249&lon=18.4241&size=3&types=address'

Additional response field

distance_mfloat
Distance from query point in metres, rounded to 1 decimal place. Results sorted by this ascending.
JSON200 OK
{
  "lat": -33.9249, "lon": 18.4241, "took_ms": 5,
  "results": [{
    "id":           "xyz789",
    "doc_type":     "address",
    "display_name": "12 Bree Street, Cape Town, 8000",
    "distance_m":   34.2,
    "location":     { "lat": -33.9247, "lon": 18.4238 }
  }]
}
GET /search/detail/{doc_id}

Returns the full document for a single result ID - including the GeoJSON geometry and wikidata fields that are excluded from list responses. Call only when a user selects a result.

Path parameters

doc_idstring · required
Elasticsearch document ID from the id field in /search or /search/reverse.
Shell
curl http://your-host/search/detail/abc123

Additional fields beyond list result

geometry object | null
Full GeoJSON geometry. Null if not stored.
wikidata string | null
Wikidata QID e.g. Q5465. Null if unavailable.
JSON200 OK
{
  "id": "abc123",  "doc_type": "place",
  "display_name": "Cape Town, Western Cape, ZA",
  "location": { "lat": -33.9249, "lon": 18.4241 },
  "geometry": { "type": "Point", "coordinates": [18.4241, -33.9249] },
  "wikidata": "Q5465"
}
Routing - powered by Valhalla
GET /costings

Returns all available transport modes (costings) and their descriptions. Use these IDs as the costing field in routing requests.

Shell
curl http://your-host/costings
JSON200 OK
{
  "costings": [
    { "id": "auto",          "name": "Car",            "description": "Fastest car route" },
    { "id": "auto_shorter",  "name": "Car (shortest)", "description": "Shortest distance" },
    { "id": "truck",         "name": "Truck/HGV",     "description": "Heavy goods" },
    { "id": "bicycle",       "name": "Bicycle",       "description": "Cycling" },
    { "id": "pedestrian",    "name": "Walking",       "description": "On foot" },
    { "id": "motorcycle",    "name": "Motorcycle",    "description": "Motorbike" },
    { "id": "motor_scooter", "name": "Scooter",       "description": "Scooter" },
    { "id": "bus",           "name": "Bus",           "description": "Bus" },
    { "id": "taxi",          "name": "Taxi",          "description": "Taxi" }
  ]
}
GET /costing/options/{costing}

Returns the full JSON Schema of available options for a transport mode. Use the returned field names as keys in the costing_options body of routing requests.

Path parameters

costingstring · required
One of: auto, bicycle, pedestrian, truck, motorcycle, motor_scooter
Costing options are passed in routing requests as "costing_options": { "auto": { "use_highways": 0.5 } } - keyed by the costing mode name.
POST /route

Turn-by-turn directions between two or more waypoints. Returns route geometry, step instructions, distance, and duration. Supports up to 3 alternative routes.

Request body (JSON)

locations Location[] · required
Minimum 2. See Location Object below.
costing string · required
Transport mode. See /costings for full list. Default: auto
costing_options object · optional
Mode-specific tuning. See /costing/options/{costing} for schema.
directions_options object · optional
{ "units": "kilometers"|"miles", "language": "en-US", "directions_type": "none"|"maneuvers"|"instructions" }
alternates integer · optional
Number of alternative routes. Range: 1 – 3.
avoid_locations object[] · optional
Points to avoid: [{ lon, lat, radius? }]
avoid_polygons float[][][] · optional
GeoJSON-style polygon rings to avoid
shape_format string · optional
"polyline6" (default), "polyline5", or "geojson"
date_time object · optional
Departure/arrival time for time-dependent routing
elevation_interval float · optional
Sample elevation along the route at this interval (metres)

Location Object

lon float · required
Longitude
lat float · required
Latitude
type string · optional
"break" (default) | "through" | "via" | "break_through"
heading float · optional
Preferred approach heading 0–360°
heading_tolerance float · optional
Tolerance for heading match 0–180°
name string · optional
Location label for instructions
street string · optional
Preferred street name
waiting_secs integer · optional
Wait time at this stop (seconds)
search_radius integer · optional
Snap radius in metres. Range: 1–100.
preferred_side string · optional
"same" | "opposite" | "either"
Shell
curl -X POST http://your-host/route \
  -H 'Content-Type: application/json' \
  -d '{
    "locations": [
      {"lon": 18.4241, "lat": -33.9249},
      {"lon": 18.8602, "lat": -33.9321}
    ],
    "costing": "auto"
  }'
POST /isochrone

Generates reachability polygons from a single origin point. Contours can be time-based (minutes) or distance-based (kilometres) - not both in the same request. Returns GeoJSON FeatureCollection.

Request body (JSON)

locations Location[1] · required
Exactly one origin location.
costing string · required
Transport mode. Supported: auto, bicycle, pedestrian, truck, motorcycle, motor_scooter
contours object[] · required
Array of contour objects. Time: [{"time": 15}, {"time": 30}]. Distance: [{"distance": 5}]. Max 4 contours.
costing_options object · optional
Mode-specific tuning - same as /route
polygons boolean · optional
Return filled polygons (true, default) or contour lines only (false)
denoise float · optional
0.0 – 1.0. Remove small disconnected polygons. Default: not set (Valhalla default applies).
generalize float · optional
Simplification tolerance in metres. Default: not set.
show_locations boolean · optional
Include origin point in response GeoJSON. Default: true
Shell
# Time-based (minutes)
curl -X POST http://your-host/isochrone \
  -H 'Content-Type: application/json' \
  -d '{
    "locations": [{"lon": 18.4241, "lat": -33.9249}],
    "costing": "auto",
    "contours": [{"time": 15}, {"time": 30}, {"time": 45}],
    "polygons": true,
    "generalize": 50
  }'

# Distance-based (kilometres)
curl -X POST http://your-host/isochrone \
  -H 'Content-Type: application/json' \
  -d '{
    "locations": [{"lon": 18.4241, "lat": -33.9249}],
    "costing": "pedestrian",
    "contours": [{"distance": 2}, {"distance": 5}]
  }'
POST /elevation

Returns terrain elevation for a series of coordinates along a path. Input can be a coordinate array or an encoded polyline string.

Request body (JSON)

shape object[] · optional*
[{"lon":..., "lat":...}] - provide this or encoded_polyline
encoded_polyline string · optional*
Encoded polyline (polyline5 or polyline6 depending on shape_format)
range boolean · optional
Include cumulative distance alongside each elevation value. Default: false
height_precision integer · optional
Decimal precision of height values. Range: 0 – 8
resample_distance float · optional
Resample the path at this interval in metres before querying elevation
shape_format string · optional
"polyline6" or "polyline5" - applies when using encoded_polyline
Shell
# Shape as coordinate array
curl -X POST http://your-host/elevation \
  -H 'Content-Type: application/json' \
  -d '{
    "shape": [
      {"lon": 18.4241, "lat": -33.9249},
      {"lon": 18.5000, "lat": -33.9500},
      {"lon": 18.8602, "lat": -33.9321}
    ],
    "range": true
  }'

# Encoded polyline (polyline6)
curl -X POST http://your-host/elevation \
  -H 'Content-Type: application/json' \
  -d '{"encoded_polyline": "qp~IvskA...", "range": true}'
POST /matrix

Computes a many-to-many time and distance matrix between any number of source and target locations. Returns travel time and distance for every source→target pair.

Request body (JSON)

sources Location[] · required
One or more origin locations
targets Location[] · required
One or more destination locations
costing string · required
Transport mode. Supported: auto, bicycle, pedestrian, truck, motorcycle
costing_options object · optional
Mode-specific tuning
units string · optional
"kilometers" (default) or "miles"
Shell
curl -X POST http://your-host/matrix \
  -H 'Content-Type: application/json' \
  -d '{
    "sources": [{"lon": 18.4241, "lat": -33.9249}],
    "targets": [
      {"lon": 18.8602, "lat": -33.9321},
      {"lon": 18.5500, "lat": -33.9800}
    ],
    "costing": "auto",
    "units": "kilometers"
  }'
POST /optimised_route

Solves the Travelling Salesman Problem - finds the optimal visit order for a set of locations to minimise total route cost. Returns a full route with the reordered stops.

The first and last locations are fixed as start and end points. Intermediate stops are reordered by the solver.

Request body (JSON)

locations Location[] · required
Minimum 2. First and last are fixed; intermediate stops are reordered.
costing string · required
Transport mode. Supported: auto, bicycle, pedestrian, truck
costing_options object · optional
Mode-specific tuning
directions_options object · optional
units, language, directions_type
Shell
curl -X POST http://your-host/optimised_route \
  -H 'Content-Type: application/json' \
  -d '{
    "locations": [
      {"lon": 18.4241, "lat": -33.9249},
      {"lon": 18.8602, "lat": -33.9321},
      {"lon": 18.5500, "lat": -33.9800},
      {"lon": 18.6700, "lat": -34.0100}
    ],
    "costing": "auto"
  }'
POST /map_match

Snaps a raw GPS trace to the most likely path on the road network. Returns a cleaned route with turn-by-turn directions. Useful for post-processing recorded vehicle or device tracks.

Request body (JSON)

shape object[] · optional*
[{"lon":..., "lat":..., "time": 0}] - provide this or encoded_polyline. time is seconds from start.
encoded_polyline string · optional*
Encoded polyline of the GPS trace
costing string · required
Transport mode. Supported: auto, bicycle, pedestrian, bus, motorcycle
shape_match string · optional
"map_snap" (default) | "edge_walk" | "walk_or_snap"
costing_options object · optional
Mode-specific tuning
directions_options object · optional
units, language, directions_type
trace_options object · optional
Advanced matching tuning (interpolation_distance, search_radius, etc.)
units string · optional
"kilometers" (default) | "miles"
Shell
curl -X POST http://your-host/map_match \
  -H 'Content-Type: application/json' \
  -d '{
    "shape": [
      {"lon": 18.4241, "lat": -33.9249, "time": 0},
      {"lon": 18.5000, "lat": -33.9500, "time": 30},
      {"lon": 18.8602, "lat": -33.9321, "time": 90}
    ],
    "costing": "auto",
    "shape_match": "map_snap"
  }'
POST /map_match/attributes

Same input as /map_match but returns detailed edge-level attributes for each matched road segment - speed limits, surface type, road class, and more. Useful for road quality analysis and insurance telematics.

Accepts the same request body as /map_match.