Migrate · from Google Geocoding API
Migrate from the Google Geocoding API to geolith
To move from the Google Geocoding API, replace /maps/api/geocode/json?address= with https://api.geolith.app/v1/search?text= and latlng= with /v1/reverse?point.lat=&point.lon=. Read coordinates from features[].geometry.coordinates ([lon, lat]) instead of results[].geometry.location. In a check on 194 real India queries, geolith's first result was within 1 km of Google's for 113 (58 %). geolith results may be stored permanently.
Requests
Request mapping
GET /maps/api/geocode/json?address=…
→ GET /v1/search?text=…
GET /maps/api/geocode/json?latlng=lat,lng
→ GET /v1/reverse?point.lat=…&point.lon=…
key=…
→ api_key=… or Authorization: Bearer …
components=country:IN
→ boundary.country=IN
components=postal_code:…|locality:…
→ GET /v1/search/structured?postalcode=…&locality=…
language=hi
→ lang=hi
Self-hosted server; hosted API planned.
bounds= / region= (bias)
→ focus.point.lat= & focus.point.lon=
A point bias, not a box.
place_id lookup
→ GET /v1/place?ids=geolith:<layer>:<id>
Places Autocomplete (separate API)
→ GET /v1/autocomplete?text=…
Same key, same plan.
| Google Geocoding API | geolith | Note |
|---|---|---|
| GET /maps/api/geocode/json?address=… | GET /v1/search?text=… | |
| GET /maps/api/geocode/json?latlng=lat,lng | GET /v1/reverse?point.lat=…&point.lon=… | |
| key=… | api_key=… or Authorization: Bearer … | |
| components=country:IN | boundary.country=IN | |
| components=postal_code:…|locality:… | GET /v1/search/structured?postalcode=…&locality=… | |
| language=hi | lang=hi | Self-hosted server; hosted API planned. |
| bounds= / region= (bias) | focus.point.lat= & focus.point.lon= | A point bias, not a box. |
| place_id lookup | GET /v1/place?ids=geolith:<layer>:<id> | |
| Places Autocomplete (separate API) | GET /v1/autocomplete?text=… | Same key, same plan. |
Responses
Response mapping
status: "OK" / "ZERO_RESULTS"
→ HTTP 200; features: [] when nothing matches
results[]
→ features[]
results[].formatted_address
→ features[].properties.label
results[].geometry.location.lat / .lng
→ features[].geometry.coordinates[1] / [0]
results[].place_id
→ features[].properties.gid
address_components (postal_code)
→ properties.postalcode
address_components (locality)
→ properties.locality
address_components (administrative_area_level_1)
→ properties.region
address_components (country short_name)
→ properties.country_code
types[]
→ properties.layer and properties.category[]
geometry.viewport
→ features[].bbox when the place has one; collection bbox
| Google Geocoding API | geolith | Note |
|---|---|---|
| status: "OK" / "ZERO_RESULTS" | HTTP 200; features: [] when nothing matches | |
| results[] | features[] | |
| results[].formatted_address | features[].properties.label | |
| results[].geometry.location.lat / .lng | features[].geometry.coordinates[1] / [0] | |
| results[].place_id | features[].properties.gid | |
| address_components (postal_code) | properties.postalcode | |
| address_components (locality) | properties.locality | |
| address_components (administrative_area_level_1) | properties.region | |
| address_components (country short_name) | properties.country_code | |
| types[] | properties.layer and properties.category[] | |
| geometry.viewport | features[].bbox when the place has one; collection bbox |
Code
Before and after
const url = new URL('https://maps.googleapis.com/maps/api/geocode/json');
url.searchParams.set('address', 'eiffel tower');
url.searchParams.set('components', 'country:FR');
url.searchParams.set('key', GOOGLE_KEY);
const data = await (await fetch(url)).json();
if (data.status !== 'OK') return null;
const r = data.results[0];
return { label: r.formatted_address, lat: r.geometry.location.lat, lon: r.geometry.location.lng };const url = new URL('https://api.geolith.app/v1/search');
url.searchParams.set('text', 'eiffel tower');
url.searchParams.set('boundary.country', 'FR');
url.searchParams.set('size', '1');
const res = await fetch(url, { headers: { Authorization: `Bearer ${GEOLITH_KEY}` } });
const { features } = await res.json();
if (features.length === 0) return null;
const f = features[0];
return { label: f.properties.label, lat: f.geometry.coordinates[1], lon: f.geometry.coordinates[0] };Detail
What changes
Coordinates come as GeoJSON, longitude first. The label is one string. Place ids are geolith gids, not Google place ids, so re-geocode stored ids once.
- Data is OpenStreetMap and Overture Maps. Show: © OpenStreetMap contributors (ODbL), Overture Maps Foundation.
- Results may be stored permanently. Google charges extra for that.
- Do not expect identical results. 58 % of first results were within 1 km of Google's in our India check; geolith is not claimed to be better than Google.
Questions
Migrating from Google Geocoding API: questions
- Is geolith a drop-in replacement for the Google Geocoding API?
- No. The request and response shapes differ, as the tables on this page show. The change is usually one function: build a different URL and read features[] instead of results[].
- How close are the results to Google's?
- On 194 real India queries, geolith's first result was within 1 km of Google's for 113 (58 %). Photon was about 18 %, Nominatim about 12 %, Pelias about 8 % (first 110 queries).
Geocode your first address in a minute.
100,000 requests a month free. Commercial use with attribution. No card.