Geocoding API
CSV2GEO provides a REST API with 48 endpoints for geocoding, reverse geocoding, places search, administrative boundaries (including postcode → polygon, ancestors walk-up, children walk-down, consolidated cities), IP geolocation, address autocomplete, and account management.
Try in ChatGPT
No code needed — geocode addresses directly in ChatGPT. Type an address or drop a CSV/Excel file.
Getting Your API Key
- Log in to csv2geo.com
- Go to API Keys in the sidebar
- Click Create API Key
- Copy your key — it starts with
geo_live_
Free tier: 3,000 API requests per day, no credit card required.
API Endpoints
The endpoints below are the full set callable from https://csv2geo.com/api/v1/ with a geo_live_* key — i.e. what the Python SDK and Node SDK reach by default.
Geocoding & address tools
| Method | Endpoint | Description |
|---|---|---|
| GET | /v1/geocode | Forward geocode a single address |
| POST | /v1/geocode | Batch forward geocode (up to 10,000) |
| GET | /v1/reverse | Reverse geocode a single coordinate |
| POST | /v1/reverse | Batch reverse geocode (up to 10,000) |
| GET | /v1/autocomplete | Address autocomplete |
| GET | /v1/validate | Check whether an address resolves to a real location |
| POST | /v1/validate | Batch validate addresses |
| GET | /v1/parse | Parse a free-form address into components |
| POST | /v1/parse | Batch parse addresses (up to 1,000) |
| GET | /v1/normalize | Normalize an address to USPS Publication 28 parts (parse-only, no geocoding) |
| POST | /v1/normalize | Batch normalize addresses (up to 1,000) |
| GET | /v1/standardize | Normalize an address into canonical form |
| POST | /v1/addresses/compare | Compare two addresses for similarity |
| GET | /v1/addresses/nearby | Find addresses within radius of a coordinate |
| GET | /v1/addresses/street | List addresses on a given street + postcode |
| GET | /v1/addresses/random | Random sample of addresses (testing) |
| GET | /v1/addresses/stats | Address-database statistics |
| GET | /v1/addresses/interpolate | Interpolate a coordinate from address-range data |
| GET | /v1/addresses/crossstreet | Find the cross-street nearest to a coordinate |
Places (POIs)
| Method | Endpoint | Description |
|---|---|---|
| GET | /v1/places | Search 72M+ places by name |
| GET | /v1/places/nearby | Find places near a coordinate |
| GET | /v1/places/by-id/{id} | Get place details by ID |
| GET | /v1/places/categories | List all place categories |
| GET | /v1/places/brands | Search places by brand |
| GET | /v1/places/chain | All locations of a brand/chain |
| GET | /v1/places/count | Count places matching criteria |
| GET | /v1/places/similar | Find places similar to a given place |
| GET | /v1/places/random | Random sample places |
| GET | /v1/places/stats | Database statistics by country/category |
| POST | /v1/places/batch | Batch lookup multiple places by ID |
Administrative boundaries (Divisions)
| Method | Endpoint | Description |
|---|---|---|
| GET | /v1/divisions | Search administrative boundaries by name |
| GET | /v1/divisions/contains | Find what division contains a point |
| GET | /v1/divisions/by-postcode | Postcode → polygon + bbox + population in one call |
| GET | /v1/divisions/by-id/{id} | Get division by ID |
| GET | /v1/divisions/ancestors/{id} | Walk-up "part-of" chain to country |
| GET | /v1/divisions/children/{id} | Walk-down: immediate sub-divisions |
| GET | /v1/divisions/consolidated/{id} | Consolidated cities (e.g. NYC = 5 boroughs) |
| GET | /v1/divisions/hierarchy/{id} | Full hierarchy tree from a node |
| GET | /v1/divisions/subtypes | List division subtypes (locality, county, region, etc.) |
| GET | /v1/divisions/countries | List countries with division data |
| GET | /v1/divisions/random | Random sample divisions |
| GET | /v1/divisions/stats | Division-database statistics |
IP geolocation
| Method | Endpoint | Description |
|---|---|---|
| GET | /v1/ip | IP → country, region, city, county, ASN |
| GET | /v1/ip/me | Geolocate the requester's own IP |
| POST | /v1/ip/batch | Batch IP lookup (up to 1,000) |
Routing & Navigation Pro+ only
Powered by a self-hosted Valhalla engine over the global OpenStreetMap network. All 5 modes share one tile build (drive, truck, walk, bike, motorcycle). Pro plan and above; Free/Growth keys return 403 plan_permission_denied.
| Method | Endpoint | Description |
|---|---|---|
| GET | /v1/routing | Turn-by-turn route through 2-25 waypoints. Supports truck attrs (height/weight/HAZMAT), time-aware ETA, up to 3 alternate routes, 25+ narration languages. |
| GET | /v1/isoline | Reachability polygon(s) — 1-3 ranges per call, time (≤ 3 600 s) or distance (≤ 50 000 m). |
| POST | /v1/route-matrix | N×M distance + duration matrix up to 10 000 cells. |
| POST | /v1/map-match | Snap a GPS trace (2-1 000 points) to the road network. |
| GET | /v1/optimize_route | TSP-style stop ordering for up to 20 waypoints. |
| GET | /v1/locate | Snap a single point to the nearest road; returns way_id, road_class, surface, speed_limit_kmh. |
| GET | /v1/elevation | Per-point elevation lookup (up to 500 points). |
Quick example — point-to-point routing
curl 'https://csv2geo.com/api/v1/routing?waypoints=40.7128,-74.006|34.0522,-118.2437&mode=drive&api_key=YOUR_KEY'
# → distance_m: 4_496_675 (km 4 497), duration_s: 143_484 (h 39.9), geometry: LineStringTruck-aware routing with height restriction
curl 'https://csv2geo.com/api/v1/routing?waypoints=51.5074,-0.1278|48.8566,2.3522&mode=truck&truck_height=4.5&api_key=YOUR_KEY'
# → London → Paris truck route with 4.5m height clearance; auto-routes via Eurotunnel/ferryCoverage & account
| Method | Endpoint | Description |
|---|---|---|
| GET | /v1/coverage | Per-country boundary tier + counts |
| GET | /v1/coverage-stats | Aggregate dataset stats (no API key required) |
| GET | /v1/me | Account info, plan, and rate limits |
Utilities
| Method | Endpoint | Description |
|---|---|---|
| GET | /v1/timezone | IANA timezone for a coordinate |
| GET | /v1/distance | Haversine distance between two coordinates |
Maps
| Method | Endpoint | Description |
|---|---|---|
| GET | /v1/tile/{source}/{z}/{x}/{y}.pbf | Vector map tile (planet OpenStreetMap) — 0.25 credits/tile |
| GET | /v1/tile/styles/{name}.json | MapLibre GL style document (9 ready-made styles) — free |
| GET | /v1/staticmap | Server-side rendered map image (PNG/JPEG/WebP) with marker, polyline and numbered-pin overlays — 1 credit/render |
| GET | /v1/icon | Marker pin PNG (168 Font Awesome icons OR numbered pin via type=pin&label=) — free |
| GET | /v1/property/image | Per-address aerial photograph (USGS NAIP, US-only) — 1 credit/render |
Numbered pins on /v1/staticmap — pass an optional label slot (1-4 alphanumeric chars) on any marker. Two equivalent syntaxes:
markers=lat,lng,red,7 (positional)
markers=lat,lng,color:red,label:7 (keyed; also supports hex like color:#ff8800)Used for printed walklist PDFs, tour maps, route-stop visualizations.
Per-address property image — pass a free-text US address or coordinates and get back a sharp aerial photo of the property:
GET /v1/property/image?q=3168+Beckie+Dr+SW,+Wyoming,+MI&size=350&api_key=...
GET /v1/property/image?lat=42.86753&lng=-85.7419&size=350&api_key=...size is the bbox edge length in meters (1-2000, default 350 ≈ a typical residential lot block). format is png (default), jpg, or webp. US coverage only — non-US coordinates return 400 out_of_coverage. Use cases: real-estate listings, insurance underwriting, appraisal research, computer-vision substrate for roof/pool/solar detection.
Result-quality rank block
Both geocoding and division responses include a rank block — a Geoapify-shaped object describing the strength of the result. Different fields appear depending on the endpoint:
- Geocoding (
/geocode,/reverse): emitsconfidence(0-1, match strength) andmatch_type(e.g.full_match,fuzzy_match). - Divisions (
/divisions/*): emitsimportance(0-1, derived from Wikidata sitelink count — Tokyo, Paris, NYC ≈ 1.0; small rural divisions ~0.2). Use it to disambiguate when multiple divisions match a query. About 1.02M of 4.6M divisions have a value (those with a Wikidata Q-ID).
Full scoring methodology
For the complete breakdown of how accuracy_score, accuracy, and confidence are computed — every search phase, every cross-validation penalty, every reverse-distance band, every source value — see Accuracy & Scoring. Includes the ?radius= parameter for reverse geocoding (default 100m, opt-in to 1500m).
# Geocode rank: confidence + match_type
curl "https://csv2geo.com/api/v1/geocode?q=90210&country=US&api_key=YOUR_KEY" | jq '.results[0].rank'
# → { "confidence": 1, "match_type": "full_match" }
# Division rank: importance only
curl "https://csv2geo.com/api/v1/divisions/by-id/{nyc_id}?api_key=YOUR_KEY" | jq '.result.rank'
# → { "importance": 1 }Normalize addresses (/v1/normalize)
Clean a raw address into USPS Publication 28 parts before you store or geocode it. It is parse-only: no geocoding, no database lookup, no coordinates, and it never invents a house number. It is deterministic and idempotent — normalizing a clean address returns it unchanged.
| What it does | Example |
|---|---|
Directionals to N S E W NE NW SE SW | Northwest, N.W., N W → NW |
| Street suffix to the Pub 28 standard | Street, Strt → ST; Avenue, Avn → AVE; Boulevard → BLVD |
| Unit designators | Apartment 4, #4, Apt4 → APT 4; Suite 200 → STE 200 |
| State to 2 letters | Alabama → AL |
| ZIP | 35801-1234 split into zip + zip4; a lost leading zero (2134) restored to 02134 |
| PO boxes / rural routes | recognised and returned in po_box / rural_route with a reason |
curl -X POST "https://csv2geo.com/api/v1/normalize" \
-H "Authorization: Bearer YOUR_KEY" -H "Content-Type: application/json" \
-d '{"addresses":[{"id":"row-17","address":"123 Stewart Street Northwest","address2":"apartment 4","city":"huntsville","state":"Alabama","zip":"35801"}]}'{
"results": [{
"id": "row-17", "ok": true, "changed": true,
"house_number": "123", "predirectional": "", "street_name": "STEWART",
"street_suffix": "ST", "postdirectional": "NW",
"unit_type": "APT", "unit": "4",
"city": "HUNTSVILLE", "state": "AL", "zip": "35801", "zip4": "", "country": "US",
"formatted": "123 STEWART ST NW APT 4, HUNTSVILLE AL 35801",
"changes": ["state ALABAMA to AL", "postdirectional NORTHWEST to NW", "suffix STREET to ST"],
"confidence": "high"
}],
"meta": { "total": 1, "successful": 1, "failed": 0 }
}- Input: each item is a freeform string,
{ "q": "..." }, or structured fields{id, address, address2, city, state, zip, country}.idis echoed back. Single address:GET /v1/normalize?q=...or?address=...&city=...&state=...&zip=.... - Batch: up to 1,000 addresses per request, results in request order. For larger files send chunks of 1,000.
- Bad rows never error: a row with nothing addressable returns
ok: falseand areason(empty,unparseable,no_street_name); a row that is a street without a number returnsok: true,reason: "no_house_number"; non-US rows are passed through withreason: "non_us_not_normalized". changed/changeslist what was rewritten (case and spacing alone do not count), so you can show your users what was fixed.- Cost: 1 credit for
GET, 0.5 credit per address in aPOSTbatch — the same as/v1/parse. - Known limits: a suffix word inside a name that does not end in one (
County Road 12) is not abbreviated; a letter that is really an alphabet indicator rather than a directional cannot be told apart.
Multi-language names
Pass ?lang= (BCP-47 tag) on any places, boundaries, geocode, or reverse-geocode endpoint to get the localized name. Pass ?include=other_names (composable with other includes) to receive the full translation map.
# Place name in Japanese
curl "https://csv2geo.com/api/v1/places/{id}?lang=ja&api_key=YOUR_KEY"
# → "name": "CoCo壱番屋"
# Full translation map on a division
curl "https://csv2geo.com/api/v1/divisions/by-postcode?code=70173&country=DE&include=other_names&api_key=YOUR_KEY"
# → "other_names": { "en": "Stuttgart", "fr": "Stuttgart", "ja": "シュトゥットガルト", ... }
# Geocode with translated admin names (Sprint 2.1c, 2026-05-08)
curl "https://csv2geo.com/api/v1/geocode?q=1010&country=AT&lang=de&api_key=YOUR_KEY"
# → components.country: "Österreich", components.city: "Wien"
# Geocode with full per-level translation maps (Sprint 2.1d)
curl "https://csv2geo.com/api/v1/geocode?q=1010&country=AT&include=other_names&api_key=YOUR_KEY"
# → other_names: {
# "country": { "en": "Austria", "de": "Österreich", "fr": "Autriche", ... 265 langs },
# "region": { ... },
# "locality": { ... }
# }Coverage today:
- Divisions: 1.5M divisions across ~78 languages on average (Overture
names.common) - Places: 234,000 places across 17 languages (Overture
names.rules) - Geocode + reverse: translates the embedded admin-level names (city/state/country/district) by walking the parent_division chain. Street + house_number stay in source language because Overture has no address-level translation data — Geoapify works the same way.
- Falls back to base language (e.g.
pt-BR→pt) then to the primary name
Quick Start
Forward Geocode (Address → Coordinates)
curl "https://csv2geo.com/api/v1/geocode?q=1600+Pennsylvania+Ave,+Washington+DC&country=US&api_key=YOUR_KEY"Reverse Geocode (Coordinates → Address)
curl "https://csv2geo.com/api/v1/reverse?lat=40.7484&lng=-73.9857&api_key=YOUR_KEY"Python SDK
pip install csv2geofrom csv2geo import Client
client = Client("YOUR_API_KEY")
# Forward geocode
result = client.geocode("1600 Pennsylvania Ave, Washington DC", country="US")
print(f"{result.lat}, {result.lng}")
# Reverse geocode
result = client.reverse(lat=40.7484, lng=-73.9857)
print(result.formatted_address)
# Normalize to USPS Pub 28 form (parse-only, no geocoding)
out = client.normalize_batch([{"id": "1", "address": "123 Stewart Street Northwest",
"city": "Huntsville", "state": "Alabama", "zip": "35801"}])
print(out["results"][0]["formatted"]) # 123 STEWART ST NW, HUNTSVILLE AL 35801Node.js SDK
npm install csv2geo-sdkconst { Client } = require('csv2geo-sdk');
const client = new Client({ apiKey: 'YOUR_API_KEY' });
const result = await client.geocode('1600 Pennsylvania Ave, Washington DC', { country: 'US' });
console.log(result.lat, result.lng);
// Normalize to USPS Pub 28 form (parse-only, no geocoding)
const out = await client.normalizeBatch([{ id: '1', address: '123 Stewart Street Northwest', city: 'Huntsville', state: 'Alabama', zip: '35801' }]);
console.log(out.results[0].formatted); // 123 STEWART ST NW, HUNTSVILLE AL 35801Import API Collections
Test all 48 endpoints instantly in your favorite API tool:
- Postman Collection — Import into Postman, all endpoints pre-configured
- Insomnia Collection — Import into Insomnia with environment
- OpenAPI Spec — Import into Swagger, Hoppscotch, Bruno, or any tool
Full Documentation
- API Documentation — Interactive docs with live testing
- GitHub — Source code, SDKs, and examples
- RapidAPI — Test and subscribe via RapidAPI Hub
Rate Limits
| Tier | Requests/day | Batch Size |
|---|---|---|
| Free | 1,000 | 100 |
| Starter | 50,000 | 1,000 |
| Growth | 250,000 | 5,000 |
| Pro | 1,000,000 | 10,000 |
Check response headers for your current usage:
X-RateLimit-Limit— Maximum requests per minuteX-RateLimit-Remaining— Requests remainingX-RateLimit-Reset— When the window resets