.NET

Resolve the caller automatically

Middleware determines the caller's IP, resolves it once per request, and hands you the result anywhere via HttpContext.GetGeo() - with no IP handling in your code.

Register the middleware#

Add it after UseRouting. Endpoint metadata such as [SkipGeo] is only visible to the middleware once routing has run.

csharp
var app = builder.Build();

app.UseRouting();
app.UseIpGeoTrace();
app.MapControllers();

Read the result#

Call HttpContext.GetGeo()from any endpoint. It is always safe to call - it never throws for a missing lookup. Show prices in the visitor's currency:

csharp
[HttpGet("checkout")]
public IActionResult Checkout()
{
    var geo = HttpContext.GetGeo();
    var currency = geo.IsResolved ? geo.Value.Country?.Currency ?? "USD" : "USD";
    return Ok(_catalog.PricedIn(currency));
}

The same works from a minimal API - route the visitor to the nearest warehouse:

csharp
app.MapGet("/shipping-estimate", (HttpContext http) =>
{
    return http.GetGeo().TryGetValue(out var visitor)
        ? Results.Ok(Warehouses.NearestTo(visitor.Location?.Latitude, visitor.Location?.Longitude))
        : Results.Ok(Warehouses.Default);
});

Lookup status#

GeoLookup.Status tells you exactly what happened:

  • Resolved - Value carries the data; FromCache says whether an API call was made.
  • Skipped - the address was missing, invalid, private, link-local or loopback, so no API call was made.
  • Failed - Error carries the reason, such as rate_limited or quota_exceeded.
  • NotAttempted - the middleware did not run, the endpoint opted out, or ShouldResolve returned false.

Skipping endpoints#

Health checks, metrics and internal endpoints should not spend lookups. Opt them out with the attribute:

csharp
[HttpGet("health")]
[SkipGeo]
public IActionResult Health() => Ok();

Or centrally with a predicate:

csharp
app.UseIpGeoTrace(options =>
{
    options.ShouldResolve = context => !context.Request.Path.StartsWithSegments("/internal");
});

Choosing the caller's IP#

By default the middleware uses Connection.RemoteIpAddress. Behind a proxy or load balancer, configure the standard forwarded-headers middleware before UseIpGeoTrace rather than parsing headers yourself.

csharp
app.UseForwardedHeaders(new ForwardedHeadersOptions
{
    ForwardedHeaders = ForwardedHeaders.XForwardedFor
});

Private, link-local and loopback addresses (including IPv4-mapped IPv6 forms) are detected locally and skipped without an API call or quota usage.

Configuration#

All settings live on the AddIpGeoTrace options callback, and each one is optional.

csharp
builder.Services.AddIpGeoTrace(apiKey, options =>
{
    options.CacheEnabled = true;
    options.CacheTtl = TimeSpan.FromHours(6);
    options.Timeout = TimeSpan.FromSeconds(3);
    options.MaxRetries = 0;
});

Caching

Off by default. Turn it on and repeat lookups of the same IP are served from memory - no API call, no latency, and nothing counted against your quota. Only successful lookups are cached, never errors. Supply your own IGeoCache (Redis, distributed cache, anything) and caching turns on automatically.

Timeout & retries

The per-request timeout defaults to 10 seconds. Rate limits (429) and outages (503) are retried automatically, honoring the API's Retry-After header - the default is 2 retries. Set MaxRetries to 0 if you chain your own resilience handler (for example Polly) so requests are not retried twice.

You're set

That is the whole surface. Start with the defaults, then turn on caching once you see repeat traffic. Questions? support@ipgeotrace.com.