Blog

Geographic IP enrichment without a third-party lookup

sGTM has built-in geo headers from the request infrastructure. They are good enough for most use cases.

When you need country or region for an incoming request, the obvious instinct is to call MaxMind, IPStack, or another commercial geo-IP service. Before doing that, check the request headers; sGTM inherits geo data from its hosting infrastructure, and for most use cases it is accurate enough.

Headers exposed by App Engine

When sGTM runs on Google App Engine (the SprTags-managed setup), incoming requests include these headers automatically:

HeaderExample value
X-AppEngine-CountryFR, US, JP
X-AppEngine-Region75 (Paris department code)
X-AppEngine-Cityparis (lowercase, ASCII)
X-AppEngine-CityLatLong48.864716,2.349014

Reading them in a Custom Variable

return {
  country: getRequestHeader('x-appengine-country'),
  region: getRequestHeader('x-appengine-region'),
  city: getRequestHeader('x-appengine-city'),
  latlong: getRequestHeader('x-appengine-citylatlong')
};

Accuracy

Country: nearly always correct. Google's geo-IP database is well-maintained and updated regularly. Region: usually correct but with occasional misclassifications for VPN traffic. City: correct for major cities, less reliable for smaller ones. Lat/long: precision is approximate (city-level), not GPS-level.

For analytics use cases, country and region are accurate enough. For ad targeting that needs zip-code precision, you need a commercial service.

Privacy considerations

Geo data derived from IP is generally not considered PII at the country level, becomes more sensitive at city level, and is fully PII at the lat/long level. If you anonymise IP early, the geo data should be derived before anonymisation and stored separately.

When to upgrade to MaxMind

Three triggers: you need ASN/ISP data (App Engine does not provide this), you need IPv6 accuracy that App Engine misses, or you have compliance requirements that demand a documented commercial geo provider with audit trails.

For everyone else, the built-in headers are free and sufficient. Save the integration cost for somewhere it actually matters.