Singulink.Net.IPLocation.Client 1.0.1

Prefix Reserved
dotnet add package Singulink.Net.IPLocation.Client --version 1.0.1
                    
NuGet\Install-Package Singulink.Net.IPLocation.Client -Version 1.0.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Singulink.Net.IPLocation.Client" Version="1.0.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Singulink.Net.IPLocation.Client" Version="1.0.1" />
                    
Directory.Packages.props
<PackageReference Include="Singulink.Net.IPLocation.Client" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Singulink.Net.IPLocation.Client --version 1.0.1
                    
#r "nuget: Singulink.Net.IPLocation.Client, 1.0.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Singulink.Net.IPLocation.Client@1.0.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Singulink.Net.IPLocation.Client&version=1.0.1
                    
Install as a Cake Addin
#tool nuget:?package=Singulink.Net.IPLocation.Client&version=1.0.1
                    
Install as a Cake Tool

Singulink.Net.IPLocation

Chat on Discord View nuget packages Build and Test

Singulink.Net.IPLocation is a .NET project that contains a simple client and a self-hostable service for looking up the approximate location (continent, country, region, city and coordinates) and the network operator (autonomous system number and organization) of IP addresses. The service uses the free DB-IP IP to City Lite and IP to ASN Lite databases, keeps them up to date by itself, and answers lookups from memory without calling out to any other service.

We are a small team of engineers and designers dedicated to building beautiful, functional, and well-engineered software solutions. We offer very competitive rates as well as fixed-price contracts and welcome inquiries to discuss any custom development / project support needs you may have.

This package is part of our Singulink Libraries collection. Visit https://github.com/Singulink to see our full list of publicly available libraries and other open-source projects.

Usage

This repository is used for our own infrastructure needs and is open-sourced so that others can run the same service, instead of sending their users' IP addresses to an external service. The service runs on your own network, and your apps look up addresses with the client, which is given the service's address. The instance at https://iplocation.singulink.com is there to try the service out, for example in its Swagger UI at https://iplocation.singulink.com/swagger, and isn't meant for real use.

var client = new IPLocationClient(new Uri("https://iplocation.example.internal/"));
var result = await client.LookupAsync(IPAddress.Parse("8.8.8.8"));

Console.WriteLine(result.GetDisplayLocation()); // Mountain View, California, United States
Console.WriteLine(result.Organization);         // Google LLC

Lookups of private addresses (private networks, loopback, link-local and other addresses that are not routable on the internet) return a result with IsPrivate set and no location. Pass includeHostName: true to also look up an address's host name with reverse DNS.

Attribution

The data is licensed under the Creative Commons Attribution 4.0 International License, so apps that show results must credit DB-IP with a link: the text and address are available in DataAttribution.Text and DataAttribution.Url (<a href="https://db-ip.com">IP Geolocation by DB-IP</a>). CC BY 4.0 also asks for the license to be named, with a link to it where that is practical: its short name and address are in DataAttribution.LicenseName and DataAttribution.LicenseUrl (<a href="https://creativecommons.org/licenses/by/4.0/">CC BY 4.0</a>). The service's /Info endpoint and the client's GetInfoAsync return the same values as attribution, attributionUrl, licenseName and licenseUrl, for apps that show the credit from them.

The free databases are less accurate than DB-IP's commercial ones: countries are reliable, but cities can be off, especially for mobile networks. They have no postal codes, time zones or connection types.

Installation

A client package is available on NuGet - simply install the Singulink.Net.IPLocation.Client package. It brings in Singulink.Net.IPLocation, the package with the types that the client and the service share: IPLocationResult, DataInfo, DataAttribution, the error codes in IPLocationErrorCodes, the batch size in IPLocationLimits and the JSON serializer context. The client is built on the client library of the Singulink HTTP API Toolkit.

Supported Runtimes: .NET 10+ and .NET Framework 4.6.2+. Other runtimes that can use .NET Standard 2.0 libraries, such as .NET 8 and 9, get a build warning: they are not supported or tested.

IPLocationClient sends its requests to the service at the address given to its constructor. All clients share one HttpClient, so an app usually creates one client and keeps it, for example as a singleton:

services.AddSingleton<IIPLocationClient>(new IPLocationClient(new Uri("https://iplocation.example.internal/")));

A lookup of more than IPLocationLimits.MaxBatchSize (100) addresses is sent in several requests. GetInfoAsync returns the loaded databases and the credit their license requires. Errors that the service returns are thrown as the toolkit's ApiException types, with the service's message, error code and Retry-After time (see Errors), and ApiFailures.IsTransient tells whether a failure is worth trying again. The constructor that takes an HttpMessageHandler sends the client's requests through it, for example to answer them in tests with ApiErrorResponse.Create.

For running the service on your own infrastructure, deploy Singulink.Net.IPLocation.Service to your desired hosting destination. The project is a .NET 10 web service that can be hosted in IIS, Azure, or any other suitable .NET hosting environment. It needs no database: on first start it downloads the current month's databases (about 65 MB compressed, 135 MB on disk) into its data directory, and it downloads each new monthly release as it comes out. The settings are in the IPLocation section of appsettings.json, or in environment variables like IPLocation__DataDirectory:

Setting Default Description
DataDirectory ../data Where the databases and the saved copy of Cloudflare's IP ranges are kept, absolute or relative to the application directory. The default is next to the application directory so that deployments do not remove it.
AutoUpdate true Whether to download missing databases and each monthly release. Turn it off to manage the files yourself (named like dbip-city-lite-2026-10.mmdb and dbip-asn-lite-2026-10.mmdb).

The rate limit and Cloudflare settings are in their own sections (see Running the Service). The service has no secrets to configure.

Running the Service

The service needs outbound HTTPS access to download.db-ip.com for the databases and to www.cloudflare.com for Cloudflare's IP ranges (see Cloudflare), and permission to write to its data directory.

Errors

Errors use the error format of the Singulink HTTP API Toolkit: the status code and a text/plain message. Some errors have an error code in square brackets in front of the message, and their content type has a format=error-code parameter. The client throws the toolkit's exception for each status: BadRequestApiException, ValidationApiException, TooManyRequestsApiException, ServerErrorApiException or ServiceUnavailableApiException, with the message, the error code in ErrorCode and the Retry-After time in RetryAfter:

Status Message Returned when
400 '{ip}' is not a valid IP address. An address isn't a valid IP address.
400 The request is not valid. A required parameter is missing, or the body of a batch lookup isn't a JSON array of strings. For the body, the message is followed by the location of the error in it.
422 At most 100 addresses can be looked up at once. A batch lookup has more than IPLocationLimits.MaxBatchSize (100) addresses.
429 [rate-limited] Too many requests. Please wait a moment and try again. The client made too many requests (see Rate Limit).
500 An unexpected error has occurred and has been logged. Reference ID: {id}. Something unexpected failed. The error is logged with the same reference ID.
503 [databases-not-loaded] The IP location data is still being downloaded. Please try again in a few minutes. The databases aren't loaded yet.
503 [cloudflare-ranges-unavailable] The service is starting. Please try again shortly. The service has no Cloudflare IP ranges yet (see Cloudflare).

The 429 and 503 responses have a Retry-After header with the number of seconds to wait: until the next rate limit window, 60 while the databases are being downloaded and 10 for Cloudflare's ranges.

Rate Limit

Each client with a public address can make 50 requests a minute, counted in windows that start at every full minute, and IPv6 clients are counted per /64 network. The limit is for the public instance, which is there to try the service out. Clients with private addresses aren't limited: private IPv4 networks, loopback, link-local, the shared address space (100.64.0.0/10), unique local IPv6 addresses and connections without an address. So a deployment inside a private network, whose apps reach it from such addresses, isn't limited. A request over the limit gets a 429 error (see Errors). The RateLimits settings below change the limit.

This is the per-client rate limit of the Singulink HTTP API Toolkit, with its settings in the RateLimits configuration section, for example RateLimits__PermitLimit as an environment variable:

Setting Default Description
PermitLimit 50 The number of requests each client address can make per window.
Window 00:01:00 The length of a window.
ExemptPrivateNetworks true Whether clients with private addresses are exempt.
ExemptNetworks (none) Other addresses, or networks in CIDR notation, whose clients are exempt, such as RateLimits__ExemptNetworks__0=203.0.113.7.
Enabled true Whether requests are limited at all.

Each server counts the requests it gets, so in a web farm a client can make up to the limit on each server.

Cloudflare

The service can run behind Cloudflare's proxy. For a request whose connection comes from one of Cloudflare's IP ranges, the service takes the client's address from Cloudflare's CF-Connecting-IP header and the scheme from X-Forwarded-Proto, so the rate limit counts each client rather than Cloudflare's servers. All other requests keep their connection's address and scheme, and any headers starting with CF- are removed from them, so the service can be reached through Cloudflare and directly at the same time. This is the Cloudflare forwarding of the Singulink HTTP API Toolkit.

The service downloads Cloudflare's IP ranges at startup and once a day, and saves them to cloudflare-ip-ranges.txt in the data directory. When the download at startup fails, it uses that saved copy. Without any ranges, as on a first start that can't download them, the service refuses every request with a 503 error (cloudflare-ranges-unavailable) and tries the download again every 10 seconds, until it succeeds.

The settings are in the Cloudflare configuration section, for example Cloudflare__SavedRangesPath as an environment variable (see the toolkit's documentation for all of them):

Setting Default Description
SavedRangesPath cloudflare-ip-ranges.txt in the data directory Where the downloaded ranges are saved. Relative paths are resolved against the content root.
TrustedProxyNetworks (none) Addresses, or networks in CIDR notation, of other proxies whose connections are trusted like Cloudflare's, such as Cloudflare__TrustedProxyNetworks__0=127.0.0.1 for Cloudflare Tunnel's cloudflared on the same machine.
DownloadRanges true Whether the ranges are downloaded. When false, they are only read from SavedRangesPath, at startup and every RefreshInterval.
RefreshInterval 1.00:00:00 How often the ranges are downloaded again.

Hosting in IIS

The service downloads the databases and each monthly release while it runs. With IIS's defaults, it only starts with the first request and stops after 20 minutes without requests, so the first download only starts with the first request, and a new release is only downloaded while requests keep the service running. These settings keep it running:

  • Set the app pool's Start Mode to AlwaysRunning and its Idle Time-out to 0.
  • Set the site's Preload Enabled to True. This needs IIS's Application Initialization feature (Web-AppInit), which isn't installed by default.

Behind Cloudflare, IIS needs no changes for the client addresses, since the service reads Cloudflare's headers itself. IIS's own logs still show the addresses of Cloudflare's servers.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.1 86 10/26/2026