Monitor your key's usage before you hit a limit
Watching your quota headers as you go tells you when a limit is approaching, well before a request actually gets rejected.
An access log full of IP addresses has real geographic information buried in it, but only once each address has been resolved to coordinates that a mapping tool can actually plot.
Pull the unique IP addresses out of your log file first, rather than looking up every single log line individually, since the same visitor address commonly appears many times across a session and there is no reason to pay for the same lookup repeatedly.
sort logfile.txt | awk '{print $1}' | sort -u > unique_ips.txtSend the list of unique addresses as a bulk POST array to /v1/ip.
POST /v1/ip
Content-Type: application/json
["203.0.113.10", "198.51.100.25", "192.0.2.44"]{
"status": "ok",
"results": [
{"ip": "203.0.113.10", "version": 4, "found": true, "country": "Germany", "country_code": "DE", "region": "Berlin", "city": "Berlin", "postcode": "10115", "lat": 52.5300, "lon": 13.3800, "timezone": "Europe/Berlin", "asn": 1111, "org": "Example ISP"},
{"ip": "198.51.100.25", "version": 4, "found": true, "country": "Spain", "country_code": "ES", "region": "Madrid", "city": "Madrid", "postcode": "28001", "lat": 40.4168, "lon": -3.7038, "timezone": "Europe/Madrid", "asn": 2222, "org": "Example Networks"},
{"ip": "192.0.2.44", "version": 4, "found": false}
]
}Pair each returned lat and lon with the frequency count of that original address in your log, so a visitor who appears a hundred times in the log contributes proportionally more weight than one who appears once. Feed the resulting list of weighted coordinate points into whichever mapping or charting tool you use to render the heatmap.
If a precise point-by-point heatmap is more detail than you need, aggregating by country_code instead of raw coordinates produces a simpler choropleth-style view, one figure per country rather than a scattered cloud of points. This uses exactly the same bulk lookup, just grouped differently once the results come back, so it costs nothing extra to build both views from the same resolved data.
An entry with found: false, as in the third result above, should simply be excluded from the heatmap rather than plotted at a default location, since including it would misleadingly cluster unresolved traffic somewhere on the map that has nothing to do with where it actually came from.
Do not skip filtering out obvious non-visitor traffic, such as internal health checks or monitoring services that hit your server constantly from a fixed address, before building the unique address list. A monitoring service polling every minute can rack up a disproportionate log line count that has nothing to do with real visitor geography, and while deduplication already limits it to one resolved point, that one point can still visually dominate a heatmap out of proportion to any real traffic pattern it represents.
The org field on a resolved address often reveals when traffic is coming from a data center or cloud hosting range rather than a residential or mobile connection, which is a reasonable signal for filtering out bot or scraper traffic before it skews a heatmap meant to represent real visitor locations.
Resolving unique addresses rather than every log line is what keeps this affordable, since request cost is one per unique address, not one per log line. A log with a million lines but only a few thousand unique visitor addresses costs a few thousand requests, not a million, comfortably manageable against prepaid credit or an Unlimited package for a large site, and often within the free allowance for a smaller one. Watching the X-Quota-Used header during a large batch job is a simple way to confirm usage is tracking as expected before the job finishes.
Deduplicating before you resolve is the single biggest lever for keeping a log-based heatmap affordable. The IPv4 lookup docs cover the bulk request format in full.