ip2location/ip2location-io-php
Official PHP SDK for IP2Location.io that geolocates IP addresses with country, region, city, coordinates, ISP, ASN, timezone, and proxy/VPN detection. A simple API client for adding IP intelligence to PHP and Laravel apps.
Install with Composer:
composer require ip2location/ip2location-io-php
Get an API key from the IP2Location.io dashboard, then store it in .env:
IP2LOCATION_API_KEY=your-key-here
Add it to config/services.php:
'ip2location' => [
'key' => env('IP2LOCATION_API_KEY'),
],
The smallest useful integration is a wrapper service around IPGeolocation:
namespace App\Services;
use IP2LocationIO\Configuration;
use IP2LocationIO\IPGeolocation;
class IpGeoService
{
private IPGeolocation $client;
public function __construct()
{
$config = new Configuration();
$config->apiKey = config('services.ip2location.key');
$this->client = new IPGeolocation($config);
}
public function lookup(string $ip): ?object
{
$result = $this->client->lookup($ip);
if (isset($result->error)) {
report(new \RuntimeException($result->error->error_message ?? 'IP2Location error'));
return null;
}
return $result;
}
}
Register it as a singleton in AppServiceProvider:
$this->app->singleton(\App\Services\IpGeoService::class);
First use case: enrich a login, registration, or checkout event with country, city, ASN, and proxy/VPN signals. Look first at the package README, then src/Configuration.php, src/IPGeolocation.php, and src/Whois.php. The vendor API documentation matters more than the SDK source because the returned fields depend on your plan.
Wrap the SDK behind your own service. The SDK is a thin API client, not a Laravel package. Binding IpGeoService in the container gives you one place to configure the key, normalize errors, add caching, and fake the service in tests.
Cache aggressively by IP. Geolocation changes slowly. Cache country-level results for hours or days, and cache sensitive fields such as proxy/VPN flags for shorter periods:
$geo = Cache::remember(
"ip2location:{$ip}",
now()->addHours(12),
fn () => app(IpGeoService::class)->lookup($ip)
);
Do not cache failed responses, and decide explicitly whether null means “unknown” or “error”.
Validate and skip non-public IPs before calling the API.
if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
return null;
}
This avoids wasting credits on 127.0.0.1, 10.x, 172.16.x, 192.168.x, and ::1 during local development.
Use middleware only where needed. Attaching geo data to every request is tempting but wasteful. Apply it to authentication, checkout, or API routes:
$request->attributes->set('geo', $geo);
Queue bulk enrichment. For imports, backfills, or analytics jobs, dispatch a job per batch rather than calling lookup() inside a large loop. This keeps request latency low and makes retries and rate limiting manageable.
Separate WHOIS from geolocation. WHOIS lookups are slower, more variable, and often more expensive. Use a dedicated WhoisService and cache by domain:
use IP2LocationIO\Whois;
$whois = new Whois($config);
$data = $whois->lookup('example.com');
Build a small DTO or accessor layer. API responses are dynamic and plan-dependent. Instead of scattering $result->country_code across your app, expose countryCode(), cityName(), isProxy(), and similar methods with safe fallbacks.
Fake your wrapper in tests. The SDK creates its own HTTP calls, so Laravel’s Http::fake() will not intercept them. Bind a fake IpGeoService implementation in your test container, or run a small number of real integration tests against the sandbox.
error property or error code before assuming fields exist. Also handle transport failures, timeouts, and HTTP errors.VARCHAR(45) or equivalent column for IP addresses.null from a transient API failure can hide a working lookup for hours. Log failures and avoid caching them.How can I help you explore Laravel packages today?