Skip to content

Data Engineering

Geocoding at Scale — Google vs Nominatim vs Mapbox When You Have 100K Addresses

When You're Geocoding 100K Insurance Claim Addresses, Pricing Matters. A Lot.

📍 💰

You've got a spreadsheet of 100,000 addresses. Storm damage claims. Hail reports. Customer service requests. You need latitude and longitude for every single one — for heatmaps, routing, clustering, geographic analysis. Sounds simple. It's not. Google Geocoding API costs $5 per 1,000 addresses. Do the math: 100K addresses = $500 just to geocode once. Nominatim is free but self-hosted and inconsistent. Mapbox is $0.50 per 1,000 batch. Aidxn geocoded 100K+ addresses for Rebuild Relief (insurance claims, hail and storm). This is what we learned about cost, quality, Australian coverage, and the hybrid pattern that works.

Skip the hype. Skip the all-in-one vendors. Here's the decision tree: if you're under 10K addresses, Google is fine. 10K–100K? Mapbox batch is the sweet spot — fast, reliable, cheap. Over 100K and doing this regularly? Hybrid approach: Mapbox for 95% of addresses (they'll match), Google Geocoding API for the low-confidence stragglers (5%), cache everything. Australia coverage matters too — we tested all three. Spoiler: Mapbox dominates Australian postcodes. Google is solid but slower. Nominatim is spicy if you host it yourself.

What Is Geocoding and Why It Matters At Scale

Geocoding is simple in theory: input an address string, output latitude and longitude. In practice, it's hard. Addresses have typos. Streets change names. Suburbs get renamed. A postcode maps to 10,000 different locations. "123 Smith St, Brisbane, QLD 4000" should return -27.4719, 153.0251. But the API might return -27.4705, 153.0249 (off by 150 metres), or fail entirely and return null. At scale, accuracy compounds. 100K addresses with 5% failure rate = 5,000 missing coordinates. You can't hand-fix 5,000 rows. You need high match rates and low price.

Geocoding feeds downstream: heatmaps (plot claims by density), routing (dispatch crews efficiently), geofencing (trigger alerts when crews enter damage zones), cohort analysis (do claims in postcode 4000 differ from postcode 6000?), and reporting (show executives a map of where revenue is concentrated). Get geocoding wrong and every downstream system breaks. Get it expensive and your analytics budget evaporates.

The Three Contenders: Price, Quality, Australian Coverage

Google Geocoding API: $5 per 1,000 requests. First 25,000 free per month (if you've got Web Maps Platform credits). Cached results (second lookup of the same address) cost $0.50/1,000. Ultra-reliable. Returns not just lat/lon but detailed components (street, suburb, postcode, state, country) and confidence scores. Australian coverage is solid — Google Maps data feeds this, and they've invested heavily in APAC. Latency: sub-100ms. Auth: API key or OAuth. Batch? No official batch endpoint (you submit requests one by one or use Cloud Tasks to parallelize). Rate limit: 50 QPS (requests per second) for standard accounts, 600 QPS for premium.

Nominatim (OpenStreetMap): Free, open-source, self-hosted. You download the latest OSM database dump (~80GB), spin up a PostgreSQL instance with PostGIS, and query the API. Results? Inconsistent. Some addresses return accurate coordinates. Others are off by kilometres. Confidence is binary (found or not found) — no soft scores. Australian coverage is patchy — depends on volunteer mapping contributions. If someone mapped a Sydney street in 2018 and it's been renamed, your query fails. Latency: depends on your hardware (could be 5ms or 500ms). Auth: none, it's your server. Batch? Easy — query the database directly. Cost: hardware and electricity, maybe $200/month for a decent instance. The appeal: once you run it, addresses are free forever. The catch: you're now running a geo database and maintaining 80GB of data.

Mapbox Geocoding API: $0.50 per 1,000 requests for batch (up to 100 addresses per batch). First 100,000 per month free (in some plans). Cached results cost $0.01 per 1,000. Reliable, with response times under 200ms. Returns lat/lon, components, and relevance scores. Batch endpoint lets you send 100 addresses in one request and get 100 results back — huge efficiency win. Australian coverage is strong (Mapbox uses OpenStreetMap + proprietary data). Auth: API token. Rate limit: depends on your plan, typically 600 QPS. The play: batch as many addresses as possible into one request, keep the relevance score, reject anything below 0.8 confidence, and re-query the rejects with Google as a fallback.

Hybrid Reality: Mapbox first (cheap, fast batch), accept anything with relevance > 0.85. Requery failures + low-relevance (< 0.85) with Google Geocoding. Google's stricter algorithm catches what Mapbox misses. For 100K addresses, expect: 85K match on Mapbox (batch cost = $4.25), 12K requery on Google (cost = $60). Total: ~$65 + caching = massive savings vs $500 all-in with Google.

Australian Postcodes: Gotchas and Coverage

Australia is weird. Four-digit postcodes don't map to a single point — they map to a suburb or region. "4000" is Brisbane CBD, but it spans 5+ kilometres. When you geocode "4000 Brisbane QLD", which lat/lon do you get? The centroid of the postcode? The town hall? Different APIs return different points. Google returns the centroid. Mapbox returns the centroid. Nominatim might return a random spot in the postcode or fail entirely.

Rebuild Relief's claims include suburb + postcode but often miss street-level detail. "Hail damage, postcode 4000" with no street address. Geocoding needs to handle this gracefully. Strategy: if the input has a street, geocode the full address. If it's postcode + suburb only, geocode the postcode and let the confidence score tell you the match quality (should be lower). If it's postcode only, geocode the postcode (will return centroid). Mapbox's relevance score is crucial here — a full-address match gets 0.99, a postcode-only match gets 0.55. You can threshold accordingly.

The Hybrid Pattern: Mapbox → Google Fallback

import requests import json from typing import Optional, Dict MAPBOX_TOKEN = "YOUR_MAPBOX_TOKEN" GOOGLE_KEY = "YOUR_GOOGLE_KEY" def geocode_hybrid(address: str) -> Optional[Dict]: """ Geocode via Mapbox first. If relevance < 0.85, fallback to Google. """ # Try Mapbox mapbox_url = f"https://api.mapbox.com/geocoding/v5/mapbox.places/{address}.json" params = {"access_token": MAPBOX_TOKEN} try: r = requests.get(mapbox_url, params=params, timeout=5) data = r.json() if data.get("features") and len(data["features"]) > 0: feature = data["features"][0] relevance = feature.get("relevance", 0) coords = feature.get("geometry", {}).get("coordinates", []) if relevance >= 0.85 and len(coords) == 2: return { "lat": coords[1], "lon": coords[0], "relevance": relevance, "source": "mapbox", "address": feature.get("place_name", "") } except Exception as e: print(f"Mapbox error: {e}") # Fallback to Google google_url = "https://maps.googleapis.com/maps/api/geocode/json" params = {"address": address, "key": GOOGLE_KEY} try: r = requests.get(google_url, params=params, timeout=5) data = r.json() if data.get("results") and len(data["results"]) > 0: result = data["results"][0] location = result.get("geometry", {}).get("location", {}) return { "lat": location.get("lat"), "lon": location.get("lng"), "relevance": 0.9, # Assume high confidence if Google matched "source": "google", "address": result.get("formatted_address", "") } except Exception as e: print(f"Google error: {e}") return None # Batch example: geocode 10K addresses addresses = [ "123 Smith St, Brisbane QLD 4000", "456 Hail Ln, Gold Coast QLD 4217", # ... 9998 more ] results = [] for addr in addresses: result = geocode_hybrid(addr) results.append({ "input": addr, "output": result }) # Cache results to Redis or SQLite so re-runs are instant import sqlite3 conn = sqlite3.connect("geocode_cache.db") cursor = conn.cursor() cursor.execute(""" CREATE TABLE IF NOT EXISTS cache ( address TEXT PRIMARY KEY, lat REAL, lon REAL, relevance REAL, source TEXT, cached_address TEXT ) """) for res in results: if res["output"]: cursor.execute(""" INSERT OR IGNORE INTO cache VALUES (?, ?, ?, ?, ?, ?) """, ( res["input"], res["output"]["lat"], res["output"]["lon"], res["output"]["relevance"], res["output"]["source"], res["output"]["address"] )) conn.commit()

This pattern saves money. Mapbox batch requests mean you send 100 addresses in one HTTP call and get 100 results back. Code above assumes serial lookups for clarity, but in production, you'd batch 100 at a time: geocode_batch([addr1, addr2, ...addr100]). For 100K addresses, that's 1,000 batch requests to Mapbox (cost: $0.50 per 1,000 = $0.50 total for all 100K if they all match). The 5% that don't match get re-queried on Google individually. Much cheaper than querying Google 100K times.

Caching Strategy: Never Geocode The Same Address Twice

If you're running this monthly (new claims come in daily), you'll see repeating addresses. "123 Smith St, Brisbane" might appear 50 times across different batches. Cache it. SQLite, Redis, or in-memory dict — whatever fits. Check cache before querying any API. For Rebuild Relief, caching cut API calls by 40% month-on-month as the address base overlapped more. Cost: minimal storage. Win: massive.

Cache keys matter. Normalize addresses before caching: strip extra whitespace, lowercase, standardize abbreviations (St → Street, Rd → Road). "123 Smith St, Brisbane" and "123 SMITH ST BRISBANE" should hash to the same key. Dirty cache keys mean duplicate API calls.

Six FAQs

What's the actual latency end-to-end?

Mapbox batch: 100 addresses in one request, response under 200ms. Serial Google queries: 100 addresses × 100ms = 10 seconds. If you're processing 100K addresses and need results in an hour, parallelize: send 10 concurrent requests to Mapbox (10 batches of 100), each one returns in 200ms, and you've geocoded 1,000 addresses in 200ms. Python asyncio or threaded requests make this easy. Expect 100K addresses to finish in 20–30 minutes with full parallelization.

Does postcode-only geocoding work well?

Yes, but with caveats. Mapbox returns the postcode centroid (lat/lon of the "middle" of the postcode) and marks it low-confidence (0.4–0.6 relevance). If you need street-level precision and only have postcodes, you're out of luck. But if you're just clustering claims by geographic region or visualizing density heatmaps, postcode centroids are fine. For Rebuild Relief, postcode-level heatmaps work; street-level routing requires full addresses.

What's the failure rate you should expect?

With Mapbox + Google hybrid, expect < 2% failures on well-formed Australian addresses (street + suburb + postcode). Incomplete addresses (postcode only) have much higher failure rates unless you're geocoding to postcode centroids. Typos (e.g., "Brisbne" instead of "Brisbane") fail the first time but might match after fuzzy correction. If you have > 5% failures, pre-clean your address data (fix obvious typos, fill in missing postcodes).

How do I handle multiple matches for the same address?

Mapbox and Google both return a ranked list (best match first). Use the first result (highest confidence). If relevance is < 0.8, you're in the ambiguous zone — could be a typo, could be a real address the API doesn't recognize. For Rebuild Relief, we flag these manually. "Address matched with low confidence — please verify." Usually the human says "yes, that's right" or corrects the postcode and we re-run. Low-confidence matches are better than false confidence.

Can I use the cached results for more than a year?

Addresses change slowly. A street renamed is rare. A postcode shifting boundaries is rarer. One-year cache reuse is safe. If you suspect an address has changed (e.g., a new development in an area), refresh it. For Rebuild Relief, we refresh the whole cache annually — costs ~$4 on Mapbox and gives us confidence. The time cost (30 minutes to re-geocode 100K) is negligible.

Should I use Google Places API instead?

No. Google Places is designed for autocomplete (as-you-type suggestions) and place details (hours, phone, reviews). Geocoding API is for batch address-to-coordinates. Places costs more and is slower for bulk geocoding. Use Geocoding API for this job.

The Bottom Line

Geocoding at scale is not about picking the best API — it's about blending APIs intelligently. Mapbox is your workhorse (fast, cheap, reliable). Google is your backup (slower, pricier, stricter matching). Nominatim is your option if you're already running a PostGIS database and want to control the algorithm. Cache everything. For 100K Australian insurance claims, expect to spend $60–100 on APIs and save 10+ hours of manual geocoding. The hybrid pattern turns a $500 problem into a $65 problem. Read more on BigQuery pipelines for how to layer geocoded data into cohort analysis, or explore Velocity pricing to find how geographic cohorts pair with your core analytics.

Let us make some quick suggestions?

Please provide your full name.
Please provide your phone number.
Please provide a valid phone number.
Please provide your email address.
Please provide a valid email address.
Please provide your brand name or website.
Please provide your brand name or website.