PHP SDK

ip-api.io PHP SDK
composer require ip-api-io/ipapi-php

ip-api-io/ipapi-php is the official, zero-dependency client for PHP — no Composer packages to pull in, just ext-curl and ext-json. One client wraps IP geolocation, VPN/proxy/Tor detection, email validation, and fraud risk scoring, with typed exceptions and batch helpers, so you write the call, not the plumbing.

Location
Mountain View, US
Threat signals
No VPN or proxy
Dependencies
Zero Composer deps
$client->lookup('8.8.8.8')
{
"ip": "8.8.8.8",
"isp": "Google LLC",
"location": {
"country": "United States",
"city": "Mountain View",
"timezone": "America/Los_Angeles"
},
"suspicious_factors": {
"is_vpn": false,
"is_datacenter": true
}
}

What you'll build

  • Install and construct the zero-dependency client
  • Look up any IP — or the caller's IP — with one call
  • Detect VPN, proxy, Tor, and threats; batch up to 100 IPs
  • Handle rate limits and errors the production-ready way
Trusted by thousands of businesses
Fast JSON API responses
Real-time validation
Simple integration, SDKs & examples

Prerequisites

  • PHP 8.1 or higher with the curl and json extensions
  • An ip-api.io API key — get one free
  • Composer for installation — no other packages required
1

Install the package

Add the official ip-api-io/ipapi-php package with Composer. It has zero Composer dependencies — only the ext-curl and ext-json extensions, which ship with most PHP installs.

composer require ip-api-io/ipapi-php
2

Construct the client

Import IpApiIo\Client and create one instance with your API key. Read the key from an environment variable — never hardcode it. The API rejects keyless requests with 401.

<?php

require 'vendor/autoload.php';

use IpApiIo\Client;

$client = new Client(apiKey: getenv('IP_API_IO_KEY'));

Reuse a single client across your request — register it as a singleton in your framework's service container (Laravel, Symfony) so the cURL handle is shared.

3

Look up an IP address

Call lookup($ip) for any IPv4 or IPv6 address, or lookup() with no argument to resolve the caller's own IP. Every response is an associative array with a location array and a suspicious_factors array.

$info = $client->lookup('8.8.8.8');

echo $info['ip'];                       // "8.8.8.8"
echo $info['isp'];                      // "Google LLC"
echo $info['location']['country'];      // "United States"
echo $info['location']['city'];         // "Mountain View"
echo $info['location']['timezone'];     // "America/Los_Angeles"

// Resolve the caller's own IP (no argument):
$me = $client->lookup();
echo $me['ip'], ' ', $me['location']['country'];
Associative arrays: responses are plain PHP arrays, so nested data is read with bracket syntax — $info['suspicious_factors']['is_vpn']. Fields are null for private ranges or unrecognized addresses.
4

Detect VPN, proxy, Tor & batch lookups

The suspicious_factors array carries seven boolean flags on every lookup — this is what sets ip-api.io apart from plain geolocation APIs. You get security intelligence in the same call, at no extra cost.

$info = $client->lookup('185.220.101.45'); // example Tor exit node
$sf = $info['suspicious_factors'];

if ($sf['is_vpn'] || $sf['is_proxy'] || $sf['is_tor_node']) {
    echo 'Anonymized traffic — consider a CAPTCHA or block';
}
if ($sf['is_datacenter']) {
    echo 'Cloud/datacenter IP — likely automated';
}
if ($sf['is_threat']) {
    echo 'Active threat signal — block this request';
}

$isRisky = $sf['is_vpn'] || $sf['is_proxy'] || $sf['is_tor_node'] || $sf['is_threat'];

Need to check many IPs at once? lookupBatch takes up to 100 addresses in a single request (it throws an InvalidArgumentException if the array is empty or longer than 100):

$batch = $client->lookupBatch(['8.8.8.8', '1.1.1.1', '9.9.9.9']);

echo $batch['total_processed'], ' ', $batch['successful_lookups'];

foreach ($batch['results'] as $ip => $result) {
    echo $ip, ' ', $result['location']['country'], ' ', $result['suspicious_factors']['is_vpn'];
}
Tip: want a single block / review / allow decision instead of raw flags? Use $client->riskScore($ip) for a combined fraud risk score from 0 to 100.
5

Production-ready error handling

The client throws typed exceptions so you can react to each failure precisely. It never retries on its own — on a rate limit, RateLimitError tells you exactly when to try again.

use IpApiIo\Client;
use IpApiIo\IpApiError;
use IpApiIo\RateLimitError;
use IpApiIo\AuthenticationError;
use IpApiIo\InvalidRequestError;
use IpApiIo\ServerError;

$client = new Client(apiKey: getenv('IP_API_IO_KEY'));

try {
    $info = $client->lookup('8.8.8.8');
    // ... use $info
} catch (RateLimitError $e) {
    // HTTP 429 — quota exhausted
    echo "Rate limited. Remaining: {$e->remaining}. Resets at {$e->reset}";
} catch (AuthenticationError $e) {
    echo 'Invalid or missing API key — get one free at https://ip-api.io';
} catch (InvalidRequestError $e) {
    echo 'Bad request: ', $e->getMessage();
} catch (ServerError $e) {
    echo 'ip-api.io is having issues — try again later';
} catch (IpApiError $e) {
    echo "error {$e->statusCode}: ", $e->getMessage();
}
No automatic retries: wrap calls in your own retry/backoff logic and use RateLimitError::$reset (a Unix timestamp) to schedule the next attempt instead of hammering the API.

Behind a load balancer, run the lookup server-side and pass the real client IP from the X-Forwarded-For header:

$xff = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? '';
$clientIp = trim(explode(',', $xff)[0]) ?: ($_SERVER['REMOTE_ADDR'] ?? '');

try {
    $info = $client->lookup($clientIp);
    header('Content-Type: application/json');
    echo json_encode([
        'ip'        => $info['ip'],
        'country'   => $info['location']['country'],
        'is_vpn'    => $info['suspicious_factors']['is_vpn'],
        'is_threat' => $info['suspicious_factors']['is_threat'],
    ]);
} catch (IpApiError $e) {
    // Treat ip-api.io as a non-critical dependency
    echo json_encode(['ip' => $clientIp, 'country' => null]);
}
Reference

Response & method reference

lookup() returns an associative array with three parts: the top-level ip/isp/asn, a suspicious_factors array, and a location array. Location fields are null for private ranges or unrecognized addresses.

$client ->lookup('78.55.53.58')
{
  "ip": "78.55.53.58",
  "isp": "Deutsche Telekom AG",
  "asn": "AS3320",
  "suspicious_factors": {
    "is_proxy":      false,
    "is_tor_node":   false,
    "is_spam":       false,
    "is_crawler":    false,
    "is_datacenter": false,
    "is_vpn":        false,
    "is_threat":     false
  },
  "location": {
    "country":           "Germany",
    "country_code":      "DE",
    "city":              "Berlin",
    "latitude":          52.5694,
    "longitude":         13.3753,
    "zip":               "13409",
    "timezone":          "Europe/Berlin",
    "local_time":        "2024-05-20T22:16:52+02:00",
    "local_time_unix":   1716236212,
    "is_daylight_savings": true
  }
}

suspicious_factors — security signals (all boolean)

FieldDescription
is_vpnVPN service, corporate gateway, or self-hosted VPN detected
is_proxyHTTP, HTTPS, or SOCKS proxy (~99.5% accuracy)
is_tor_nodeTor exit node, relay, or bridge (updated in real time)
is_datacenterCloud provider (AWS, GCP, Azure), VPS, or hosting facility
is_threatActive security threat — malware C&C, botnet, or DDoS source
is_spamAssociated with spam, phishing, or malware email campaigns
is_crawlerKnown web crawler, scraper, or bot

location — geographic and timezone data

FieldTypeDescription
countrystringFull country name in English (ISO 3166-1)
country_codestringISO 3166-1 alpha-2 code (e.g. "DE", "US")
citystringCity or municipality name (85–95% accuracy)
latitudefloatDecimal degrees, WGS84 — ~50 km median accuracy radius
longitudefloatDecimal degrees, WGS84 — ~50 km median accuracy radius
zipstringPostal or ZIP code in country-specific format
timezonestringIANA timezone identifier (e.g. "America/Los_Angeles")
local_timestringCurrent local time in ISO 8601 with UTC offset
local_time_unixintUnix timestamp (seconds) in local timezone
is_daylight_savingsboolTrue if currently observing DST; null if not applicable

Client methods

MethodReturnsWhat it does
lookup(?string $ip = null)arrayGeolocate one IP, or the caller's IP if omitted
lookupBatch(array $ips)arrayGeolocate up to 100 IPs in one call
riskScore(?string $ip = null)arrayFraud risk score (0–100) for an IP
torCheck(string $ip)arrayCheck whether an IP is a Tor node
emailInfo(string $email)arrayBasic email validation (syntax, MX, disposable)
validateEmail(string $email)arrayFull validation incl. SMTP deliverability
asn(string $ip)arrayAutonomous System Number lookup
whois(string $domain)arrayWHOIS domain registration info
domainAge(string $domain)arrayDomain registration date and age in days
rateLimit()arrayRemaining quota and plan limits
Full surface: the client also exposes validateEmailBatch, emailRiskScore, ipReputation, reverseDns, forwardDns, mxRecords, domainAgeBatch, and usageSummary. See the README and per-feature guides.
FAQ

Frequently asked questions

Common questions about the ip-api.io PHP SDK.

Is the ip-api-io/ipapi-php package official?

Yes. ip-api-io/ipapi-php is the official PHP client for ip-api.io, maintained by the ip-api.io team and published to Packagist from github.com/ip-api-io/ipapi-php. It is MIT licensed and not affiliated with ip-api.com or ipapi.com.

What is the Composer package name for the ip-api.io SDK?

The package is ip-api-io/ipapi-php; the namespace is IpApiIo. Install and construct it like this:

<?php
// composer require ip-api-io/ipapi-php
use IpApiIo\Client;

$client = new Client(apiKey: getenv('IP_API_IO_KEY'));

It has zero Composer dependencies — just the ext-curl and ext-json extensions — and works on PHP 8.1+.

What do the methods return?

Every method returns parsed JSON as a PHP associative array, so you read fields like $info['location']['country'] and $info['suspicious_factors']['is_vpn'].

Can I use the SDK in front-end code?

No. Calls should run server-side so your API key stays private — never expose the key to a browser. Use the SDK in your PHP backend (Laravel, Symfony, a plain script, or a queue worker); your front end calls your own endpoint, which uses the SDK to reach ip-api.io.

How do I handle rate limits?

On HTTP 429 the SDK throws an IpApiIo\RateLimitError exposing $e->limit, $e->remaining, and $e->reset parsed from the response headers. The client never retries automatically — catch the exception and schedule your retry after the reset timestamp.

Pricing

Pricing

Start using IP-API.io to make your website safer and more user-friendly. Keep out unwanted bots, show visitors content that's relevant to where they are, and spot risky IP addresses quickly. It's perfect for making online shopping more personal and keeping your site secure. Get started today with one of the plans!

Small
€10 /mo
100,000 geo ip requests
10,000 advanced email validation requests
Location data
Email validation
Risk score calculation
Currency data
Time zone data
Threat data
Unlimited support
HTTPS encryption
Create Account to Subscribe
Medium
€20 /mo
300,000 geo ip requests
25,000 advanced email validation requests
Location data
Email validation
Risk score calculation
Currency data
Time zone data
Threat data
Unlimited support
HTTPS encryption
Create Account to Subscribe

Need support?

Explore how IP-API.io can enhance your security, provide robust bot protection, and improve IP geolocation accuracy for your applications.

Contact Support

Found a bug in the SDK?

The ip-api-io/ipapi-php package is open source. Open an issue or pull request on GitHub and we'll take a look.

Open on GitHub