Skip to main content

Cost Optimization Patterns

Location API bills do not grow linearly with a business. They grow with whatever the system calls per unit of something. Get that unit wrong and no rate negotiation saves you. Get it right and the bill becomes a small, flat, forecastable line that grows with locations rather than with traffic. This page is a taxonomy of the patterns that move it, ordered by the magnitude they move.
Read this alongside Reducing Google Maps Costs, which is a process — instrument, fix, model. This page is the catalogue the process draws from.

The problem statement

Almost every expensive location system has the same shape:
  • An API call attached to a unit of work that is too small
  • A computation performed at request time that should have been performed once
  • A latency guarantee purchased for a workload where nobody is waiting
Everything below is a variation on those three.

The ordering principle

Patterns are ranked by the exponent they change, not the constant.
RankPatternMoves the bill byEffort
1Change the unit of workOrders of magnitudeLow
2Choose the right primitiveOrders of magnitudeLow
3Precompute and materializeUnbounded → boundedMedium
4CacheLarge constantLow
5DeduplicateLarge constantTrivial
6BatchRate differenceMedium
7Trim the requestSmall constantTrivial
Teams routinely start at rank 7. Setting return fields correctly is worth doing and will never save a platform whose architecture calls a geocoder per GPS packet.

Pattern 1: Change the unit of work

The single highest-leverage change available, and it is not a caching problem. Systems call APIs per unit. The default unit is almost always too small.
Wrong unitRight unitRatio
GPS packetDetected stop~250:1 for a 12-stop shift
KeystrokeDebounced query~8:1 on a typical address
Page loadUser actionUnbounded
OrderDistinct address~5:1 in mature customer bases
Trip pointTrip~2,000:1
A vehicle emitting a position every ten seconds over a nine-hour shift produces 3,240 packets. Across 200 vehicles: 648,000 per day. Any API call attached to a packet multiplies by that number.The address is needed when a human looks at a screen, or when a stop is detected. Not at 3:47:20am on I-80.
Diagnostic: instrument the ratio of API calls to business events. Reverse-geocode calls per detected stop should be near 1. Routing calls per dispatch should be near the number of legs. Geocodes per order should approach zero.
This ratio is the most valuable number in your observability stack. Total call volume grows with the business; the ratio should not. A ratio that doubles overnight is a regression a deploy introduced, and it is visible hours before it appears on an invoice.

Pattern 2: Choose the right primitive

Three primitives look interchangeable and are not. An n×m cost table costs n·m routing calls or 1 matrix call. A 20-depot, 500-stop assignment problem is 10,000 calls or 1. That is a complexity difference, not a rate difference. No pricing tier closes it. Where the loop hides: nearest-driver assignment, store locator ranking, territory design, freight rating, site selection, delivery zone economics. All matrices. The inverse error: using a solver where a lookup would do, or matrix where you needed geometry. Matrix returns no path. If a driver follows it, you needed routing. See Routing vs Matrix.

Pattern 3: Precompute and materialize

This pattern converts an unbounded cost into a bounded one. That is a stronger statement than “it saves money.” Certain artifacts are expensive to compute, cheap to store, and stable for months:
  • Drive-time isolines around fixed locations
  • Distance matrices between fixed depots and stable destination sets
  • Lane distances for freight rating
  • Territory polygons
  • Trade areas
Compute them on a schedule. Store them in PostGIS. Query them locally, forever. The arithmetic: A 400-store network with three delivery bands is 1,200 isoline calls per quarter. Not per day. Not per customer. The cost is bounded by store count — a business number you already know. The same system, computing isolines at checkout, costs one call per session. That is unbounded, it scales with your success, and it is why the pattern matters.
If your architecture calls an external service to answer “is this point inside this polygon,” you have built a spatial database with an API bill attached. Containment is a geometry test. It requires no road network, no traffic data, no routing engine.
Diagnostic: for each API call in a request path, ask what would have to change for this answer to differ? If the answer is “a new store opens,” it is a materialization candidate. Failure modes: stale artifacts. Version them. Recompute on events — a location opens, a service promise changes, a significant map release ships — not on a timer. See Delivery Zones and Catchment Area.

Pattern 4: Cache

Buildings are stationary. This is the whole insight. Normalization is the multiplier. 123 Main St and 123 Main Street are the same address, and a cache keyed on the raw string treats them as different. Trim, case-fold, expand abbreviations, canonicalize postal codes — before hashing. Instrument the hit rate before and after. Teams routinely find that half their misses were the same address written three ways. Free doubling. Invalidate on events, not clocks.
A thirty-day TTL on a geocode invalidates a stable rooftop match for a building that has stood since 1904, and does nothing about the subdivision that opened yesterday. Time-based TTL on geocoding is cargo-cult caching: it costs money and improves nothing.
Invalidate on map release, low confidence, explicit correction, or an operational failure signal such as a failed delivery. Round before caching coordinates. GPS jitter defeats exact-match lookup. Round to roughly five decimal places — about one metre. A vehicle idling at a depot generates hundreds of pings within a metre of each other. One entry. Different TTLs for different things. Route geometry between fixed points is stable for hours. The ETA is not. Cache them separately or you serve stale arrival times to drivers. The multi-tenant caveat: a shared geocode cache is a side channel. Key by tenant. See Multi-Tenant Location Platform. See Caching Geocoding Results.

Pattern 5: Deduplicate

The cheapest pattern on this page, skipped constantly. A raw order export containing four million rows may contain nine hundred thousand distinct addresses. Geocoding it bills four million. Normalize first, then deduplicate. Deduplicating before normalization catches only exact string matches, which is most of the repetition and not all of it. Filter what you already have. WHERE geocoded_at IS NULL. On the second run this is most of the table.
Run the deduplication stage and count the rows that remain. Present that number before you present a timeline. It is usually a fifth of what you were handed, and it changes the conversation with whoever asked.
Request deduplication at the edge is the real-time analogue. Two concurrent requests for the same geocode should produce one upstream call. A simple in-flight request map handles it.

Pattern 6: Batch

Same data, different product, different rate. The tell: if the result is written to a database rather than rendered to a screen, it should have been batched. Nothing waits for nightly address normalization. Nothing waits for historical trip matching. Nothing waits for a partner data import. Paying real-time rates for latency nobody consumes is the second-largest avoidable cost in this domain. Batch is a job lifecycle, not a bulk endpoint. Create, start, poll, retrieve errors, retrieve results, delete. Persist the job ID before polling, or a worker restart resubmits a job you already paid for. Three status codes that are not errors:
ResponseMeaning
429 on job startConcurrency limit. Queue.
204 on errors endpointZero errors. Celebrate.
404 on resultsJob has not succeeded yet.
Code that treats any non-200 as failure will log a successful job as broken every night until someone reads the specification. See High-Volume Geocoding and Batch Geocoding.

Pattern 7: Trim the request

Small, free, and worth doing after the six patterns above. Set return explicitly. Requesting polyline,actions,instructions,turnByTurnActions when you needed summary inflates payloads by orders of magnitude. Nothing consumes turn-by-turn instructions server-side. Request only matrix fields you read. consumptions is not free. Debounce autocomplete at 200–300ms. Undebounced, you bill once per character typed, per session, forever. Coarsen resolution where nobody sees the difference. A high-resolution isoline renders identically to a simplified one at the zoom level shown, and tests containment faster. CDN in front of tiles. Static per zoom, x, y, and style. Available on any platform.

Monitoring and forecasting

Alert on calls per business event, not on total volume. Total volume is supposed to grow. The ratio is not. Instrument:
  • Cache hit rate, per cache
  • Reverse-geocode calls per detected stop
  • Routing calls per dispatch
  • Geocodes per order
  • Matrix calls per assignment cycle
  • 429 rate, by endpoint
  • 403 count — should be zero; any occurrence is a licensing issue, not a bug
Forecasting is a function of business inputs, not of last month’s bill. Once the architecture is right, the cost of most surfaces is bounded by a number you already know:
SurfaceBounded by
Materialized zonesLocation count × bands × refresh frequency
Store locatorSessions × cache miss rate × k
GeocodingDistinct new addresses per month
Trip matchingTrips per day, not points
Territory designRep count × unit count, quarterly
If a line item is large and variable, something in the pipeline is calling an API where it should be reading a table.
Present the forecast as a function of business inputs. “Our location cost is XperthousandnewdistinctaddressesplusX per thousand new distinct addresses plus Y per store per quarter” is a sentence a CFO can plan against. “It was $40k last month” is not.
Model both commercial models. Call-volume pricing suits SaaS platforms whose calls track customer count. Asset-based pricing suits fleet operators with a countable vehicle population and unpredictable call volume — a dispatcher rerouting 200 trucks forty times a day is punished by call-volume pricing for operational diligence. Asset-based availability depends on contract tier. Confirm before it becomes load-bearing in a business case. See HERE Pricing Explained.

Where these patterns compose

Patterns 1 and 2 move you from unbounded to bounded. Patterns 3 and 4 move you from bounded to free. Patterns 5, 6, and 7 reduce the constant on whatever remains. That ordering is why a team that starts by negotiating rates and setting return fields ends up with a cheaper unbounded system.

Common mistakes

Optimizing constants before exponents. Trimming responses on a system that geocodes every packet. Negotiating a rate to fix a complexity problem. A routing loop is not expensive per call. Time-based TTLs on geocoding. Caching the raw address string. Calling an API to answer a containment question. Materializing nothing, then blaming the vendor. Not deduplicating before batch. Real-time rates for overnight work. Alerting on total volume rather than on calls per business event. Shared caches in multi-tenant systems. A leak dressed as an optimization. Migrating before optimizing. Moving the waste to a cheaper meter. You will save money and still be wrong. Forecasting from last month’s invoice rather than from business inputs. Treating 403 as retryable. It never succeeds, and it is a commercial conversation.

Production checklist

  • Calls-per-business-event instrumented for every high-volume surface
  • Reverse-geocode-to-detected-stop ratio measured and near 1
  • Every routing call audited: is it inside a loop?
  • Cost-table workloads moved to Matrix Routing
  • Serviceability, catchments, and lane distances materialized in PostGIS
  • Nothing in a request path calls an API to answer a containment question
  • Geocode cache keyed on normalized address; hit rate instrumented
  • Normalization applied at write time, before hashing
  • Invalidation triggered by events, not by TTL
  • Coordinates rounded before reverse-geocode cache lookup
  • Input deduplicated before every batch job
  • Latency-tolerant workloads moved to batch
  • Batch job IDs persisted before polling; 429/204/404 handled correctly
  • return fields explicit on every routing call
  • Autocomplete debounced at 200–300ms
  • CDN in front of tiles
  • Caches keyed by tenant in multi-tenant systems
  • Alerts on calls-per-event ratios, 429 rate, and any 403
  • Forecast expressed as a function of business inputs, not last month’s bill
  • Both commercial pricing models modelled against your worst month

Routing vs Matrix

Pattern 2, in full.

Caching Geocoding Results

Pattern 4, including normalization and privacy.

High-Volume Geocoding

Patterns 5 and 6, as a pipeline.

HERE Pricing Explained

Call volume versus asset-based, and free bundles that do not pool.
Reducing Google Maps Costs · Vehicle Tracking · Delivery Zones · Geofencing · Multi-Tenant Location Platform

HERE documentation


Need help designing or implementing a production HERE solution? Placematic helps engineering teams select the right HERE APIs, estimate usage, migrate from Google Maps and build production-ready geospatial systems. Talk to us.