Guide

Geocode a CSV without an API key

A spreadsheet of addresses in, latitude and longitude out, on your own machine. No sign-up, no quota, no per-row bill.

In Python

geocode_csv.pyPython
import csv
import gridpin
from gridpin_mcp import sheets

g = gridpin.Geocoder(str(sheets.download("rs")))   # pick the country of your addresses

with open("addresses.csv", newline="", encoding="utf-8") as f:
    rows = list(csv.DictReader(f))                 # needs a column named "address"

hits = g.geocode_many([r["address"] for r in rows], 1)   # spreads the list across all cores

with open("geocoded.csv", "w", newline="", encoding="utf-8") as f:
    out = csv.DictWriter(f, fieldnames=[*rows[0], "lat", "lon", "precision"])
    out.writeheader()
    for row, found in zip(rows, hits):
        best = found[0] if found else {}
        out.writerow({**row, "lat": best.get("lat", ""), "lon": best.get("lon", ""),
                      "precision": best.get("precision", "")})

An address the file cannot place gets empty lat and lon, not a guess. Filter on precision: house is a door-level match.

In DuckDB

geocode.sqlSQL
INSTALL gridpin_ext FROM community;
LOAD gridpin_ext;
SELECT gridpin_load('serbia.bin');

CREATE TABLE orders AS SELECT * FROM read_csv('addresses.csv');

COPY (
  SELECT id, address,
         (hit ->> 'lat')::DOUBLE AS lat,
         (hit ->> 'lon')::DOUBLE AS lon,
         hit ->> 'precision'      AS precision
  FROM (SELECT *, gridpin_geocode(address)::JSON AS hit FROM orders)
) TO 'geocoded.csv';

Use the path that gridpin-fetch rs printed. More on the extension: geocode inside DuckDB.