API Documentation
Geocoding, Places Search, and Map Tiles β everything you need to build location-aware apps.
π Authentication
All API requests require an API key. There are two types of keys:
| Key Type | Prefix | Use Case |
|---|---|---|
| Server Key | latlng_ |
All APIs β geocoding, reverse, places, tiles. Keep secret in backend code. |
| Maps Key | pk_latlng_ |
Map tiles, static maps, and autosuggest. Safe to embed in client-side code. Can be domain-restricted. |
Header Authentication (Server Key)
X-Api-Key: latlng_your_key_here
Query Parameter (Maps Key)
https://tiles.latlng.work/v1/metadata?key=pk_latlng_your_key
The X-Api-Key header is the recommended format. You can also pass the key as a query parameter (?api_key=YOUR_KEY) for backward compatibility.
Get your API keys from the dashboard.
π Forward Geocoding
Convert an address or place name into geographic coordinates.
Parameters
| Parameter | Type | Description |
|---|---|---|
q required |
string | The address or place name to geocode |
limit |
integer | Maximum number of results (default: 10) |
lang |
string | Language for results (e.g., "en", "de", "fr") |
Example Request
curl "https://api.latlng.work/api?q=Seattle,WA" \ -H "X-Api-Key: your_api_key"
Example Response
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [-122.3321, 47.6062]
},
"properties": {
"name": "Seattle",
"state": "Washington",
"country": "United States",
"type": "city"
}
}
]
}
π Reverse Geocoding
Convert geographic coordinates into a human-readable address.
Parameters
| Parameter | Type | Description |
|---|---|---|
lat required |
number | Latitude coordinate |
lon required |
number | Longitude coordinate |
Example Request
curl "https://api.latlng.work/reverse?lat=47.6062&lon=-122.3321" \ -H "X-Api-Key: your_api_key"
π Places Nearby
Find points of interest near a location. Places data is powered by OpenStreetMap via Photon.
Parameters
| Parameter | Type | Description |
|---|---|---|
lat required |
number | Latitude of the center point |
lon required |
number | Longitude of the center point |
radius |
integer | Search radius in meters (default: 1000, max: 5000) |
category |
string | Filter by category (e.g., "hotel", "restaurant") |
limit |
integer | Maximum results to return (default: 20) |
Example Request
curl "https://api.latlng.work/v1/places/nearby?lat=47.6062&lon=-122.3321&radius=1000&limit=5" \ -H "X-Api-Key: your_api_key"
Example Response
{
"type": "nearby",
"center": { "lat": 47.6062, "lon": -122.3321 },
"radius": 1000,
"count": 2,
"places": [
{
"name": "Pike Place Market",
"lat": 47.6097,
"lon": -122.3425,
"category": "marketplace",
"distance_m": 328
},
{
"name": "Starbucks Reserve Roastery",
"lat": 47.6140,
"lon": -122.3281,
"category": "cafe",
"distance_m": 912
}
]
}
π Places Search
Search for places by name with optional location bias and category filtering.
Parameters
| Parameter | Type | Description |
|---|---|---|
q required |
string | Search query (e.g., "Starbucks") |
lat |
number | Latitude for location bias |
lon |
number | Longitude for location bias |
category |
string | Filter by category |
country |
string | Filter by country code (e.g., "US") |
limit |
integer | Maximum results (default: 10) |
Example Request
curl "https://api.latlng.work/v1/places/search?q=Starbucks&lat=47.60&lon=-122.33&limit=5" \ -H "X-Api-Key: your_api_key"
Example Response
{
"type": "search",
"query": "Starbucks",
"count": 2,
"places": [
{
"name": "Starbucks",
"lat": 47.6101,
"lon": -122.3420,
"category": "cafe",
"distance_m": 845,
"match_score": 1.0
}
]
}
π‘ Places Autosuggest
Get place name suggestions as the user types. Ideal for search-as-you-type UIs.
Parameters
| Parameter | Type | Description |
|---|---|---|
q required |
string | Partial query (min 2 characters) |
lat |
number | Latitude for location bias |
lon |
number | Longitude for location bias |
limit |
integer | Maximum suggestions (default: 5) |
Example Request
curl "https://api.latlng.work/v1/places/autosuggest?q=star&lat=47.60&lon=-122.33" \ -H "X-Api-Key: your_api_key"
Example Response
{
"type": "autosuggest",
"query": "star",
"suggestions": [
{ "name": "Starbucks", "category": "cafe", "similarity": 0.85 },
{ "name": "Star Nails", "category": "beauty_salon", "similarity": 0.72 }
]
}
π Places Categories
List all available place categories you can use to filter results. Categories are based on OpenStreetMap tags. View full categories list β
Example Request
curl "https://api.latlng.work/v1/places/categories" \ -H "X-Api-Key: your_api_key"
Example Response
{
"categories": [
{ "name": "restaurant", "count": 584210 },
{ "name": "cafe", "count": 312045 },
{ "name": "hotel", "count": 198432 }
]
}
πΊοΈ Map Tiles API
Serve OpenStreetMap-based vector tiles for MapLibre GL JS. Uses a Maps Key (pk_latlng_) passed as a query parameter.
TileJSON Metadata
Returns a TileJSON spec describing tile sources, bounds, and attribution. This is the URL you pass to MapLibre's source.url.
Vector Tiles
Returns Protobuf-encoded vector tiles. Normally accessed by MapLibre automatically β you don't call this directly.
MapLibre GL JS Integration
<link rel="stylesheet" href="https://unpkg.com/maplibre-gl/dist/maplibre-gl.css">
<script src="https://unpkg.com/maplibre-gl/dist/maplibre-gl.js"></script>
<script src="https://unpkg.com/protomaps-themes-base"></script>
<div id="map" style="width:100%; height:400px;"></div>
<script>
const map = new maplibregl.Map({
container: 'map',
style: {
version: 8,
glyphs: 'https://protomaps.github.io/basemaps-assets/fonts/{fontstack}/{range}.pbf',
sprite: 'https://protomaps.github.io/basemaps-assets/sprites/v4/light',
sources: {
protomaps: {
type: 'vector',
url: 'https://tiles.latlng.work/v1/metadata?key=pk_latlng_YOUR_KEY'
}
},
layers: protomapsL.layers('protomaps', protomapsL.namedFlavor('light'), { lang: 'en' })
},
center: [-122.33, 47.61],
zoom: 13
});
</script>
β οΈ Rate Limits
Usage is tracked per-user across all API keys with a unified quota β all API calls (geocoding, reverse, places, tiles) count toward one shared pool.
| Plan | Quota | Period |
|---|---|---|
| Free | 3,000 total API calls | Per day |
| Pro | 1,000,000 total API calls | Per month |
| Enterprise | Custom | Custom |
Every geocode, reverse geocode, places search, and tile request counts equally toward your quota.
Rate limit headers are included in every response:
| Header | Description |
|---|---|
X-RateLimit-Limit |
Your plan's total request limit for the current period |
X-RateLimit-Remaining |
Requests remaining in current period (across all API types) |
πΊοΈ Static Maps API
Generate a static map image (PNG or JPEG) from a URL. Great for email previews, social cards, and server-side rendering.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
key * | string | β | Your API key (header or query param) |
center | string | β | Map center as lng,lat. Required unless using bbox. |
zoom | number | β | Zoom level 0β20. Required unless using bbox. |
bbox | string | β | Auto-fit to bounding box minLng,minLat,maxLng,maxLat. Overrides center/zoom. |
width | integer | 800 | Image width in pixels (max 2048) |
height | integer | 600 | Image height in pixels (max 2048) |
style | string | dark | light Β· dark Β· grayscale Β· black Β· white Β· contrast |
format | string | png | png or jpeg |
markers | string | β | Pipe-separated: lng,lat,color,label|lng,lat,color,label |
path | string | β | Polyline: weight:color:opacity|lng,lat|lng,lat|... (repeatable) |
geojson | string | β | URL-encoded GeoJSON to overlay on the map |
Examples
Simple map centered on NYC:
https://api.latlng.work/v1/static ?center=-74.006,40.7128 &zoom=12 &width=800&height=400 &style=dark &key=YOUR_KEY
Map with a marker:
https://api.latlng.work/v1/static ?center=-74.006,40.7128 &zoom=14 &markers=-74.006,40.7128,22c55e,A &key=YOUR_KEY
Auto-fit to a bounding box:
https://api.latlng.work/v1/static ?bbox=-74.25,40.49,-73.7,40.92 &width=1200&height=600 &style=light &key=YOUR_KEY
JavaScript fetch example:
const params = new URLSearchParams({
center: '-74.006,40.7128',
zoom: '12',
width: '800',
height: '400',
style: 'dark',
key: 'YOUR_KEY'
});
const img = document.createElement('img');
img.src = `https://api.latlng.work/v1/static?${params}`;
document.body.appendChild(img);
π¦ Dataset Tiles API
Upload your own geodata (GeoJSON, GPX, KML, Shapefile, GeoPackage) via the dashboard and serve it as vector tiles. Access requires a Maps key (pk_latlng_...) that belongs to the dataset owner.
Tile Endpoint
Metadata Endpoint
| Parameter | Type | Description |
|---|---|---|
id * | string | Dataset ID from the dashboard (e.g. ds_abc123...) |
z * | integer | Tile zoom level (0β14) |
x * | integer | Tile X coordinate |
y * | integer | Tile Y coordinate |
key * | string | Your Maps key (pk_latlng_...) as a query parameter |
Usage with Leaflet
const MY_KEY = 'pk_latlng_your_maps_key';
const DATASET_ID = 'ds_your_dataset_id';
const map = L.map('map').setView([40.71, -74.01], 12);
// Base tiles
L.tileLayer(`https://tiles.latlng.work/v1/tiles/{z}/{x}/{y}.pbf?key=${MY_KEY}`).addTo(map);
// Your dataset as a vector tile layer (requires Leaflet.VectorGrid)
L.vectorGrid.protobuf(
`https://tiles.latlng.work/v1/datasets/${DATASET_ID}/{z}/{x}/{y}.pbf?key=${MY_KEY}`,
{
vectorTileLayerStyles: {
[DATASET_ID]: { color: '#22c55e', weight: 2, fillOpacity: 0.4 }
}
}
).addTo(map);
Usage with MapLibre GL JS
const MY_KEY = 'pk_latlng_your_maps_key';
const DATASET_ID = 'ds_your_dataset_id';
map.addSource('my-dataset', {
type: 'vector',
tiles: [
`https://tiles.latlng.work/v1/datasets/${DATASET_ID}/{z}/{x}/{y}.pbf?key=${MY_KEY}`
],
minzoom: 0,
maxzoom: 14
});
map.addLayer({
id: 'my-dataset-layer',
type: 'fill',
source: 'my-dataset',
'source-layer': DATASET_ID,
paint: {
'fill-color': '#22c55e',
'fill-opacity': 0.5
}
});
Plan Limits
| Plan | Datasets | Max File Size |
|---|---|---|
| Free | 3 | 50 MB |
| Pro | 50 | 1 GB |
| Enterprise | Unlimited | 5 GB |
β Error Codes
| Code | Description |
|---|---|
400 |
Bad Request β Missing or invalid parameters |
401 |
Unauthorized β Invalid or missing API key |
403 |
Forbidden β Public key used on server-only endpoint, or domain not allowed |
429 |
Too Many Requests β Rate limit exceeded |
500 |
Internal Server Error |