Référence de l’API

Chaque endpoint avec ses paramètres, ce qu’il coûte dans votre offre, une requête à copier et une réponse réellement renvoyée par l’API. Les descriptions des endpoints sont en anglais.

L’essentiel

Base URL https://geo.runningcat.dev. Send your key as Authorization: Bearer <key> or x-api-key: <key>; keys come from https://geo.runningcat.dev/account. Coordinates are [lon, lat] in degrees (WGS 84), times ISO 8601 in UTC, distances in km or m as the field name says. Errors answer {"error": "...", "reason": "..."} with the status codes listed per operation.

Every answer says what it cost and what is left: X-Request-Cost, X-RateLimit-Limit and X-RateLimit-Remaining (per minute), X-RateLimit-Day-Limit and X-RateLimit-Day-Remaining.

Credits to show: map data © OpenStreetMap contributors (ODbL); weather and air quality from Open-Meteo.com (CC BY 4.0); terrain from Mapterhorn; air quality stations and river gauges as each answer's credits or the data sources page lists.

La même référence en fichier OpenAPI 3.1 : /openapi.json. Chargez-le dans Postman, Insomnia ou un générateur de code, ou donnez-le à un assistant IA.

Reports

Several answers about one point, area or route in one request.

GET /v1/point

Everything about a point. Place, time zone, elevation, weather, air quality, air quality stations and river gauges at a point in one request. include picks the parts (comma-separated: place, timezone, elevation, weather, air, airStations, rivers; all by default). A part that fails comes back in errors with the reason; the others still answer.

Coût: One request whatever the parts.

Paramètres

lonLongitude, −180 to 180
latLatitude, −90 to 90
include facultatifComma-separated parts: place, timezone, elevation, weather, air, airStations, rivers
lang facultatifLanguage of place names, e.g. en, de, pl; local names when absent
days facultatifForecast days, 1 to 14 (default 2)
hours facultatifAir quality hours, 1 to 120 (default 24)
pastHours facultatifPast hours of the air quality model before the current hour, 0 to 48 (default 0)
radiusKm facultatifSearch radius for stations and gauges, 1 to 100 km (default 10)

Requête

curl 'https://geo.runningcat.dev/v1/point?lon=6.1432&lat=46.2044&include=place,timezone,elevation&lang=en' \
  -H "Authorization: Bearer YOUR_KEY"

Exemple de réponse

{
 "lon": 6.1432,
 "lat": 46.2044,
 "place": {
  "lon": 6.1432,
  "lat": 46.2044,
  "country": {
   "code": "CH",
   "name": "Switzerland"
  },
  "region": {
   "name": "Geneva",
   "code": "CH-GE"
  },
  "locality": {
   "name": "Geneva",
   "type": "city",
   "distanceKm": 0.39
  },
  "nearestPlace": {
   "name": "Saint-Gervais",
   "type": "neighbourhood",
   "distanceKm": 0.26
  },
  "areas": [
   {
    "level": 2,
    "name": "Switzerland",
    "code": "CH"
   },
   {
    "level": 4,
    "name": "Geneva",
    "code": "CH-GE"
   },
   {
    "level": 8,
    "name": "Geneva"
   }
  ],
  "timezone": {
   "tz": "Europe/Zurich",
   "offsetMin": 60
  }
 },
 "timezone": {
  "tz": "Europe/Zurich",
  "offsetMin": 60
 },
 "elevation": {
  "ele": 374
 }
}

GET /v1/area

Everything over an area. Weather and air quality grids, elevation, air quality stations, river gauges and map layers over a box. include picks the parts (weather, air, airStations, elevation, rivers, layers; all but layers by default). Grids have about cells cells (at most 400) listed from the north-west corner row by row, hourly from the current hour; cells × hours at most 9600. Stations, gauges and layers need a box of at most 16 square degrees.

Coût: One request per part; layers one per layer.

Paramètres

bboxwest,south,east,north in degrees, at most 3600 square degrees
include facultatifComma-separated parts: weather, air, airStations, elevation, rivers, layers
cells facultatifGrid cells, 1 to 400 (default 100)
hours facultatifHours, 1 to 120 (default 24)
layers facultatifComma-separated layers: protected_area, route, piste, aerialway, boundary (default protected_area, route, piste, aerialway)

Requête

curl 'https://geo.runningcat.dev/v1/area?bbox=6.0,46.1,6.3,46.3&include=weather,elevation&cells=4&hours=2' \
  -H "Authorization: Bearer YOUR_KEY"

Exemple de réponse

{
 "bbox": [
  6,
  46.1,
  6.3,
  46.3
 ],
 "rows": 2,
 "cols": 2,
 "lon": [
  6.075,
  6.225
 ],
 "lat": [
  46.25,
  46.15
 ],
 "weather": {
  "times": [
   "2026-11-12T12:00:00Z",
   "2026-11-12T13:00:00Z"
  ],
  "values": {
   "temperatureC": [
    [
     11.6,
     12.3,
     12.2,
     12
    ],
    [
     11.8,
     12.3,
     12.3,
     12.2
    ]
   ],
   "feelsLikeC": [
    [
     10.4,
     11.4,
     10.9,
     11.5
    ],
    [
     10.1,
     11,
     11.1,
     11.6
    ]
   ],
   "precipitationMm": [
    [
     0.8,
     0.9,
     1,
     1.1
    ],
    [
     0.2,
     0.4,
     0.4,
     0.7
    ]
   ],
   "weatherCode": [
    [
     61,
     61,
     61,
     61
    ],
    [
     61,
     61,
     61,
     61
    ]
   ],
   "windKmh": [
    [
     9.4,
     9.2,
     11.7,
     6.6
    ],
    [
     12.8,
     12.1,
     12,
     8.4
    ]
   ],
   "windDirDeg": [
    [
     40,
     21,
     34,
     351
    ],
    [
     38,
     27,
     33,
     10
    ]
   ],
   "gustKmh": [
    [
     16.2,
     18.4,
     22.3,
     16.6
    ],
    [
     24.8,
     20.9,
     24.1,
     19.8
    ]
   ],
   "cloudPct": [
    [
     100,
     100,
     100,
     100
    ],
    [
     100,
     100,
     100,
     100
    ]
   ],
   "humidityPct": [
    [
     94,
     94,
     95,
     98
    ],
    [
     91,
     94,
     95,
     99
    ]
   ],
   "pressureHpa": [
    [
     992.9,
     992.9,
     992.9,
     993.1
    ],
    [
     992,
     992.1,
     992.1,
     992.2
    ]
   ],
   "boundaryLayerM": [
    [
     205,
     185,
     210,
     145
    ],
    [
     230,
     180,
     185,
     120
    ]
   ]
  }
 },
 "elevation": {
  "values": [
   453.2,
   435,
   424.3,
   526.6
  ]
 }
}

POST /v1/route/report

Everything along a route. Elevation profile, weather at the time you reach each point, time zones, air quality stations, river gauges, map layers, surfaces and land cover along a route in one request. include picks the parts (profile, weather, timezones, airStations, rivers, layers, surfaces, landcover; by default profile, weather, timezones, airStations, rivers, layers); the other fields are those of the single endpoints below. A route is GeoJSON {"type": "LineString", "coordinates": [[lon, lat], ...]} or a MultiLineString of sections in order, up to 20,000 km.

Coût: One request per part.

Corps

routeGeoJSON LineString or MultiLineString or GeoJSON LineString or MultiLineString
include facultatifarray of profile, weather, timezones, airStations, rivers, layers, surfaces, landcover, at least 1 items
departure facultatifstring (ISO time with offset)
speedKmh facultatifnumber, 2 to 80
stepKm facultatifnumber, 0.5 to 100
stepM facultatifnumber, 10 to 1000
layers facultatifarray of protected_area, route, piste, aerialway, boundary, at least 1 items
bufferM facultatifnumber, 0 to 1000
bufferKm facultatifnumber, 0.1 to 20
lang facultatifstring

Requête

curl -X POST 'https://geo.runningcat.dev/v1/route/report' \
  -H "Authorization: Bearer YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"route":{"type":"LineString","coordinates":[[6.1432,46.2044],[6.15,46.19],[6.175,46.17],[6.2,46.15]]},"include":["profile","timezones","surfaces","landcover"]}'

Exemple de réponse

{
 "profile": {
  "routeLengthKm": 7.567,
  "stepM": 10,
  "ascentM": 814,
  "descentM": 109,
  "minM": 374,
  "maxM": 1093.7,
  "structures": [
   {
    "fromKm": 0.139,
    "toKm": 0.178,
    "kind": "tunnel",
    "name": "Passage de la Petite-Corraterie",
    "maxheight": null,
    "maxheightM": null
   },
   {
    "fromKm": 1.05,
    "toKm": 1.07,
    "kind": "tunnel",
    "name": null,
    "maxheight": "default",
    "maxheightM": null
   },
   {
    "fromKm": 3.89,
    "toKm": 3.91,
    "kind": "bridge",
    "name": null,
    "maxheight": null,
    "maxheightM": null
   }
  ],
  "profile": [
   {
    "km": 0,
    "lon": 6.1432,
    "lat": 46.2044,
    "ele": 374
   },
   {
    "km": 0.01,
    "lon": 6.14324,
    "lat": 46.204315,
    "ele": 374.4
   },
   {
    "km": 0.02,
    "lon": 6.143281,
    "lat": 46.204229,
    "ele": 374.5
   }
  ]
 },
 "timezones": {
  "at": "2026-11-12T12:35:36.495Z",
  "routeLengthKm": 7.6,
  "timezones": [
   {
    "tz": "Europe/Zurich",
    "fromKm": 0,
    "toKm": 5.6,
    "offsetMin": 60
   },
   {
    "tz": "Europe/Paris",
    "fromKm": 5.6,
    "toKm": 7.6,
    "offsetMin": 60
   }
  ]
 },
 "surfaces": {
  "km": {
   "paved": 2.76,
   "unpaved": 0.32,
   "unknown": 4.49
  },
  "highwayKm": {
   "none": 1.95,
   "footway": 1.53,
   "service": 1.25,
   "path": 0.7,
   "residential": 0.54,
   "tertiary": 0.44,
   "track": 0.32,
   "cycleway": 0.2,
   "living_street": 0.18,
   "unclassified": 0.16,
   "pedestrian": 0.14,
   "secondary": 0.06,
   "primary": 0.04,
   "motorway": 0.04,
   "secondary_link": 0.02,
   "platform": 0.02
  },
  "runs": [
   {
    "fromKm": 0,
    "toKm": 0.02,
    "surface": "asphalt",
    "highway": "residential",
    "tracktype": null,
    "smoothness": null
   },
   {
    "fromKm": 0.02,
    "toKm": 0.04,

…

Places and time zones

What lies at a point and what time it is there.

GET /v1/where

Where a point is. Country, region, locality, the nearest named place, protected areas and the time zone at a point.

Coût: One request.

Paramètres

lonLongitude, −180 to 180
latLatitude, −90 to 90
lang facultatifLanguage of place names, e.g. en, de, pl; local names when absent

Requête

curl 'https://geo.runningcat.dev/v1/where?lon=6.1432&lat=46.2044&lang=en' \
  -H "Authorization: Bearer YOUR_KEY"

Exemple de réponse

{
 "lon": 6.1432,
 "lat": 46.2044,
 "country": {
  "code": "CH",
  "name": "Switzerland"
 },
 "region": {
  "name": "Geneva",
  "code": "CH-GE"
 },
 "locality": {
  "name": "Geneva",
  "type": "city",
  "distanceKm": 0.39
 },
 "nearestPlace": {
  "name": "Saint-Gervais",
  "type": "neighbourhood",
  "distanceKm": 0.26
 },
 "areas": [
  {
   "level": 2,
   "name": "Switzerland",
   "code": "CH"
  },
  {
   "level": 4,
   "name": "Geneva",
   "code": "CH-GE"
  },
  {
   "level": 8,
   "name": "Geneva"
  }
 ],
 "timezone": {
  "tz": "Europe/Zurich",
  "offsetMin": 60
 }
}

POST /v1/where

Where many points are. The same for up to 1000 points, in order. at (ISO time) sets the moment for the time zone offsets; now by default.

Coût: One request.

Corps

pointsarray of [lon, lat], 1 to 1000 items
lang facultatifstring
at facultatifstring (ISO time with offset)

Requête

curl -X POST 'https://geo.runningcat.dev/v1/where' \
  -H "Authorization: Bearer YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"points":[[6.1432,46.2044],[19.94,50.06]],"lang":"en"}'

Exemple de réponse

{
 "places": [
  {
   "lon": 6.1432,
   "lat": 46.2044,
   "country": {
    "code": "CH",
    "name": "Switzerland"
   },
   "region": {
    "name": "Geneva",
    "code": "CH-GE"
   },
   "locality": {
    "name": "Geneva",
    "type": "city",
    "distanceKm": 0.39
   },
   "nearestPlace": {
    "name": "Saint-Gervais",
    "type": "neighbourhood",
    "distanceKm": 0.26
   },
   "areas": [
    {
     "level": 2,
     "name": "Switzerland",
     "code": "CH"
    },
    {
     "level": 4,
     "name": "Geneva",
     "code": "CH-GE"
    },
    {
     "level": 8,
     "name": "Geneva"
    }
   ],
   "timezone": {
    "tz": "Europe/Zurich",
    "offsetMin": 60
   }
  },
  {
   "lon": 19.94,
   "lat": 50.06,
   "country": {
    "code": "PL",
    "name": "Poland"
   },
   "region": {
    "name": "Lesser Poland Voivodeship",
    "code": "PL-12"
   },
   "locality": {
    "name": "Krakow",
    "type": "city",
    "distanceKm": 0.31
   },
   "nearestPlace": {
    "name": "Krakow",
    "type": "city",
    "distanceKm": 0.31
   },
   "areas": [
    {
     "level": 2,
     "name": "Poland",
     "code": "PL"
    },
    {
     "level": 4,
     "name": "Lesser Poland Voivodeship",
     "code": "PL-12"
    },
    {
     "level": 6,
     "name": "Krakow"
    }
   ],
   "timezone": {
    "tz": "Europe/Warsaw",
    "offsetMin": 60
   }
  }
 ]
}

POST /v1/timezones

Time zones of points. IANA time zone and its offset in minutes east of UTC for up to 10,000 points, at at (now by default).

Coût: One request.

Corps

pointsarray of [lon, lat], 1 to 10000 items
at facultatifstring (ISO time with offset)

Requête

curl -X POST 'https://geo.runningcat.dev/v1/timezones' \
  -H "Authorization: Bearer YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"points":[[6.1432,46.2044],[-73.98,40.75]]}'

Exemple de réponse

{
 "timezones": [
  {
   "tz": "Europe/Zurich",
   "offsetMin": 60
  },
  {
   "tz": "America/New_York",
   "offsetMin": -300
  }
 ]
}

POST /v1/route/timezones

Time zones along a route. The stretches of a route in each time zone, with their offsets at at. A route is GeoJSON {"type": "LineString", "coordinates": [[lon, lat], ...]} or a MultiLineString of sections in order, up to 20,000 km.

Coût: One request.

Corps

routeGeoJSON LineString or MultiLineString or GeoJSON LineString or MultiLineString
at facultatifstring (ISO time with offset)

Requête

curl -X POST 'https://geo.runningcat.dev/v1/route/timezones' \
  -H "Authorization: Bearer YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"route":{"type":"LineString","coordinates":[[6.1432,46.2044],[6.15,46.19],[6.175,46.17],[6.2,46.15]]}}'

Exemple de réponse

{
 "routeLengthKm": 7.6,
 "at": "2026-11-12T12:35:38.786Z",
 "timezones": [
  {
   "tz": "Europe/Zurich",
   "fromKm": 0,
   "toKm": 5.6,
   "offsetMin": 60
  },
  {
   "tz": "Europe/Paris",
   "fromKm": 5.6,
   "toKm": 7.6,
   "offsetMin": 60
  }
 ]
}

POST /v1/places/search

Find places by name. Villages, towns, cities and other named places matching a query: exact names first, then names starting with the query, larger and more populous first. Each name comes as "Place, Region, Country", in locale where the place has it. Place names only, no street addresses.

Coût: One request.

Corps

querystring, 2 to 120 characters
limit facultatifinteger, 1 to 10 (default 6)
locale facultatifstring

Requête

curl -X POST 'https://geo.runningcat.dev/v1/places/search' \
  -H "Authorization: Bearer YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query":"Chamonix","limit":2,"locale":"en"}'

Exemple de réponse

{
 "places": [
  {
   "name": "Chamonix, Auvergne-Rhône-Alpes, France",
   "lat": 46.0295495,
   "lon": 6.6117818
  },
  {
   "name": "Chamonix-Mont-Blanc, Auvergne-Rhône-Alpes, France",
   "lat": 45.9230969,
   "lon": 6.8700009
  }
 ]
}

Elevation and terrain

Heights, profiles and terrain tiles.

GET /v1/elevation

Height of a point. Height above sea level in metres (0.1 m steps); open sea is 0.

Coût: One request.

Paramètres

lonLongitude, −180 to 180
latLatitude, −90 to 90

Requête

curl 'https://geo.runningcat.dev/v1/elevation?lon=7.6586&lat=45.9763' \
  -H "Authorization: Bearer YOUR_KEY"

Exemple de réponse

{
 "ele": 4449.1
}

POST /v1/elevation

Heights of points. Heights of the ground for up to 10,000 points, in order; null where there is no terrain. Each point is read on its own, so a point in a tunnel gets the mountain above it and one on a bridge the valley below; for a route or a recorded track use POST /v1/route/profile (with at: "points" to keep its own points), which follows the road there.

Coût: One request.

Corps

pointsarray of [lon, lat], 1 to 10000 items

Requête

curl -X POST 'https://geo.runningcat.dev/v1/elevation' \
  -H "Authorization: Bearer YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"points":[[7.6586,45.9763],[6.1432,46.2044]]}'

Exemple de réponse

{
 "elevations": [
  4449.1,
  374
 ]
}

POST /v1/route/profile

Elevation profile of a route. Heights every stepM metres (10 to 1000; chosen by length when absent), with climb, descent, lowest and highest point. A profile has at most 5,000 samples, so on a long route the step grows past the one asked for (10 m holds up to about 50 km, 20 m up to 100 km); the answer's stepM is the step used. at: "points" gives a height at each point of the route as sent instead, for recorded tracks: no resampling, distances stay the track's, up to 50,000 points (more answers 413), stepM null in the answer. On bridges and in tunnels (OSM bridge and tunnel ways the route runs along) the heights follow the road: a straight line between the heights just beyond each end, not the valley under a bridge or the mountain over a tunnel. A recorded track whose points stray from a tunnel or bridge where GPS was lost still counts as on it within 80 m, as long as no other road is nearer than 25 m. structures lists those stretches (fromKm, toKm, kind bridge or tunnel, name, and the clearance: maxheight as tagged in OSM, maxheightM in metres when it is a number, null when untagged; default means no sign and room for the country's general limit, below_default no sign and less room than that); it is null when they could not be looked up and the heights are the terrain's. heights: "terrain" gives the ground under every point instead. For a recorded track send it here with at: "points" rather than to POST /v1/elevation, which reads each point on its own and so cannot follow a road through a tunnel. A route is GeoJSON {"type": "LineString", "coordinates": [[lon, lat], ...]} or a MultiLineString of sections in order, up to 20,000 km.

Coût: One request.

Corps

routeGeoJSON LineString or MultiLineString or GeoJSON LineString or MultiLineString
stepM facultatifnumber, 10 to 1000
at facultatifstep, points
heights facultatifroad, terrain

Requête

curl -X POST 'https://geo.runningcat.dev/v1/route/profile' \
  -H "Authorization: Bearer YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"route":{"type":"LineString","coordinates":[[6.1432,46.2044],[6.15,46.19],[6.175,46.17],[6.2,46.15]]},"stepM":500}'

Exemple de réponse

{
 "routeLengthKm": 7.567,
 "stepM": 500,
 "ascentM": 733,
 "descentM": 31,
 "minM": 375.1,
 "maxM": 1090.8,
 "structures": [
  {
   "fromKm": 0.139,
   "toKm": 0.178,
   "kind": "tunnel",
   "name": "Passage de la Petite-Corraterie",
   "maxheight": null,
   "maxheightM": null
  },
  {
   "fromKm": 1.05,
   "toKm": 1.07,
   "kind": "tunnel",
   "name": null,
   "maxheight": "default",
   "maxheightM": null
  },
  {
   "fromKm": 3.89,
   "toKm": 3.91,
   "kind": "bridge",
   "name": null,
   "maxheight": null,
   "maxheightM": null
  }
 ],
 "profile": [
  {
   "km": 0,
   "lon": 6.1432,
   "lat": 46.2044,
   "ele": 375.1
  },
  {
   "km": 0.5,
   "lon": 6.145218,
   "lat": 46.200126,
   "ele": 383.6
  },
  {
   "km": 1,
   "lon": 6.147237,
   "lat": 46.195852,
   "ele": 385.8
  }
 ]
}

GET /v1/dem/{z}/{x}/{y}.png

Terrain tiles (Terrarium). Terrain tiles for hillshading, 3D terrain and contours, in Terrarium encoding: height in metres = R × 256 + G + B / 256 − 32768. {y}.png is 256 px, zoom 0 to 13; {y}.webp is 512 px lossless, zoom 0 to 12. All land is covered; there is no zoom 14, so a contour layer drawn at 14 sets maxzoom 13 on its source. 404 where there is no tile. Credit: terrain from Mapterhorn.

Coût: One request per 50 tiles.

Paramètres

zZoom
xTile column
yTile row

Requête

curl 'https://geo.runningcat.dev/v1/dem/12/2117/1446.png' \
  -H "Authorization: Bearer YOUR_KEY"

Exemple de réponse: image/png or image/webp

Map layers

Protected areas, trails, pistes, lifts, boundaries and land cover.

GET /v1/layers/{layer}

A map layer in a box. Protected areas, hiking and cycling routes, ski pistes, lifts or administrative boundaries in a box, as a GeoJSON FeatureCollection with OSM tags; truncated is true when limit cut it. Layers: protected_area, route, piste, aerialway, boundary. kinds narrows a layer (route: hiking, bicycle, mtb, ...; boundary: admin levels such as 2, 4).

Coût: One request.

Paramètres

layerprotected_area, route, piste, aerialway, boundary
bboxwest,south,east,north in degrees, at most 16 square degrees
kinds facultatifComma-separated kinds within the layer
limit facultatifFeatures, 1 to 5000 (default 2000)
lang facultatifLanguage of place names, e.g. en, de, pl; local names when absent

Requête

curl 'https://geo.runningcat.dev/v1/layers/protected_area?bbox=6.0,46.1,6.3,46.3&limit=2&lang=en' \
  -H "Authorization: Bearer YOUR_KEY"

Exemple de réponse

{
 "type": "FeatureCollection",
 "truncated": true,
 "features": [
  {
   "type": "Feature",
   "id": "R5555535",
   "properties": {
    "layer": "protected_area",
    "kind": "protected_area",
    "name": "Parc naturel régional du Haut-Jura",
    "tags": {
     "name": "Parc naturel régional du Haut-Jura",
     "name:br": "Park natur rannvroel ar Jura-Uhel",
     "name:de": "Regionaler Naturpark Hochjura",
     "name:en": "Jura Mountains Regional Natural Park",
     "name:fr": "Parc naturel régional du Haut-Jura",
     "website": "https://www.parc-haut-jura.fr/",
     "boundary": "protected_area",
     "wikidata": "Q3364633",
     "wikipedia": "fr:Parc naturel régional du Haut-Jura",
     "iucn_level": "5",
     "protect_class": "5",
     "protection_title": "parc naturel régional"
    }
   },
   "geometry": {
    "type": "Polygon",
    "coordinates": [
     [
      [
       6.065334,
       46.3
      ],
      [
       6.064507,
       46.298717
      ],
      [
       6.063409,
       46.297955
      ]
     ]
    ]
   }
  },
  {
   "type": "Feature",
   "id": "R14980670",
   "properties": {
    "layer": "protected_area",
    "kind": "protected_area",
    "name": "Réserve naturelle de la haute chaîne du Jura",
    "tags": {
     "name": "Réserve naturelle de la haute chaîne du Jura",
     "access": "restricted",
     "leisure": "nature_reserve",
     "name:it": "Riserva naturale dell'Alta Catena del Giura",
     "website": "https://www.rnn-hautechainedujura.fr/",
     "boundary": "protected_area",
     "wikidata": "Q3457394",
     "wikipedia": "fr:Réserve naturelle nationale de la haute chaîne du Jura",
     "protect_class": "4",
     "protection_title": "Réserve naturelle nationale"
    }
   },
   "geometry": {
    "type": "Polygon",
    "coordinates": [
     [
     
…

GET /v1/layers/landcover

Land cover in a box. What the ground is, from OpenStreetMap areas: forest, scrub, grassland, farmland, wetland, water, glacier, bare, park, built (kind). A GeoJSON FeatureCollection, largest areas first, clipped to the box; the box is at most 0.1 square degrees because forests and fields come by the thousand. kinds keeps some classes.

Coût: One request.

Paramètres

bboxwest,south,east,north, at most 0.1 square degrees
kinds facultatifComma-separated classes: forest, scrub, grassland, farmland, wetland, water, glacier, bare, park, built
limit facultatifFeatures, 1 to 5000 (default 2000)

Requête

curl 'https://geo.runningcat.dev/v1/layers/landcover?bbox=6.10,46.15,6.30,46.30&limit=2' \
  -H "Authorization: Bearer YOUR_KEY"

Exemple de réponse

{
 "type": "FeatureCollection",
 "truncated": true,
 "features": [
  {
   "type": "Feature",
   "id": "R332617",
   "properties": {
    "layer": "landcover",
    "kind": "water",
    "name": "Le Léman",
    "tags": {
     "ele": "372",
     "boat": "yes",
     "name": "Le Léman",
     "salt": "no",
     "tidal": "no",
     "water": "lake",
     "name:ca": "Llac Léman",
     "name:cs": "Ženevské jezero",
     "name:da": "Lemansøen",
     "name:de": "Genfersee",
     "name:en": "Lake Geneva",
     "name:es": "Lago Lemán",
     "name:et": "Genfi järv",
     "name:fr": "Le Léman",
     "name:he": "אגם ז'נבה",
     "name:hr": "Ženevsko jezero",
     "name:hu": "Genfi-tó",
     "name:is": "Genfarvatn",
     "name:it": "Lago Lemano",
     "name:ja": "レマン湖",
     "name:lb": "Genferséia",
     "name:nl": "Meer van Genève",
     "name:pl": "Jezioro Genewskie",
     "name:ru": "Женевское озеро",
     "name:sk": "Ženevské jazero",
     "name:sl": "Ženevsko jezero",
     "name:sr": "Женевско језеро",
     "name:tt": "Женева күле",
     "name:uk": "Женевське озеро",
     "natural": "water",
     "alt_name": "Lac Léman;Lac de Genève",
     "name:frp": "Lèman",
     "swimming": "yes",
     "wikidata": "Q6403",
     "ref:gwlnr": "CH0097570000",
     "wikipedia": "fr:Léman",
     "ref:gewiss": "9757",
     "short_name": "Léman",
     "alt_name:da": "Genevesøen",
     "alt_name:de": "Genfer See",
     "alt_name:en": "Lake Leman",
     "alt_name:es": "Lago de Ginebra",
     "alt_name:et": "Leman järv",
     "alt_name:fr": "Lac Léman;Lac de Genève",
     "alt_name:hr": "Lemansko jezero",
     "alt_name:hu": "Leman-tó",
     "alt_name:it": "Lago di Ginevra",
     "alt_name:lb": "Lemanerséi",
     "alt_name:sr": "Леманско језеро",
     "intermittent": "no",
     "name:sr-Latn": "Ženevsko jeze
…

POST /v1/route/landcover

Land cover along a route. What the ground is along a route, every 50 m (coarser over 250 km): kilometres per class and the stretches of each, in route order. A point takes the class of the smallest area it lies in (a wood inside a park is forest); unmapped where OpenStreetMap maps no area (often roads, villages and fields nobody drew). A route is GeoJSON {"type": "LineString", "coordinates": [[lon, lat], ...]} or a MultiLineString of sections in order, up to 20,000 km.

Coût: One request.

Corps

routeGeoJSON LineString or MultiLineString or GeoJSON LineString or MultiLineString

Requête

curl -X POST 'https://geo.runningcat.dev/v1/route/landcover' \
  -H "Authorization: Bearer YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"route":{"type":"LineString","coordinates":[[6.1432,46.2044],[6.15,46.19],[6.175,46.17],[6.2,46.15]]}}'

Exemple de réponse

{
 "routeLengthKm": 7.57,
 "stepM": 50,
 "km": {
  "built": 3.1,
  "park": 1.7,
  "forest": 1.07,
  "grassland": 0.85,
  "unmapped": 0.65,
  "farmland": 0.15,
  "water": 0.05
 },
 "runs": [
  {
   "fromKm": 0,
   "toKm": 0.25,
   "class": "built"
  },
  {
   "fromKm": 0.25,
   "toKm": 0.3,
   "class": "park"
  },
  {
   "fromKm": 0.3,
   "toKm": 0.35,
   "class": "built"
  }
 ]
}

POST /v1/route/layers

Map layers along a route. The layer features a route runs through or within bufferM (0 to 1000, default 30) of, with the stretches of the route inside each. Layers protected_area, route, piste, aerialway by default. A route is GeoJSON {"type": "LineString", "coordinates": [[lon, lat], ...]} or a MultiLineString of sections in order, up to 20,000 km.

Coût: One request.

Corps

routeGeoJSON LineString or MultiLineString or GeoJSON LineString or MultiLineString
layers facultatifarray of protected_area, route, piste, aerialway, boundary, at least 1 items
bufferM facultatifnumber, 0 to 1000
lang facultatifstring

Requête

curl -X POST 'https://geo.runningcat.dev/v1/route/layers' \
  -H "Authorization: Bearer YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"route":{"type":"LineString","coordinates":[[6.1432,46.2044],[6.15,46.19],[6.175,46.17],[6.2,46.15]]}}'

Exemple de réponse

{
 "routeLengthKm": 7.57,
 "stepM": 50,
 "bufferM": 30,
 "features": [
  {
   "layer": "route",
   "id": "R11063902",
   "kind": "hiking",
   "name": "ViaJacobi",
   "tags": {
    "to": "Genève (frontière)",
    "ref": "4",
    "from": "Genève",
    "name": "ViaJacobi",
    "route": "hiking",
    "network": "nwn",
    "distance": "9",
    "operator": "Wanderland Schweiz",
    "osmc:symbol": "green:green::4:white"
   },
   "stretches": [
    {
     "fromKm": 0,
     "toKm": 0.2
    },
    {
     "fromKm": 0.75,
     "toKm": 0.75
    }
   ],
   "km": 0.2
  },
  {
   "layer": "route",
   "id": "R1307899",
   "kind": "bicycle",
   "name": "Cyclévasion Genève-Salève",
   "tags": {
    "to": "Pas-de-l'Échelle",
    "ref": "104",
    "from": "Genève",
    "name": "Cyclévasion Genève-Salève",
    "route": "bicycle",
    "name:fr": "Cyclévasion Genève-Salève",
    "network": "lcn",
    "distance": "7",
    "operator": "Etat de Genève et Ville de Genève"
   },
   "stretches": [
    {
     "fromKm": 0,
     "toKm": 0
    },
    {
     "fromKm": 0.5,
     "toKm": 0.6
    }
   ],
   "km": 0.1
  },
  {
   "layer": "route",
   "id": "R1230605",
   "kind": "hiking",
   "name": "Eaux d'Arve - Pinchat",
   "tags": {
    "name": "Eaux d'Arve - Pinchat",
    "route": "hiking",
    "network": "lwn",
    "osmc:symbol": "yellow::yellow_diamond"
   },
   "stretches": [
    {
     "fromKm": 2.95,
     "toKm": 3
    }
   ],
   "km": 0.05
  }
 ]
}

Weather

Forecasts for a point, an area and a route on the way.

GET /v1/weather

Forecast for a point. Current conditions, hourly and daily forecast for up to 14 days (default 7), corrected to the height of the point; the local time zone comes along. Hours carry temperature, feels-like, precipitation, weather code, wind, gusts, cloud cover, humidity, sea-level pressure (pressureHpa) and the height of the boundary layer (boundaryLayerM, from the global GFS model everywhere, about 13 km cells). Units: °C, mm, km/h, degrees, percent, hPa, m; times in UTC.

Coût: One request.

Paramètres

lonLongitude, −180 to 180
latLatitude, −90 to 90
days facultatif1 to 14 (default 7)

Requête

curl 'https://geo.runningcat.dev/v1/weather?lon=6.1432&lat=46.2044&days=2' \
  -H "Authorization: Bearer YOUR_KEY"

Exemple de réponse

{
 "lon": 6.1432,
 "lat": 46.2044,
 "elevationM": 374,
 "current": {
  "time": "2026-11-12T12:30:00Z",
  "temperatureC": 12.7,
  "feelsLikeC": 11.7,
  "precipitationMm": 0.1,
  "weatherCode": 61,
  "windKmh": 9.8,
  "windDirDeg": 36,
  "gustKmh": 23,
  "cloudPct": 100,
  "humidityPct": 93,
  "pressureHpa": 992.5,
  "boundaryLayerM": 205,
  "isDay": true
 },
 "hourly": [
  {
   "time": "2026-11-11T23:00:00Z",
   "temperatureC": 10.1,
   "feelsLikeC": 9.7,
   "precipitationMm": 0,
   "weatherCode": 3,
   "windKmh": 2.6,
   "windDirDeg": 164,
   "gustKmh": 5.4,
   "cloudPct": 100,
   "humidityPct": 97,
   "pressureHpa": 1002.4,
   "boundaryLayerM": 10,
   "isDay": false
  },
  {
   "time": "2026-11-12T00:00:00Z",
   "temperatureC": 9.9,
   "feelsLikeC": 9.3,
   "precipitationMm": 0,
   "weatherCode": 3,
   "windKmh": 2.9,
   "windDirDeg": 150,
   "gustKmh": 4.3,
   "cloudPct": 100,
   "humidityPct": 97,
   "pressureHpa": 1001.7,
   "boundaryLayerM": 10,
   "isDay": false
  },
  {
   "time": "2026-11-12T01:00:00Z",
   "temperatureC": 10.2,
   "feelsLikeC": 9.8,
   "precipitationMm": 0,
   "weatherCode": 3,
   "windKmh": 1.6,
   "windDirDeg": 117,
   "gustKmh": 5.4,
   "cloudPct": 100,
   "humidityPct": 95,
   "pressureHpa": 1000.8,
   "boundaryLayerM": 10,
   "isDay": false
  }
 ],
 "daily": [
  {
   "date": "2026-11-12",
   "minC": 9.9,
   "maxC": 12.7,
   "precipitationMm": 15.7,
   "windMaxKmh": 10.8,
   "gustMaxKmh": 23,
   "weatherCode": 61,
   "sunrise": "2026-11-12T06:32:07Z",
   "sunset": "2026-11-12T16:06:42Z"
  },
  {
   "date": "2026-11-13",
   "minC": 3.9,
   "maxC": 11.5,
   "precipitationMm": 1.3,
   "windMaxKmh": 25.2,
   "gustMaxKmh": 57.2,
   "weatherCode": 61,
   "sunrise": "2026-11-13T06:33:33Z",
   "sunset": "2026-11-13T16:05:32Z"
  }
 ],
 "timezone": {

…

GET /v1/weather/area

Forecast grid over an area. Forecast on a grid of about cells cells (at most 400), listed from the north-west corner row by row: values.<name>[step][cell], with times for the steps. hours steps come back, stepHours apart, from pastHours before the current hour. cells × steps at most 9600. Keys given the bulk_grid permission (on request) may ask for up to 20,000 cells and 250,000 values; such grids are built one at a time behind other requests, so they take longer.

Coût: One request.

Paramètres

bboxwest,south,east,north in degrees, at most 3600 square degrees
cells facultatif1 to 400 (default 100)
stepDeg facultatifInstead of cells: a lattice with cell centres at west + k·stepDeg and north − k·stepDeg, 0.01 to 10 degrees, so the cells can sit on a model's own nodes (CAMS Europe: every 0.1° at x.x5°, CAMS Global: every 0.4°)
hours facultatifHow many steps come back, 1 to 120 (default 24)
stepHours facultatifHours between two steps, 1 to 6 (default 1)
pastHours facultatifStart this many hours before the current hour, 0 to 48 (default 0); the window may span up to 168 hours

Requête

curl 'https://geo.runningcat.dev/v1/weather/area?bbox=6.0,46.1,6.3,46.3&cells=4&hours=2' \
  -H "Authorization: Bearer YOUR_KEY"

Exemple de réponse

{
 "bbox": [
  6,
  46.1,
  6.3,
  46.3
 ],
 "rows": 2,
 "cols": 2,
 "lon": [
  6.075,
  6.225
 ],
 "lat": [
  46.25,
  46.15
 ],
 "times": [
  "2026-11-12T12:00:00Z",
  "2026-11-12T15:00:00Z"
 ],
 "values": {
  "temperatureC": [
   [
    11.6,
    12.3,
    12.2,
    12
   ],
   [
    11.4,
    12.1,
    12.1,
    11.9
   ]
  ],
  "feelsLikeC": [
   [
    10.4,
    11.4,
    10.9,
    11.5
   ],
   [
    10.5,
    10.9,
    11.1,
    10.9
   ]
  ],
  "precipitationMm": [
   [
    0.8,
    0.9,
    1,
    1.1
   ],
   [
    1.2,
    1.1,
    1.1,
    1.4
   ]
  ],
  "weatherCode": [
   [
    61,
    61,
    61,
    61
   ],
   [
    61,
    61,
    61,
    61
   ]
  ],
  "windKmh": [
   [
    9.4,
    9.2,
    11.7,
    6.6
   ],
   [
    6.9,
    10.8,
    9.9,
    9.1
   ]
  ],
  "windDirDeg": [
   [
    40,
    21,
    34,
    351
   ],
   [
    43,
    26,
    19,
    342
   ]
  ],
  "gustKmh": [
   [
    16.2,
    18.4,
    22.3,
    16.6
   ],
   [
    19.4,
    21.2,
    19.1,
    19.8
   ]
  ],
  "cloudPct": [
   [
    100,
    100,
    100,
    100
   ],
   [
    100,
    100,
    100,
    100
   ]
  ],
  "humidityPct": [
   [
    94,
    94,
    95,
    98
   ],
   [
    93,
    94,
    96,
    96
   ]
  ],
  "pressureHpa": [
   [
    992.9,
    992.9,
    992.9,
    993.1
   ],
   [
    991.7,
    991.5,
    991.7,
    991.7
   ]
  ],
  "boundaryLayerM": [
   [
    205,
    185,
    210,
    145
   ],
   [
    200,
    120,
    130,
    100
   ]
  ]
 }
}

POST /v1/weather/current

Current weather at many points. Current conditions at up to 2,000 points in one request, in order (for example a wind field for a map): the same fields as current in /v1/weather, the boundary layer height included. Over 400 points the request waits its turn behind other large requests.

Coût: One request.

Corps

pointsarray of [lon, lat], 1 to 2000 items

Requête

curl -X POST 'https://geo.runningcat.dev/v1/weather/current' \
  -H "Authorization: Bearer YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"points":[[6.1432,46.2044],[19.94,50.06]]}'

Exemple de réponse

{
 "points": [
  {
   "lon": 6.1432,
   "lat": 46.2044,
   "current": {
    "time": "2026-11-12T12:30:00Z",
    "temperatureC": 12.7,
    "feelsLikeC": 11.7,
    "precipitationMm": 0.1,
    "weatherCode": 61,
    "windKmh": 9.8,
    "windDirDeg": 36,
    "gustKmh": 23,
    "cloudPct": 100,
    "humidityPct": 93,
    "pressureHpa": 992.5,
    "boundaryLayerM": 205,
    "isDay": true
   }
  },
  {
   "lon": 19.94,
   "lat": 50.06,
   "current": {
    "time": "2026-11-12T12:30:00Z",
    "temperatureC": 8,
    "feelsLikeC": 7,
    "precipitationMm": 0,
    "weatherCode": 2,
    "windKmh": 1.1,
    "windDirDeg": 162,
    "gustKmh": 4.7,
    "cloudPct": 80,
    "humidityPct": 89,
    "pressureHpa": 997.8,
    "boundaryLayerM": 585,
    "isDay": true
   }
  }
 ]
}

POST /v1/route/weather

Weather along a route on the way. The forecast at points along the route for the time you reach each of them, leaving at departure (now by default) at speedKmh (default 20), every stepKm; a summary gives the coldest and warmest, the wettest hour, the strongest headwind and gust. A route is GeoJSON {"type": "LineString", "coordinates": [[lon, lat], ...]} or a MultiLineString of sections in order, up to 20,000 km.

Coût: One request.

Corps

routeGeoJSON LineString or MultiLineString or GeoJSON LineString or MultiLineString
departure facultatifstring (ISO time with offset)
speedKmh facultatifnumber, 2 to 80
stepKm facultatifnumber, 0.5 to 100

Requête

curl -X POST 'https://geo.runningcat.dev/v1/route/weather' \
  -H "Authorization: Bearer YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"route":{"type":"LineString","coordinates":[[6.1432,46.2044],[6.15,46.19],[6.175,46.17],[6.2,46.15]]},"speedKmh":18}'

Exemple de réponse

{
 "routeLengthKm": 7.6,
 "departure": "2026-11-12T12:35:43.550Z",
 "arrival": "2026-11-12T13:00:57.025Z",
 "speedKmh": 18,
 "summary": {
  "minTemperatureC": 8.4,
  "maxTemperatureC": 12.7,
  "maxPrecipitationMm": 0.7,
  "maxHeadwindKmh": -3.1,
  "maxGustKmh": 27
 },
 "points": [
  {
   "km": 0,
   "lon": 6.1432,
   "lat": 46.2044,
   "time": "2026-11-12T12:35:43.550Z",
   "temperatureC": 12.7,
   "feelsLikeC": 11.7,
   "precipitationMm": 0.4,
   "weatherCode": 61,
   "windKmh": 9.9,
   "windDirDeg": 37,
   "gustKmh": 21.1,
   "cloudPct": 100,
   "humidityPct": 92,
   "pressureHpa": 992.1,
   "boundaryLayerM": 230,
   "isDay": true,
   "headwindKmh": -3.3,
   "crosswindKmh": 9.3
  },
  {
   "km": 5,
   "lon": 6.17818,
   "lat": 46.16746,
   "time": "2026-11-12T12:52:23.550Z",
   "temperatureC": 12.2,
   "feelsLikeC": 11.4,
   "precipitationMm": 0.5,
   "weatherCode": 61,
   "windKmh": 8.8,
   "windDirDeg": 31,
   "gustKmh": 19.2,
   "cloudPct": 100,
   "humidityPct": 95,
   "pressureHpa": 992.2,
   "boundaryLayerM": 180,
   "isDay": true,
   "headwindKmh": -3.5,
   "crosswindKmh": 8.1
  },
  {
   "km": 7.6,
   "lon": 6.2,
   "lat": 46.15,
   "time": "2026-11-12T13:00:57.025Z",
   "temperatureC": 8.4,
   "feelsLikeC": 6.3,
   "precipitationMm": 0.7,
   "weatherCode": 61,
   "windKmh": 11.2,
   "windDirDeg": 33,
   "gustKmh": 27,
   "cloudPct": 100,
   "humidityPct": 100,
   "pressureHpa": 992.3,
   "boundaryLayerM": 120,
   "isDay": true,
   "headwindKmh": -3.1,
   "crosswindKmh": 10.8
  }
 ]
}

Air quality

Forecasts and what stations measure now.

GET /v1/air

Air quality forecast for a point. Current and hourly PM2.5, PM10, nitrogen dioxide, ozone, sulphur dioxide, carbon monoxide, dust, UV index, European and US AQI; in Europe also ammonia and pollen (alder, birch, grass, mugwort, olive, ragweed). µg/m³; pollen in grains/m³.

Coût: One request.

Paramètres

lonLongitude, −180 to 180
latLatitude, −90 to 90
hours facultatifHours from the current one, 1 to 120 (default 48)
pastHours facultatifPast hours of the model before the current hour, 0 to 48 (default 0): what the forecast said for the last hours, to set against what stations measured

Requête

curl 'https://geo.runningcat.dev/v1/air?lon=6.1432&lat=46.2044&hours=2' \
  -H "Authorization: Bearer YOUR_KEY"

Exemple de réponse

{
 "lon": 6.1432,
 "lat": 46.2044,
 "current": {
  "time": "2026-11-12T12:00:00Z",
  "pm25": 3,
  "pm10": 4.5,
  "nitrogenDioxide": 9.5,
  "ozone": 45,
  "sulphurDioxide": 0.3,
  "carbonMonoxide": 143,
  "ammonia": 1.2,
  "dust": 0,
  "uvIndex": 0.5,
  "europeanAqi": 19,
  "usAqi": 38,
  "alderPollen": 0,
  "birchPollen": 0,
  "grassPollen": 0,
  "mugwortPollen": 0,
  "olivePollen": 0,
  "ragweedPollen": 0
 },
 "hourly": [
  {
   "time": "2026-11-12T12:00:00Z",
   "pm25": 3,
   "pm10": 4.5,
   "nitrogenDioxide": 9.5,
   "ozone": 45,
   "sulphurDioxide": 0.3,
   "carbonMonoxide": 143,
   "ammonia": 1.2,
   "dust": 0,
   "uvIndex": 0.5,
   "europeanAqi": 19,
   "usAqi": 38,
   "alderPollen": 0,
   "birchPollen": 0,
   "grassPollen": 0,
   "mugwortPollen": 0,
   "olivePollen": 0,
   "ragweedPollen": 0
  },
  {
   "time": "2026-11-12T13:00:00Z",
   "pm25": 2.7,
   "pm10": 4.3,
   "nitrogenDioxide": 9.2,
   "ozone": 48,
   "sulphurDioxide": 0.3,
   "carbonMonoxide": 140,
   "ammonia": 1.2,
   "dust": 0,
   "uvIndex": 0.3,
   "europeanAqi": 18,
   "usAqi": 38,
   "alderPollen": 0,
   "birchPollen": 0,
   "grassPollen": 0,
   "mugwortPollen": 0,
   "olivePollen": 0,
   "ragweedPollen": 0
  }
 ]
}

GET /v1/air/area

Air quality grid over an area. The air quality values of /v1/air on a grid, shaped like the weather grid, with the same hours, stepHours and pastHours. Keys given the bulk_grid permission (on request) may ask for up to 20,000 cells and 250,000 values; such grids are built one at a time behind other requests, so they take longer.

Coût: One request.

Paramètres

bboxwest,south,east,north in degrees, at most 3600 square degrees
cells facultatif1 to 400 (default 100)
stepDeg facultatifInstead of cells: a lattice with cell centres at west + k·stepDeg and north − k·stepDeg, 0.01 to 10 degrees, so the cells can sit on a model's own nodes (CAMS Europe: every 0.1° at x.x5°, CAMS Global: every 0.4°)
hours facultatifHow many steps come back, 1 to 120 (default 24)
stepHours facultatifHours between two steps, 1 to 6 (default 1)
pastHours facultatifStart this many hours before the current hour, 0 to 48 (default 0); the window may span up to 168 hours

Requête

curl 'https://geo.runningcat.dev/v1/air/area?bbox=6.0,46.1,6.3,46.3&cells=4&hours=2' \
  -H "Authorization: Bearer YOUR_KEY"

Exemple de réponse

{
 "bbox": [
  6,
  46.1,
  6.3,
  46.3
 ],
 "rows": 2,
 "cols": 2,
 "lon": [
  6.075,
  6.225
 ],
 "lat": [
  46.25,
  46.15
 ],
 "times": [
  "2026-11-12T12:00:00Z",
  "2026-11-12T13:00:00Z"
 ],
 "values": {
  "pm25": [
   [
    3,
    3.5,
    3.6,
    4.3
   ],
   [
    2.5,
    3.2,
    3.1,
    4.4
   ]
  ],
  "pm10": [
   [
    3.7,
    4.9,
    4.7,
    5.6
   ],
   [
    3.5,
    4.9,
    4.5,
    5.2
   ]
  ],
  "nitrogenDioxide": [
   [
    6.5,
    10.5,
    10.9,
    14.1
   ],
   [
    6.2,
    9.1,
    9.7,
    13.8
   ]
  ],
  "ozone": [
   [
    54,
    43,
    45,
    35
   ],
   [
    55,
    44,
    48,
    38
   ]
  ],
  "sulphurDioxide": [
   [
    0.3,
    0.3,
    0.3,
    0.3
   ],
   [
    0.2,
    0.3,
    0.3,
    0.3
   ]
  ],
  "carbonMonoxide": [
   [
    136,
    154,
    153,
    165
   ],
   [
    130,
    147,
    149,
    165
   ]
  ],
  "ammonia": [
   [
    0.9,
    1.1,
    1.2,
    1
   ],
   [
    0.9,
    1.2,
    1.3,
    1.1
   ]
  ],
  "dust": [
   [
    0,
    0,
    0,
    0
   ],
   [
    0,
    0,
    0,
    0
   ]
  ],
  "uvIndex": [
   [
    0.5,
    0.5,
    0.4,
    0.4
   ],
   [
    0.3,
    0.4,
    0.3,
    0.3
   ]
  ],
  "europeanAqi": [
   [
    18,
    21,
    21,
    25
   ],
   [
    18,
    18,
    19,
    25
   ]
  ],
  "usAqi": [
   [
    30,
    42,
    38,
    39
   ],
   [
    29,
    42,
    38,
    39
   ]
  ],
  "alderPollen": [
   [
    0,
    0,
    0,
    0
   ],
   [
    0,
    0,
    0,
    0
   ]
  ],
  "birchPollen": [
   [
    0,
    0,
    0,
    0
   ],
   [
    0,
    0,
    0,
    0
   ]
  ],
  "grassPollen": [
   [
    0,
    0,
    0,
    0
   ],
   [
    0,
    0,
    0,
    0
   ]
  ],
  "mugwortPollen": [
   [
    0,
    0,
    0,
    0
   ],
   [
    0,
    0,
    0,
    0
   ]
  ]
…

GET /v1/air/stations

Measured air quality at stations. The latest value per pollutant (last 3 hours; 6 for EEA stations) at official stations (kind=government) and volunteer sensors (kind=citizen), nearest first: around a point (lon, lat, radiusKm 1 to 100, default 25) or in a box (bbox, nearest its centre first). µg/m³; credits names the sources to show.

Coût: One request.

Paramètres

lon facultatifLongitude, −180 to 180
lat facultatifLatitude, −90 to 90
radiusKm facultatif1 to 100 (default 25)
bbox facultatifwest,south,east,north in degrees, at most 16 square degrees
limit facultatifAround a point 1 to 100 (default 10); in a box 1 to 1000 (default 200)
kind facultatifgovernment or citizen; both when absent

Requête

curl 'https://geo.runningcat.dev/v1/air/stations?lon=19.94&lat=50.06&bbox=6.0,46.1,6.3,46.3&limit=2' \
  -H "Authorization: Bearer YOUR_KEY"

Exemple de réponse

{
 "stations": [
  {
   "id": "syngeos:10043",
   "name": "Kraków, Warszawska 24",
   "kind": "citizen",
   "lon": 19.94143,
   "lat": 50.07168,
   "distanceKm": 1.3,
   "time": "2026-11-12T10:37:38.000Z",
   "values": {
    "pm10": 19,
    "pm25": 23
   }
  },
  {
   "id": "syngeos:10082",
   "name": "Kraków, Warszawska 24",
   "kind": "citizen",
   "lon": 19.94367,
   "lat": 50.07152,
   "distanceKm": 1.31,
   "time": "2026-11-12T11:05:27.000Z",
   "values": {
    "o3": 38,
    "pm10": 11,
    "pm25": 11
   }
  }
 ],
 "credits": [
  "Syngeos sensor network (https://syngeos.pl)"
 ]
}

POST /v1/route/air/stations

Air quality stations along a route. Stations within bufferKm (0.1 to 20) of the route, in route order with alongKm; up to limit (at most 500). A route is GeoJSON {"type": "LineString", "coordinates": [[lon, lat], ...]} or a MultiLineString of sections in order, up to 20,000 km.

Coût: One request.

Corps

routeGeoJSON LineString or MultiLineString or GeoJSON LineString or MultiLineString
bufferKm facultatifnumber, 0.1 to 20
limit facultatifinteger, 1 to 500
kind facultatifgovernment, citizen

Requête

curl -X POST 'https://geo.runningcat.dev/v1/route/air/stations' \
  -H "Authorization: Bearer YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"route":{"type":"LineString","coordinates":[[19.9,50.07],[19.98,50.05]]},"limit":2}'

Exemple de réponse

{
 "stations": [
  {
   "id": "gios:400",
   "name": "Kraków, Aleja Krasińskiego",
   "kind": "government",
   "lon": 19.92619,
   "lat": 50.05768,
   "distanceKm": 0.6,
   "alongKm": 2.25,
   "time": "2026-11-12T12:00:00.000Z",
   "values": {
    "co": 544,
    "no2": 38.2,
    "pm10": 37.7,
    "pm25": 19.8
   }
  },
  {
   "id": "syngeos:10043",
   "name": "Kraków, Warszawska 24",
   "kind": "citizen",
   "lon": 19.94143,
   "lat": 50.07168,
   "distanceKm": 1.25,
   "alongKm": 2.75,
   "time": "2026-11-12T10:37:38.000Z",
   "values": {
    "pm10": 19,
    "pm25": 23
   }
  }
 ],
 "credits": [
  "Źródło danych: GIOŚ - EKOINFONET; current data are unverified and may change, CC BY 4.0 (https://creativecommons.org/licenses/by/4.0/)",
  "Syngeos sensor network (https://syngeos.pl)"
 ]
}

GET /v1/air/stations/{id}/history

Readings of one station. Hourly readings per pollutant for the last days (1 to 7, default 7). The id comes from the station lists.

Coût: One request.

Paramètres

idsource:station, e.g. gios:400
days facultatif1 to 7 (default 7)

Requête

curl 'https://geo.runningcat.dev/v1/air/stations/gios:400/history?days=1' \
  -H "Authorization: Bearer YOUR_KEY"

Exemple de réponse

{
 "id": "syngeos:10043",
 "readings": {
  "pm10": [
   {
    "time": "2026-11-11T14:00:00.000Z",
    "value": 15
   },
   {
    "time": "2026-11-11T15:00:00.000Z",
    "value": 17
   },
   {
    "time": "2026-11-11T16:00:00.000Z",
    "value": 17
   }
  ],
  "pm25": [
   {
    "time": "2026-11-11T13:00:00.000Z",
    "value": 11
   },
   {
    "time": "2026-11-11T14:00:00.000Z",
    "value": 11
   },
   {
    "time": "2026-11-11T15:00:00.000Z",
    "value": 12
   }
  ]
 },
 "credits": [
  "Syngeos sensor network (https://syngeos.pl)"
 ]
}

Rivers

Water levels and discharge at river gauges.

GET /v1/rivers

River gauges. The latest water level (cm above the gauge zero), discharge (m³/s), change over a day and the warning state at gauges, nearest first: around a point (lon, lat, radiusKm 1 to 100, default 25) or in a box (bbox).

Coût: One request.

Paramètres

lon facultatifLongitude, −180 to 180
lat facultatifLatitude, −90 to 90
radiusKm facultatif1 to 100 (default 25)
bbox facultatifwest,south,east,north in degrees, at most 16 square degrees
limit facultatifAround a point 1 to 100 (default 10); in a box 1 to 1000 (default 200)

Requête

curl 'https://geo.runningcat.dev/v1/rivers?lon=6.1432&lat=46.2044&bbox=6.0,46.1,6.3,46.3&limit=2' \
  -H "Authorization: Bearer YOUR_KEY"

Exemple de réponse

{
 "gauges": [
  {
   "id": "hubeau:V035501202",
   "name": "[Le Rhône] en Suisse - Halle de l'Ile - Barrage du Seujet (OFEV)",
   "river": null,
   "lon": 6.1416,
   "lat": 46.20457,
   "distanceKm": 0.13,
   "time": "2026-11-12T12:00:00.000Z",
   "levelCm": null,
   "change24hCm": null,
   "dischargeM3s": 107,
   "warningCm": null,
   "alarmCm": null,
   "state": null
  },
  {
   "id": "bafu:2606",
   "name": "Genève, Halle de l'Île",
   "river": "Rhône",
   "lon": 6.14154,
   "lat": 46.20466,
   "distanceKm": 0.13,
   "time": "2026-11-12T12:20:00.000Z",
   "levelCm": null,
   "change24hCm": null,
   "dischargeM3s": 110.375,
   "warningCm": null,
   "alarmCm": null,
   "state": null
  }
 ]
}

POST /v1/route/rivers

River gauges along a route. Gauges within bufferKm (0.1 to 20, default 2) of the route, nearest first. A route is GeoJSON {"type": "LineString", "coordinates": [[lon, lat], ...]} or a MultiLineString of sections in order, up to 20,000 km.

Coût: One request.

Corps

routeGeoJSON LineString or MultiLineString or GeoJSON LineString or MultiLineString
bufferKm facultatifnumber, 0.1 to 20

Requête

curl -X POST 'https://geo.runningcat.dev/v1/route/rivers' \
  -H "Authorization: Bearer YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"route":{"type":"LineString","coordinates":[[6.1432,46.2044],[6.15,46.19],[6.175,46.17],[6.2,46.15]]}}'

Exemple de réponse

{
 "gauges": [
  {
   "id": "bafu:2606",
   "name": "Genève, Halle de l'Île",
   "river": "Rhône",
   "lon": 6.14154,
   "lat": 46.20466,
   "distanceKm": 0.13,
   "time": "2026-11-12T12:20:00.000Z",
   "levelCm": null,
   "change24hCm": null,
   "dischargeM3s": 110.375,
   "warningCm": null,
   "alarmCm": null,
   "state": null
  },
  {
   "id": "hubeau:V035501202",
   "name": "[Le Rhône] en Suisse - Halle de l'Ile - Barrage du Seujet (OFEV)",
   "river": null,
   "lon": 6.1416,
   "lat": 46.20457,
   "distanceKm": 0.13,
   "time": "2026-11-12T12:00:00.000Z",
   "levelCm": null,
   "change24hCm": null,
   "dischargeM3s": 107,
   "warningCm": null,
   "alarmCm": null,
   "state": null
  },
  {
   "id": "bafu:2170",
   "name": "Genève, Bout du Monde",
   "river": "Arve",
   "lon": 6.15935,
   "lat": 46.1803,
   "distanceKm": 0.16,
   "time": "2026-11-12T12:20:00.000Z",
   "levelCm": 37949.2,
   "change24hCm": 15.3,
   "dischargeM3s": 64.84,
   "warningCm": null,
   "alarmCm": null,
   "state": null
  }
 ]
}

GET /v1/rivers/{id}/history

Readings of one gauge. Readings for the last days (1 to 30, default 7). The id comes from the gauge lists.

Coût: One request.

Paramètres

idsource:station, e.g. hubeau:V035501202
days facultatif1 to 30 (default 7)

Requête

curl 'https://geo.runningcat.dev/v1/rivers/hubeau:V035501202/history?days=1' \
  -H "Authorization: Bearer YOUR_KEY"

Exemple de réponse

{
 "id": "hubeau:V035501202",
 "readings": [
  {
   "time": "2026-11-11T12:40:00.000Z",
   "levelCm": null,
   "dischargeM3s": 83.7
  },
  {
   "time": "2026-11-11T13:00:00.000Z",
   "levelCm": null,
   "dischargeM3s": 92.2
  },
  {
   "time": "2026-11-11T13:10:00.000Z",
   "levelCm": null,
   "dischargeM3s": 87.9
  }
 ]
}

Routing

Routes, loops and the surfaces of a route.

POST /v1/route

A route through points. A route through 2 to 50 points on OpenStreetMap data. Profiles: bike, city, road-bike, quiet-road-bike, gravel, mtb, hiking, car, shortest (default bike; city keeps to cycleways and off busy roads). alternative 1 to 3 gives other routes. The answer is a LineString with a height at every point, its length, climb and riding time, and surfaces: kilometres paved, unpaved and unknown, kilometres per highway value, and the stretches of each way kind with its OSM surface, highway, tracktype (grade1 solid to grade5 soft) and smoothness (null where the way has no such tag), so a stretch without a surface tag can still be judged by the kind of way. format: "gpx" answers a GPX file instead. 404 no_route when a point cannot be reached with the profile; 504 timeout after two minutes (add waypoints or split the trip).

Coût: One request and one route per started 100 km of the route found (50 km for mtb).

Corps

pointsarray of [lon, lat], 2 to 50 items
profile facultatifbike, city, road-bike, quiet-road-bike, gravel, mtb, hiking, car, shortest
alternative facultatifinteger, 0 to 3
format facultatifgeojson, gpx

Requête

curl -X POST 'https://geo.runningcat.dev/v1/route' \
  -H "Authorization: Bearer YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"points":[[6.1432,46.2044],[6.2,46.15]],"profile":"bike"}'

Exemple de réponse

{
 "profile": "bike",
 "lengthKm": 18.82,
 "ascentM": 735,
 "durationS": 8696,
 "geometry": {
  "type": "LineString",
  "coordinates": [
   [
    6.143198,
    46.204407,
    374
   ],
   [
    6.143206,
    46.204408,
    374
   ],
   [
    6.143399,
    46.204372,
    374
   ]
  ]
 },
 "surfaces": {
  "km": {
   "paved": 18,
   "unpaved": 0.04,
   "unknown": 0.79
  },
  "highwayKm": {
   "tertiary": 6.34,
   "cycleway": 5.23,
   "secondary": 3.46,
   "residential": 2.25,
   "unclassified": 0.68,
   "primary": 0.47,
   "track": 0.2,
   "pedestrian": 0.07,
   "footway": 0.07,
   "primary_link": 0.06
  },
  "runs": [
   {
    "fromKm": 0,
    "toKm": 0.235,
    "surface": "asphalt",
    "highway": "residential",
    "tracktype": null,
    "smoothness": null
   },
   {
    "fromKm": 0.235,
    "toKm": 0.308,
    "surface": "asphalt",
    "highway": "pedestrian",
    "tracktype": null,
    "smoothness": null
   },
   {
    "fromKm": 0.308,
    "toKm": 0.584,
    "surface": "asphalt",
    "highway": "unclassified",
    "tracktype": null,
    "smoothness": null
   }
  ]
 }
}

POST /v1/route/loop

A round trip from a point. A loop of about distanceKm (5 to 200) back to start, setting off towards direction (degrees, 0 = north, default 0); another direction gives another loop. The length usually lands within 10 % of the asked one. If no loop can be made that way (a glacier, the sea), the other directions are tried. The answer has the shape of /v1/route.

Coût: One request and one route per started 100 km of the route found (50 km for mtb).

Corps

start[lon, lat]
distanceKmnumber, 5 to 200
profile facultatifbike, city, road-bike, quiet-road-bike, gravel, mtb, hiking, car, shortest
direction facultatifnumber, 0 to 360

Requête

curl -X POST 'https://geo.runningcat.dev/v1/route/loop' \
  -H "Authorization: Bearer YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"start":[6.1432,46.2044],"distanceKm":25,"profile":"bike","direction":90}'

Exemple de réponse

{
 "profile": "bike",
 "lengthKm": 25.807,
 "ascentM": 141,
 "durationS": 4932,
 "geometry": {
  "type": "LineString",
  "coordinates": [
   [
    6.143198,
    46.204407,
    374
   ],
   [
    6.143206,
    46.204408,
    374
   ],
   [
    6.143201,
    46.204436,
    374
   ]
  ]
 },
 "surfaces": {
  "km": {
   "paved": 16.01,
   "unpaved": 0.05,
   "unknown": 9.75
  },
  "highwayKm": {
   "none": 7.07,
   "secondary": 6.25,
   "residential": 4.64,
   "tertiary": 2.58,
   "cycleway": 2.51,
   "living_street": 0.96,
   "unclassified": 0.89,
   "footway": 0.26,
   "service": 0.21,
   "primary": 0.21,
   "path": 0.13,
   "pedestrian": 0.08
  },
  "runs": [
   {
    "fromKm": 0,
    "toKm": 0.01,
    "surface": "asphalt",
    "highway": "residential",
    "tracktype": null,
    "smoothness": null
   },
   {
    "fromKm": 0.01,
    "toKm": 0.044,
    "surface": "asphalt",
    "highway": "residential",
    "tracktype": null,
    "smoothness": "excellent"
   },
   {
    "fromKm": 0.044,
    "toKm": 0.104,
    "surface": "asphalt",
    "highway": "residential",
    "tracktype": null,
    "smoothness": null
   }
  ]
 }
}

POST /v1/route/surfaces

Surfaces of any track. The OSM surface along a track you already have (an imported GPX, a drawn line): every 20 m the track takes the nearest road or path within 25 m. The answer has the shape of surfaces in /v1/route: kilometres paved, unpaved and unknown, kilometres per highway, and stretches with surface, highway, tracktype and smoothness. The track is not re-routed. unknown surface: the way has no surface tag (its highway, tracktype and smoothness still come along), or no way lies within 25 m (then all three are null). heightLimits lists the stretches on ways with a height limit, tunnels and passages under bridges included, so you can tell whether a car with bikes on the roof fits: maxheight as tagged in OSM (the lower of maxheight and maxheight:physical), maxheightM in metres (feet converted), or below_default, meaning no sign but less room than the country's general limit (maxheightM null); name of the way. Only what is tagged on the ways: a height barrier mapped as a point on the road is not in the list, and a way with no tag may still be low. A route is GeoJSON {"type": "LineString", "coordinates": [[lon, lat], ...]} or a MultiLineString of sections in order, up to 20,000 km.

Coût: One request.

Corps

routeGeoJSON LineString or MultiLineString or GeoJSON LineString or MultiLineString

Requête

curl -X POST 'https://geo.runningcat.dev/v1/route/surfaces' \
  -H "Authorization: Bearer YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"route":{"type":"LineString","coordinates":[[6.1432,46.2044],[6.15,46.19],[6.175,46.17],[6.2,46.15]]}}'

Exemple de réponse

{
 "km": {
  "paved": 2.76,
  "unpaved": 0.32,
  "unknown": 4.49
 },
 "highwayKm": {
  "none": 1.95,
  "footway": 1.53,
  "service": 1.25,
  "path": 0.7,
  "residential": 0.54,
  "tertiary": 0.44,
  "track": 0.32,
  "cycleway": 0.2,
  "living_street": 0.18,
  "unclassified": 0.16,
  "pedestrian": 0.14,
  "secondary": 0.06,
  "primary": 0.04,
  "motorway": 0.04,
  "secondary_link": 0.02,
  "platform": 0.02
 },
 "runs": [
  {
   "fromKm": 0,
   "toKm": 0.02,
   "surface": "asphalt",
   "highway": "residential",
   "tracktype": null,
   "smoothness": null
  },
  {
   "fromKm": 0.02,
   "toKm": 0.04,
   "surface": "unknown",
   "highway": "cycleway",
   "tracktype": null,
   "smoothness": null
  },
  {
   "fromKm": 0.04,
   "toKm": 0.059,
   "surface": "asphalt",
   "highway": "pedestrian",
   "tracktype": null,
   "smoothness": "good"
  }
 ],
 "heightLimits": []
}

Stops along a route

Water, food, shops and more along a route.

GET /v1/route-poi/categories

Stop categories. The categories and subtypes /v1/route-poi/corridor answers with: the OSM tags behind each, the default distance from the route, whether it is in the default search, and GPX and FIT waypoint symbols. version (also the ETag) changes when the catalog does.

Coût: One request.

Requête

curl 'https://geo.runningcat.dev/v1/route-poi/categories' \
  -H "Authorization: Bearer YOUR_KEY"

Exemple de réponse

{
 "version": "a266ea079477",
 "defaultCategories": [
  "grocery",
  "fuel",
  "food"
 ],
 "maxDistanceM": 5000,
 "maxCorridorKm2": 2000,
 "fallbackExport": {
  "gpx": "Waypoint",
  "fit": "generic"
 },
 "categories": [
  {
   "id": "grocery",
   "inDefaultSearch": true,
   "defaultDistanceM": 200,
   "sources": [
    "db",
    "tiles"
   ],
   "export": {
    "gpx": "Shopping Center",
    "fit": "store"
   },
   "subtypes": [
    {
     "id": "supermarket",
     "osm": [
      {
       "shop": [
        "supermarket"
       ]
      }
     ],
     "export": {
      "gpx": "Shopping Center",
      "fit": "store"
     }
    },
    {
     "id": "convenience",
     "osm": [
      {
       "shop": [
        "convenience"
       ]
      }
     ],
     "export": {
      "gpx": "Convenience Store",
      "fit": "store"
     }
    },
    {
     "id": "bakery",
     "osm": [
      {
       "shop": [
        "bakery"
       ]
      }
     ],
     "export": {
      "gpx": "Convenience Store",
      "fit": "food"
     }
    }
   ]
  },
  {
   "id": "fuel",
   "inDefaultSearch": true,
   "defaultDistanceM": 200,
   "sources": [
    "db",
    "tiles"
   ],
   "export": {
    "gpx": "Gas Station",
    "fit": "store"
   },
   "subtypes": [
    {
     "id": "fuel",
     "osm": [
      {
       "amenity": [
        "fuel"
       ]
      }
     ],
     "export": {
      "gpx": "Gas Station",
      "fit": "store"
     }
    }
   ]
  },
  {
   "id": "food",
   "inDefaultSearch": true,
   "defaultDistanceM": 200,
   "sources": [
    "db",
    "tiles"
   ],
   "export": {
    "gpx": "Restaurant",
    "fit": "food"
   },
   "subtypes": [
    {
     "id": "cafe",
     "osm": [
      {
       "amenity": [
        "cafe"
       ]
      }
     ],
     "export": {
      "gpx": "Restaurant",
      "f
…

POST /v1/route-poi/corridor

Stops along a route. Water, food, groceries, toilets, bike shops and fuel (and on request places to sleep, sights, charging, benches, picnic spots) along a route, each with its kilometre along the route and its distance from it, plus the stretches of more than 40 km without water and food (gaps). The route comes as points of {lat, lon, d} (d: metres from the start), or as sections of them for separate pieces; up to 1,000 points. Distances: each category's default, maxDistanceM for all, or distancesM per category, up to 5,000 m; route length × the widest distance at most 2,000 km·km (1,000 km at 2 km, 400 km at 5 km), else 413. Chain stores carry their brand logo and icon URLs.

Coût: One request.

Corps

points facultatifarray of object, 2 to 1000 items
sections facultatifarray of array of object, 2 to 1000 items, 1 to 64 items
categories facultatifarray of grocery, fuel, food, water, toilets, bike, sleep, sights, charging, bench, picnic, at most 11 items
maxDistanceM facultatifinteger, 1 to 5000
distancesM facultatifobject
includeSprings facultatifboolean

Requête

curl -X POST 'https://geo.runningcat.dev/v1/route-poi/corridor' \
  -H "Authorization: Bearer YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"points":[{"lat":46.2044,"lon":6.1432,"d":0},{"lat":46.19,"lon":6.15,"d":1666}],"categories":["water","food"]}'

Exemple de réponse

{
 "pois": [
  {
   "id": "node/12456128201",
   "category": "food",
   "subtype": "restaurant",
   "name": "Little Kitchen",
   "lat": 46.205307,
   "lon": 6.1434257,
   "flags": {},
   "tags": {
    "amenity": "restaurant"
   },
   "distanceAlongRouteKm": 0,
   "offsetFromRouteM": 102
  },
  {
   "id": "node/1379366890",
   "category": "water",
   "subtype": "drinking_water",
   "name": null,
   "lat": 46.2047788,
   "lon": 6.1417476,
   "flags": {
    "hasWater": true
   },
   "tags": {
    "amenity": "fountain",
    "drinking_water": "yes"
   },
   "distanceAlongRouteKm": 0,
   "offsetFromRouteM": 120
  },
  {
   "id": "node/3032374079",
   "category": "food",
   "subtype": "restaurant",
   "name": "Le Paradis",
   "lat": 46.2054609,
   "lon": 6.1419829,
   "flags": {},
   "tags": {
    "website": "https://www.terrasse-paradis.ch/",
    "addr:street": "Quai Turrettini",
    "amenity": "restaurant"
   },
   "distanceAlongRouteKm": 0,
   "offsetFromRouteM": 151
  }
 ],
 "totals": {
  "grocery": 0,
  "fuel": 0,
  "food": 79,
  "water": 19,
  "toilets": 0,
  "bike": 0
 },
 "totalCount": 98,
 "gaps": [],
 "gapCount": 0,
 "routeLengthKm": 7.568,
 "partial": false,
 "cached": false,
 "source": "db",
 "coverage": [
  {
   "sectionIndex": 0,
   "segmentIndexes": [
    0,
    1,
    2
   ]
  }
 ]
}

GET /v1/route-poi/tile/{row}/{col}

All stops in a tile. Every stop of the default categories in one 0.1° tile (row = floor(lat × 10), col = floor(lon × 10)), for apps that cache stops by area.

Coût: One request.

Paramètres

rowfloor(lat × 10)
colfloor(lon × 10)

Requête

curl 'https://geo.runningcat.dev/v1/route-poi/tile/462/61' \
  -H "Authorization: Bearer YOUR_KEY"

Exemple de réponse

{
 "pois": [
  {
   "id": "node/10009315581",
   "category": "water",
   "subtype": "drinking_water",
   "name": null,
   "lat": 46.2185561,
   "lon": 6.150866,
   "flags": {
    "hasWater": true
   },
   "tags": {
    "amenity": "drinking_water",
    "access": "yes",
    "fee": "no"
   }
  },
  {
   "id": "node/10079250380",
   "category": "food",
   "subtype": "restaurant",
   "name": "Hanae",
   "lat": 46.2043595,
   "lon": 6.1563109,
   "flags": {},
   "tags": {
    "opening_hours": "Tu-Sa 12:00-14:00,19:00-22:00",
    "website": "https://hanae-restaurant.ch/",
    "addr:street": "Rue des Pierres-du-Niton",
    "addr:housenumber": "5",
    "addr:city": "Genève",
    "phone": "+41 22 736 93 93",
    "amenity": "restaurant"
   }
  },
  {
   "id": "node/10096957554",
   "category": "food",
   "subtype": "fast_food",
   "name": "River Side",
   "lat": 46.2013962,
   "lon": 6.1553355,
   "flags": {},
   "tags": {
    "opening_hours": "Mo 19:00-01:00; Tu 19:00-02:00; We 19:00-02:30; Th 19:00-03:00; Fr, Sa 19:00-04:00; Su 19:00-00:00",
    "phone": "+41227362365",
    "amenity": "fast_food"
   }
  }
 ],
 "complete": true,
 "source": "db",
 "fetchedAt": "2026-11-12T12:36:13.181Z",
 "row": 462,
 "col": 61,
 "version": 1
}

OpenStreetMap data

Overpass QL and OSM features by tag.

POST /v1/overpass/interpreter

Overpass QL. Overpass QL queries answered from our copy of OpenStreetMap, in the Overpass JSON format. Send the query as the body (text/plain), as the form field data, or as ?data= on GET; /v1/overpass/api/interpreter works too, and the key may come as ?key=. open_at=YYYY-MM-DDTHH:MM (local time) keeps only the places open then. Errors come back as remark, as Overpass does. See the Overpass QL examples page for what is supported.

Coût: One request.

Paramètres

open_at facultatifLocal time such as 2026-11-10T06:30

Requête

curl -X POST 'https://geo.runningcat.dev/v1/overpass/interpreter' \
  -H "Authorization: Bearer YOUR_KEY" \
  --data-binary 'node["amenity"="drinking_water"](around:300,46.2044,6.1432);out;'

Exemple de réponse

{
 "version": 0.6,
 "generator": "Running Cat Geo API",
 "osm3s": {
  "timestamp_osm_base": "2026-11-02T21:21:26+00:00",
  "copyright": "The data included in this document is from www.openstreetmap.org. The data is made available under ODbL."
 },
 "elements": [
  {
   "type": "node",
   "id": 897590805,
   "lat": 46.2054894,
   "lon": 6.1451637,
   "tags": {
    "amenity": "drinking_water",
    "fountain": "bottle_refill",
    "check_date": "2023-05-24"
   }
  },
  {
   "type": "node",
   "id": 1906525422,
   "lat": 46.2028922,
   "lon": 6.1448299,
   "tags": {
    "amenity": "drinking_water",
    "designation": "potable water"
   }
  }
 ]
}

POST /v1/poi/corridor

OSM features along a route by tag. OSM nodes and ways (with center) whose tags match filters ({"amenity": ["cafe", "restaurant"], ...}, 1 to 50 keys) within buffer_m of the route. 422 not_covered outside the map data. A route is GeoJSON {"type": "LineString", "coordinates": [[lon, lat], ...]} or a MultiLineString of sections in order, up to 20,000 km.

Coût: One request.

Corps

routeGeoJSON LineString or MultiLineString or GeoJSON LineString or MultiLineString
buffer_mnumber, 1 to 20000
filtersobject

Requête

curl -X POST 'https://geo.runningcat.dev/v1/poi/corridor' \
  -H "Authorization: Bearer YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"route":{"type":"LineString","coordinates":[[6.1432,46.2044],[6.15,46.19],[6.175,46.17],[6.2,46.15]]},"buffer_m":300,"filters":{"amenity":["drinking_water"]}}'

Exemple de réponse

{
 "source": "db",
 "elements": [
  {
   "type": "node",
   "id": 897590805,
   "lat": 46.2054894,
   "lon": 6.1451637,
   "tags": {
    "amenity": "drinking_water",
    "fountain": "bottle_refill",
    "check_date": "2023-05-24"
   }
  },
  {
   "type": "node",
   "id": 1485121637,
   "lat": 46.1903548,
   "lon": 6.1505338,
   "tags": {
    "amenity": "drinking_water"
   }
  },
  {
   "type": "node",
   "id": 1769544502,
   "lat": 46.1919462,
   "lon": 6.1465644,
   "tags": {
    "amenity": "drinking_water",
    "fountain": "drinking",
    "check_date": "2024-01-30"
   }
  }
 ]
}

POST /v1/poi/boxes

OSM features in boxes by tag. The same in up to 16 boxes [west, south, east, north] of at most 1 square degree each.

Coût: One request.

Corps

boxesarray of [lon, lat], 1 to 16 items
filtersobject

Requête

curl -X POST 'https://geo.runningcat.dev/v1/poi/boxes' \
  -H "Authorization: Bearer YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"boxes":[[6.14,46.2,6.16,46.21]],"filters":{"amenity":["cafe"]}}'

Exemple de réponse

{
 "source": "db",
 "elements": [
  {
   "type": "node",
   "id": 626085414,
   "lat": 46.206384,
   "lon": 6.1450353,
   "tags": {
    "name": "Starbucks",
    "brand": "Starbucks",
    "amenity": "cafe",
    "cuisine": "coffee_shop",
    "takeaway": "yes",
    "addr:city": "Genève",
    "official_name": "Starbucks Coffee",
    "brand:wikidata": "Q37158",
    "brand:wikipedia": "en:Starbucks",
    "opening_hours": "Mo-Fr 06:30-20:30; Sa 07:00-20:30; Su 07:30-20:30",
    "source:opening_hours": "atp",
    "check_date:opening_hours": "2026-11-05",
    "phone": "+41 43 421 42 26",
    "website": "https://www.starbucks.co.uk/store-locator/40024-96840/quai-des-bergues"
   }
  },
  {
   "type": "node",
   "id": 656492497,
   "lat": 46.2009495,
   "lon": 6.1415985,
   "tags": {
    "name": "Café du Grütli",
    "phone": "+41 22 321 51 58",
    "amenity": "cafe",
    "cuisine": "regional",
    "website": "https://www.cafedugrutli.ch/",
    "diet:vegan": "yes",
    "wheelchair": "yes",
    "addr:street": "Rue du Général-Dufour",
    "opening_hours": "Mo-Th 08:00-24:00, Fr 16:00-23:00, Sa 16:00-01:00, Su 16:00-23:00",
    "addr:housenumber": "16",
    "diet:gluten_free": "yes"
   }
  },
  {
   "type": "node",
   "id": 767144613,
   "lat": 46.207876,
   "lon": 6.1416771,
   "tags": {
    "name": "Chatime",
    "brand": "Chatime",
    "branch": "KINAYO",
    "amenity": "cafe",
    "cuisine": "bubble_tea",
    "takeaway": "yes",
    "brand:wikidata": "Q16829306"
   }
  }
 ]
}

POST /v1/roads/along

Roads along a route. Highway ways within buffer_m (1 to 200) of a route, with their OSM tags and geometry. For surface statistics /v1/route/surfaces does the matching for you. A route is GeoJSON {"type": "LineString", "coordinates": [[lon, lat], ...]} or a MultiLineString of sections in order, up to 20,000 km.

Coût: One request.

Corps

routeGeoJSON LineString or MultiLineString or GeoJSON LineString or MultiLineString
buffer_mnumber, 1 to 200

Requête

curl -X POST 'https://geo.runningcat.dev/v1/roads/along' \
  -H "Authorization: Bearer YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"route":{"type":"LineString","coordinates":[[6.1432,46.2044],[6.15,46.19],[6.175,46.17],[6.2,46.15]]},"buffer_m":20}'

Exemple de réponse

{
 "source": "db",
 "ways": [
  {
   "id": 4272838,
   "tags": {
    "lit": "yes",
    "name": "Rue Lombard",
    "lanes": "2",
    "busway": "lane",
    "oneway": "yes",
    "highway": "secondary",
    "surface": "asphalt",
    "cycleway": "share_busway",
    "maxspeed": "50",
    "lanes:psv": "1",
    "psv:lanes": "yes|designated"
   },
   "geometry": [
    [
     6.147473,
     46.195835
    ],
    [
     6.147557,
     46.19577
    ],
    [
     6.147715,
     46.195644
    ]
   ]
  },
  {
   "id": 5070216,
   "tags": {
    "lit": "yes",
    "name": "Boulevard de la Tour",
    "lanes": "3",
    "oneway": "yes",
    "highway": "secondary",
    "surface": "asphalt",
    "maxspeed": "30",
    "cycleway:right": "lane"
   },
   "geometry": [
    [
     6.14679,
     46.195418
    ],
    [
     6.146854,
     46.195432
    ],
    [
     6.146914,
     46.195452
    ]
   ]
  },
  {
   "id": 5070228,
   "tags": {
    "lit": "yes",
    "name": "Rue de la Cité",
    "bicycle": "designated",
    "highway": "pedestrian",
    "surface": "sett",
    "segregated": "no"
   },
   "geometry": [
    [
     6.14362,
     46.203894
    ],
    [
     6.143629,
     46.203875
    ],
    [
     6.143649,
     46.203835
    ]
   ]
  }
 ]
}

Service

The state of the data.

GET /v1/status

Data status. The loaded OpenStreetMap regions, when they were imported and how fresh their data is, and how busy the server is.

Coût: One request.

Requête

curl 'https://geo.runningcat.dev/v1/status' \
  -H "Authorization: Bearer YOUR_KEY"

Exemple de réponse

{
 "import": {
  "regions": [
   "africa",
   "antarctica",
   "australia-oceania"
  ],
  "version": 31,
  "import_id": 31,
  "imported_at": "2026-11-08T17:55:10.220669+00:00",
  "switched_at": "2026-11-08T17:55:10.220669+00:00",
  "data_timestamp": "2026-11-02T21:21:26+00:00",
  "region_details": [
   {
    "region": "africa",
    "import_id": 29,
    "switched_at": "2026-11-08T17:53:57.686042+00:00",
    "data_timestamp": "2026-11-07T21:21:36+00:00"
   },
   {
    "region": "antarctica",
    "import_id": 30,
    "switched_at": "2026-11-08T17:55:10.220669+00:00",
    "data_timestamp": "2026-11-07T21:21:36+00:00"
   },
   {
    "region": "australia-oceania",
    "import_id": 28,
    "switched_at": "2026-11-07T09:06:14.889892+00:00",
    "data_timestamp": "2026-11-05T21:23:02+00:00"
   }
  ]
 },
 "flyover": {
  "memory": 0,
  "store": 0,
  "db": 0,
  "uncovered": 0,
  "failed": 0,
  "memory_bytes": 0,
  "db_slots": {
   "active": 0,
   "waiting": 0
  }
 },
 "requests": {
  "running": 1,
  "waiting": 0
 }
}

L’offre gratuite donne 500 requêtes par jour, y compris pour les applications commerciales. Créez un compte avec Google ou votre e-mail et générez vos clés tout de suite.