Seven clients, one line each

Whatever the language of the program that needs an address, the request is the same: a GET to /search with the address in q. Here is that line seven times.

Run it yourself Server, Docker Python JavaScript

What you have

A running server: gridpin serve usa.bin --port 8080. Each line below asks it for the same Chicago address and prints the point.

Steps

1. curl, the whole answer in Nominatim's jsonv2:

terminalCLI
$ curl -s "http://127.0.0.1:8080/search?q=233+S+Wacker+Dr,+Chicago,+IL+60606&format=jsonv2&limit=1"
[{"boundingbox":["41.8781580","41.8791580","-87.6363684","-87.6353684"],"category":"place","display_name":"233 South WACKER Drive, Chicago, 60606","importance":0.95,"lat":"41.8786580","licence":"National Address Database (USDOT) and state and county open data, datasets: NAD, us/ma/statewide, us/ca/los_angeles, us/mn/statewide, us/or/statewide, us/ny/city_of_new_york, us/fl/hillsborough, us/ca/sacramento, us/mi/oakland, us/ca/fresno, us/ga/dekalb, us/ca/san_francisco, us/fl/manatee, us/id/ada, us/la/city_of_new_orleans, us/pa/lackawanna, us/ga/clarke, us/ca/napa, us/ca/nevada, us/wi/waupaca; via Overture Maps / OpenAddresses, release 2026-09-23.1; address normalization applied; partial coverage (sources with an explicit right to redistribute); settlement names © GeoNames (CC BY 4.0)","lon":"-87.6358684","place_id":5691876238618044,"place_rank":30,"type":"house"}]

2. Python with geopy (the domain and scheme arguments):

terminalCLI
$ python3 -c 'from geopy.geocoders import Nominatim; p = Nominatim(user_agent="my-app", domain="127.0.0.1:8080", scheme="http").geocode("233 S Wacker Dr, Chicago, IL 60606"); print(p.latitude, p.longitude)'
41.878658 -87.6358684

3. Python with requests:

terminalCLI
$ python3 -c 'import requests; print(requests.get("http://127.0.0.1:8080/search", params={"q": "233 S Wacker Dr, Chicago, IL 60606", "format": "json"}).json()[0]["lat"])'
41.8786580

4. Node.js 18 or newer, with the built-in fetch:

terminalCLI
$ node -e 'fetch("http://127.0.0.1:8080/search?format=json&q=" + encodeURIComponent("233 S Wacker Dr, Chicago, IL 60606")).then(r => r.json()).then(j => console.log(j[0].lat, j[0].lon))'
41.8786580 -87.6358684

5. PHP, with no package:

terminalCLI
$ php -r '$j = json_decode(file_get_contents("http://127.0.0.1:8080/search?format=json&q=" . urlencode("233 S Wacker Dr, Chicago, IL 60606"))); echo $j[0]->lat, " ", $j[0]->lon, "\n";'
41.8786580 -87.6358684

6. Ruby, with the standard library:

terminalCLI
$ ruby -rnet/http -rjson -e 'u = URI("http://127.0.0.1:8080/search"); u.query = URI.encode_www_form(q: "233 S Wacker Dr, Chicago, IL 60606", format: "json"); j = JSON.parse(Net::HTTP.get(u)); puts "#{j[0]["lat"]} #{j[0]["lon"]}"'
41.8786580 -87.6358684

7. R, with base R only:

terminalCLI
$ Rscript -e 'j <- readLines(url(paste0("http://127.0.0.1:8080/search?format=json&q=", URLencode("233 S Wacker Dr, Chicago, IL 60606", reserved = TRUE))), warn = FALSE); cat(regmatches(j, gregexpr("\"(lat|lon)\":\"[^\"]*\"", j))[[1]], "\n")'
"lat":"41.8786580" "lon":"-87.6358684"

What you get

lat and lon come back as strings, as Nominatim sends them; geopy turns them into numbers. The answer also carries display_name, address with addressdetails=1, and the licence of the data.

Why it works

The server answers Nominatim's paths and formats, so anything that can make an HTTP request is a client: no SDK, no key, and nothing to change but the host.

Limits

  • Run with Python 3.11, geopy 2.5.0, requests 2.34.2, Node.js 24.9.0, PHP 8.5.7, Ruby 2.6.10 and R 4.6.2.
  • Libraries that wrap Nominatim (geocoder-php, node-geocoder and others) take the same host setting; they were not run here and are not listed.
  • One country per server.

All its keys: command reference. The function: local server.