# Reverse geocoding in JavaScript

> Turn latitude and longitude into an address in the browser and in Node.js with the geolith reverse geocoding API, including the user's current location and error handling.

---
title: Reverse geocoding in JavaScript
description: Turn latitude and longitude into an address in the browser and in Node.js with the geolith reverse geocoding API, including the user's current location and error handling.
date: '2026-10-22'
tags: [javascript, node, reverse geocoding]
minutes: 8
steps:
  - name: Get an API key
    text: Create a publishable key for browser code or a secret key for Node.js at console.geolith.app.
  - name: Call /v1/reverse
    text: Send point.lat and point.lon. The API returns GeoJSON features nearest first, each with properties.distance in kilometres.
  - name: Read the label and address parts
    text: Use features[0].properties.label for display and the parts (street, locality, postalcode) for forms.
  - name: Handle errors and empty results
    text: Check res.ok, and treat an empty features array as "no address within the radius".
faqs:
  - q: What radius does reverse geocoding search?
    a: 1 km by default. Set boundary.circle.radius up to 100 (km) for rural points.
  - q: Why is the first result a shop and not a street address?
    a: Reverse returns the nearest places of any layer. Add layers=address for street addresses only, or layers=locality for the town.
  - q: Can I cache reverse geocoding results?
    a: Yes. geolith results may be stored permanently on every plan.
---

Reverse geocoding turns coordinates into a place: given a latitude and longitude, `GET https://api.geolith.app/v1/reverse?point.lat=…&point.lon=…` returns the nearest addresses and places as GeoJSON, nearest first, each with its distance in kilometres. Below are a browser example that names the user's current location and a Node.js function for servers.

## The request

```bash
curl "https://api.geolith.app/v1/reverse?point.lat=48.858262&point.lon=2.294496&size=1" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

```json
{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "geometry": { "type": "Point", "coordinates": [2.294496, 48.858262] },
      "properties": {
        "gid": "geolith:venue:4611686018432401268",
        "layer": "venue",
        "label": "Tour Eiffel, Paris 7e Arrondissement, Paris, Île-de-France, France",
        "distance": 0,
        "street": "Avenue Anatole France",
        "postalcode": "75007",
        "region": "Île-de-France",
        "country_code": "FR"
      }
    }
  ]
}
```

Parameters:

| Parameter | Meaning |
| --- | --- |
| `point.lat`, `point.lon` | The point. Required. |
| `boundary.circle.radius` | Search radius in km. Default 1, maximum 100. |
| `size` | Number of results, 1 to 40. Default 10. |
| `layers` | For example `address` (street addresses only) or `venue,address`. |
| `lang` | Names in a language, for example `hi`. |

## In the browser: "use my location"

Use a **publishable** key (`gc_pk_...`) with your site's origin on its allowed list.

```html
<button id="locate">Use my location</button>
<p id="where"></p>

<script type="module">
  const API_KEY = 'YOUR_API_KEY'; // publishable key, gc_pk_...

  async function reverseGeocode(lat, lon) {
    const url = new URL('https://api.geolith.app/v1/reverse');
    url.searchParams.set('point.lat', String(lat));
    url.searchParams.set('point.lon', String(lon));
    url.searchParams.set('size', '1');
    url.searchParams.set('layers', 'address,street,venue');
    url.searchParams.set('api_key', API_KEY);
    const res = await fetch(url);
    if (!res.ok) throw new Error(`geolith ${res.status}: ${await res.text()}`);
    const { features } = await res.json();
    return features[0] ?? null;
  }

  document.getElementById('locate').addEventListener('click', () => {
    const out = document.getElementById('where');
    navigator.geolocation.getCurrentPosition(
      async ({ coords }) => {
        try {
          const place = await reverseGeocode(coords.latitude, coords.longitude);
          out.textContent = place
            ? `${place.properties.label} (${Math.round(place.properties.distance * 1000)} m away)`
            : 'No address found within 1 km.';
        } catch (err) {
          out.textContent = err.message;
        }
      },
      (err) => (out.textContent = `Location unavailable: ${err.message}`),
      { enableHighAccuracy: true, timeout: 10000 },
    );
  });
</script>
```

Browsers only allow `navigator.geolocation` on HTTPS pages (and `localhost`).

## In Node.js

Use a **secret** key (`gc_sk_...`) from an environment variable. Node 18 and later have `fetch` built in.

```js
// reverse.mjs — run with: GEOLITH_API_KEY=YOUR_API_KEY node reverse.mjs 35.6586 139.7454
const API = 'https://api.geolith.app/v1/reverse';

export async function reverseGeocode(lat, lon, { radiusKm = 1, layers } = {}) {
  const url = new URL(API);
  url.searchParams.set('point.lat', String(lat));
  url.searchParams.set('point.lon', String(lon));
  url.searchParams.set('boundary.circle.radius', String(radiusKm));
  url.searchParams.set('size', '1');
  if (layers) url.searchParams.set('layers', layers);

  const res = await fetch(url, {
    headers: { Authorization: `Bearer ${process.env.GEOLITH_API_KEY}` },
  });
  if (res.status === 429) throw new Error('geolith: rate limited or monthly quota used up');
  if (!res.ok) throw new Error(`geolith ${res.status}: ${await res.text()}`);

  const { features } = await res.json();
  if (features.length === 0) return null;
  const f = features[0];
  return {
    label: f.properties.label,
    gid: f.properties.gid,
    layer: f.properties.layer,
    distanceKm: f.properties.distance,
    postalcode: f.properties.postalcode ?? null,
    lon: f.geometry.coordinates[0],
    lat: f.geometry.coordinates[1],
  };
}

const [lat, lon] = process.argv.slice(2).map(Number);
if (Number.isFinite(lat) && Number.isFinite(lon)) {
  console.log(await reverseGeocode(lat, lon, { radiusKm: 2 }));
}
```

## Places around a point

To list several places near a point (shops near a delivery partner, stations near a flat), call `/v1/nearby` with the same `point.lat` and `point.lon`. It searches up to 50 km and returns places nearest first:

```js
const url = new URL('https://api.geolith.app/v1/nearby');
url.searchParams.set('point.lat', '12.9716');
url.searchParams.set('point.lon', '77.5946');
url.searchParams.set('layers', 'venue');
url.searchParams.set('size', '10');
```

## Attribution

Show `© OpenStreetMap contributors (ODbL), Overture Maps Foundation` wherever you display the result. Coverage is worldwide.


Get a free API key: https://console.geolith.app/signup
