Skip to content
Go back

I Stopped Paying Google Maps API

By SumGuy 11 min read
I Stopped Paying Google Maps API
Contents

The Email

It arrived on a Tuesday, which is somehow always when the bad news comes. Google Cloud Billing, subject line: “Your invoice is ready.” The project was a small delivery dashboard I’d built for a friend’s local food business, nothing fancy, just address lookup, a map view, and some routing. It had been live for about four months.

The geocoding line item was more than the server. Combined with Places API calls from the search autocomplete, the map tile usage, and a handful of Directions API hits, the monthly Google bill had quietly crept past the point where the project made financial sense. The business wasn’t huge. The margins on food delivery aren’t huge. And here was a third-party API eating a non-trivial chunk of each month’s revenue.

I knew, intellectually, that APIs have costs. What I hadn’t thought carefully about was the shape of those costs at modest-but-real usage. Basic geocoding is actually pretty reasonable. Everything else is where Google gets you.

The Pricing Structure You Don’t Think About Until It’s Too Late

I’ll use directional language here because Google’s exact per-request pricing changes and I don’t want this article to be wrong in six months. But the structure, the shape of it, is stable and worth understanding.

Basic geocoding (address to coordinates, coordinates to address) sits in a fairly modest tier. It’s the gateway drug. It’s cheap enough that you don’t think twice about it when you’re prototyping.

Autocomplete is a different story. Every keypress in a search box can be a billable autocomplete request. Type a seven-character address and you’ve potentially made six or seven API calls, depending on your debounce settings. If you didn’t implement debounce at all, and if you’re not careful, you didn’t, every typo your users make is billing you. At modest usage, this is annoying. At real usage, it’s a line item that makes you sit down.

Place Details is the one that really bites. When a user selects a result from autocomplete and you need the phone number, opening hours, reviews, or verified business name, that’s a separate call, and it sits in a substantially higher pricing tier. The pattern of “autocomplete → place details” for a single business lookup is two separate billable events, each in different cost brackets.

A rough mental model for anyone building something from scratch:

API call typeRelative cost
Geocoding (forward/reverse)Low
Static maps tilesLow
Directions / routingMedium
Places AutocompleteMedium-high
Place DetailsHigh
Nearby SearchHigh

The projects that get into trouble are the ones that naively treat “Google Maps API” as a single thing with a single price, then discover on invoice day that half their usage was in the expensive tiers.

The Specific Pain: Autocomplete Billing

The autocomplete problem is worth dwelling on because it’s so easy to miss.

User types 123 Main S. That’s seven characters. If you’re not debouncing aggressively, you’ve just made seven autocomplete requests. Some of them returned results the user immediately discarded. Some of them were typos. All of them were billable.

The fix, debounce your input, wait until the user pauses, then fire, is obvious in retrospect. But you’re prototyping, you’re trying to get the thing working, and debouncing is a detail you add later. Then “later” is when you’re reading the invoice.

This is not a Google-specific problem. Any pay-per-request autocomplete API will penalize you for this. The difference is that with self-hosted search, Photon, Pelias, or a similar setup, the “cost” of a wayward autocomplete call is some additional load on your server. Not a line item.

What I Migrated To (And Why It’s Actually a Stack)

The thing nobody tells you about “replacing Google Maps” is that you’re not replacing one thing. Google Maps API is several products bundled under a comfortable umbrella. When you leave, you need to find pieces that cover each use case:

None of these are particularly hard to operate. All of them have a learning curve that Google Maps does not have, because Google Maps requires zero operational knowledge. You paste in a key and it works. This is not a trivial advantage.

What the Migration Actually Cost

Not in dollars. In time. This is the calculation that gets hand-waved in “why you should self-host” articles, and I think it’s worth being honest about.

For the delivery dashboard project, here’s roughly how the migration time broke down:

Server setup and Nominatim import: ~4 hours Spun up a VPS, configured Docker, ran the Nominatim import for the North America extract. Most of that four hours was the import itself running in the background while I did other things. Active keyboard time was maybe 45 minutes.

Frontend geocoding swap: ~2 hours Replacing Google geocoding calls with Nominatim calls. The response shapes are different, so you’re doing a find-and-replace on field names plus a light refactor of the code that parses results. Not hard. Just not instant.

Autocomplete migration to Photon: ~3 hours Photon’s API is simple but the behavior is slightly different from Places Autocomplete. Some tuning on result ranking. Some edge cases where Photon returned results in a different order than expected. This is the part where “mostly works” turns into “actually works for the real addresses your users type.”

Tile setup and MapLibre integration: ~3 hours Swapping Google Maps JS SDK for MapLibre is not a lift-and-replace. They have different APIs. Map style configuration is different. If you have custom overlays or controls, those need reworking. Three hours is optimistic if you have a complex map setup.

OSRM for routing: ~2 hours Straightforward Docker setup, but routing data also needs to be imported from OSM. Same pattern as Nominatim, run the import, it takes a while, results look correct.

Edge cases and QA: ~4 hours The addresses that don’t geocode right. The business name that OSM doesn’t have. The routing result that takes a weird path. This is the hidden cost that’s easy to forget.

Total: roughly two solid workdays of actual engineering time, spread across a week. For a hobby project or a project you’re going to run for years, that’s a reasonable investment. For a client project with a tight deadline, that’s a conversation you need to have before committing.

The Honest Trade-off: What You’re Actually Giving Up

This is the part of the self-hosting pitch people usually skip.

Google’s Places API isn’t just geocoding with a price premium. It’s access to a curated, continuously updated database of business information: verified phone numbers, hours of operation, user reviews, high-resolution photos, real-time “busy” data. Google employs people and runs automated systems specifically to keep that data accurate. This is not something you can replicate with OSM.

OpenStreetMap is community-maintained. It’s excellent for addresses, roads, parks, buildings, and geography. It is inconsistent for business POI data. Whether a restaurant’s phone number is in OSM depends on whether a mapper in that city bothered to add it. In dense urban areas with active OSM communities, POI coverage is solid. In rural areas or countries with less OSM activity, there are gaps.

If your application’s value prop depends on showing users accurate phone numbers, hours, reviews, and photos for arbitrary businesses, the kind of thing Google Maps does well, self-hosting won’t replace that. No honest self-hosting advocate should tell you otherwise.

Where OSM shines: addresses, roads, geography, and anything that can be described spatially. If you’re geocoding customer delivery addresses, routing trucks between warehouses, displaying a map of hiking trails, or doing any kind of high-volume address normalization, OSM data is more than sufficient. The businesses with actual problems at that layer aren’t asking for reviews; they’re asking for coordinates.

When You Should Still Pay Google

There are cases where paying Google is the right call, and pretending otherwise would be intellectually dishonest.

If your workload is small, under 10,000 requests a month, and you’re using only basic geocoding, Google’s free monthly allowance for the Geocoding API (the Essentials SKU cap, in place since Google restructured Maps Platform pricing in March 2025) covers you, and the operational overhead of running your own stack might not be worth it. A self-hosted Nominatim instance needs monitoring, disk maintenance, and occasional debugging. If you’re doing a few thousand lookups a month for a personal project, that’s real ops time for a real-money savings of maybe $5.

If you need verified business data, the Places API use case, you’re either paying Google, or you’re paying a competitor with a similar dataset (HERE, TomTom, Foursquare). There’s no free lunch there. The data is expensive to collect and maintain.

If you don’t want to operate anything, pay the money and move on. There is honor in choosing the managed solution. Not every project deserves an ops burden.

Where Self-Hosting Wins Decisively

High-volume geocoding is the clear case. Once you’re past a few hundred thousand requests a month, the economics aren’t even close. Your self-hosted stack has a fixed cost regardless of how many requests you make. Commercial APIs charge you for every one. At scale, this is the difference between a predictable server line item and a usage bill that can surprise you.

Privacy is the other one. Every geocoding call you make to Google’s API tells Google something: what addresses your users are searching, what coordinates your users are at. If your application has any privacy angle at all, health data, location history, user addresses, routing that through Google is a design choice worth questioning. Self-hosted geocoding stays on your infrastructure.

Batch processing is a related case. If you have a million old records to normalize, or you’re running a regular import job that hits geocoding for every new entry, commercial API limits and costs will constrain you. A local Nominatim instance doesn’t care if you throw 10,000 requests at it overnight.

Latency at scale is real too. A self-hosted geocoder on the same LAN or in the same data center region as your application returns results in single-digit milliseconds. Commercial APIs route through the internet and land somewhere in the 100 to 300ms range, depending on where you are. For real-time user-facing search, that’s perceptible.

The Unsexy Conclusion

Most projects land in one of two bins.

Bin one: You’re doing high-volume geocoding, routing, or mapping where the data requirement is addresses, roads, and geography. Self-host everything. The ops cost is low, the savings are real, and you get privacy and predictable costs as a bonus. Nominatim for geocoding, Photon for autocomplete, MapLibre for the frontend, OSRM if you need routing. None of it is hard to operate once it’s running.

Bin two: You need the full Places API, business data with verified hours, phones, and reviews, and you’re not doing it at scale. Pay for what you need and don’t feel bad about it. Maybe you self-host the geocoding layer and pay for Places only when the user is looking at a specific business. A hybrid approach works. Paying Google $30/month for a targeted use case is not a moral failure.

The mistake is assuming Google Maps API is a single atomic thing that you either pay for or replace entirely. It’s several products. Most of them are replaceable. One of them, the verified business metadata layer, is hard to replicate and worth paying for if you need it.

The delivery dashboard? It’s self-hosted now. The geocoding is faster, the autocomplete works better than it did (turns out building it properly the first time is useful), and the line item is gone. The routing is handled by OSRM. The map tiles are cheap from a CDN that isn’t Google.

The Google invoice is a distant memory. The 2 AM billing alert is no longer a risk.


Share this post on:

Send a Webmention

Written about this post on your own site? Send a webmention and it'll show up above once verified.


Next Post
Overpass API: SQL for OpenStreetMap

Discussion

Powered by Garrul . Sign in with GitHub or Google, or post anonymously.

Related Posts