Developer documentation

One endpoint. No credentials.

Everything you need to add IP intelligence to your application.

Free public access. No signup, API key or application quota. Respect the acceptable-use terms; availability depends on service capacity.

Make a request

Use GET /json to detect the caller, GET /json?ip=ADDRESS for a supplied address, or GET /ADDRESS/json for the path form. Both IPv4 and IPv6 literals are supported. URL-encode query values.

curl --fail "https://ip-info.com/json"
curl --fail "https://ip-info.com/json?ip=94.204.59.232"
curl --fail "https://ip-info.com/94.204.59.232/json"
curl --fail "https://ip-info.com/json?ip=2001:4860:4860::8888"

Replace https:// with http:// for HTTP access. HTTP is not redirected; HTTPS encrypts the connection. Reused HTTPS connections can avoid a fresh handshake.

Only public unicast addresses are accepted. Private, loopback, multicast, documentation and other special-purpose ranges are rejected. IPv4-mapped IPv6 addresses are normalized to IPv4. Hostnames, duplicate parameters, unknown parameters and conflicting path/query IPs are rejected. Matching path/query values are accepted.

The full response

This example illustrates the response format. Live values may differ. Every listed field is present; unavailable values are null.

{
  "ip": "94.204.59.232",
  "city": "Motor City",
  "city_geoname_id": 13118429,
  "region": "Dubai",
  "region_code": "DU",
  "region_geoname_id": 292224,
  "district": "Central",
  "district_code": null,
  "district_geoname_id": 13060544,
  "country": "AE",
  "country_name": "United Arab Emirates",
  "country_geoname_id": 290557,
  "is_eu": false,
  "continent": "AS",
  "continent_name": "Asia",
  "continent_geoname_id": 6255147,
  "loc": "25.0459,55.241",
  "latitude": 25.0459,
  "longitude": 55.241,
  "postal": null,
  "timezone": "Asia/Dubai",
  "weather_code": "AEXX0056",
  "asn": 15802,
  "as_name": "Emirates Integrated Telecommunications Company PJSC",
  "isp": "Emirates Integrated Telecommunications Company PJSC",
  "org": "Emirates Integrated Telecommunications Company",
  "connection_type": "Corporate",
  "user_type": "business",
  "is_anycast": false
}

Field reference

ipstring
Normalized public IP address that was looked up.
citystring | null
City name in English.
city_geoname_idinteger | null
City GeoNames identifier.
regionstring | null
First administrative subdivision in English.
region_codestring | null
Subdivision code as stored in the database; not prefixed with country.
region_geoname_idinteger | null
First subdivision GeoNames identifier.
districtstring | null
Second administrative subdivision in English.
district_codestring | null
Second subdivision code, where available.
district_geoname_idinteger | null
Second subdivision GeoNames identifier.
countrystring | null
Two-letter ISO country code.
country_namestring | null
Country name in English.
country_geoname_idinteger | null
Country GeoNames identifier.
is_euboolean | null
European Union membership; null means unknown.
continentstring | null
Two-letter continent code.
continent_namestring | null
Continent name in English.
continent_geoname_idinteger | null
Continent GeoNames identifier.
locstring | null
Approximate latitude,longitude pair; null if either coordinate is missing.
latitudenumber | null
Approximate latitude in decimal degrees.
longitudenumber | null
Approximate longitude in decimal degrees.
postalstring | null
Postal code, where available.
timezonestring | null
IANA time zone name.
weather_codestring | null
Nearest weather station code supplied by the database.
asninteger | null
Autonomous system number as a number, without AS prefix.
as_namestring | null
Autonomous system organization name.
ispstring | null
Internet service provider name.
orgstring | null
Organization using the IP; separate from ASN and ISP.
connection_typestring | null
Network connection classification, such as Cable/DSL, Cellular, Corporate or Dialup.
user_typestring | null
Network usage classification, such as business, residential, cellular or hosting.
is_anycastboolean | null
Anycast classification; not a VPN or proxy detection flag.

Use your language

curl --fail --max-time 10 "https://ip-info.com/json"

Complete examples remain available below without JavaScript. The interactive selector also offers HTTP variants.

cURL — complete examples

Detect caller IP

curl --fail --max-time 10 "https://ip-info.com/json"

Look up a supplied IP

curl --fail --max-time 10 "https://ip-info.com/json?ip=94.204.59.232"
JavaScript / Node.js — complete examples

Detect caller IP

// Node.js 18+ or a modern browser
const response = await fetch("https://ip-info.com/json", {
  signal: AbortSignal.timeout(10000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
console.log(await response.json());

Look up a supplied IP

// Node.js 18+ or a modern browser
const response = await fetch("https://ip-info.com/json?ip=94.204.59.232", {
  signal: AbortSignal.timeout(10000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
console.log(await response.json());
Python — complete examples

Detect caller IP

# Python 3; standard library only
import json
from urllib.request import urlopen

with urlopen("https://ip-info.com/json", timeout=10) as response:
    print(json.load(response))

Look up a supplied IP

# Python 3; standard library only
import json
from urllib.request import urlopen

with urlopen("https://ip-info.com/json?ip=94.204.59.232", timeout=10) as response:
    print(json.load(response))
PHP — complete examples

Detect caller IP

<?php
$curl = curl_init('https://ip-info.com/json');
curl_setopt_array($curl, [Chttps://ip-info.com/jsonOPT_RETURNTRANSFER => true,
    Chttps://ip-info.com/jsonOPT_TIMEOUT => 10, Chttps://ip-info.com/jsonOPT_FAILONERROR => true]);
$body = curl_exec($curl);
if ($body === false) { throw new RuntimeException(curl_error($curl)); }
curl_close($curl);
print_r(json_decode($body, true, 512, JSON_THROW_ON_ERROR));

Look up a supplied IP

<?php
$curl = curl_init('https://ip-info.com/json?ip=94.204.59.232');
curl_setopt_array($curl, [Chttps://ip-info.com/json?ip=94.204.59.232OPT_RETURNTRANSFER => true,
    Chttps://ip-info.com/json?ip=94.204.59.232OPT_TIMEOUT => 10, Chttps://ip-info.com/json?ip=94.204.59.232OPT_FAILONERROR => true]);
$body = curl_exec($curl);
if ($body === false) { throw new RuntimeException(curl_error($curl)); }
curl_close($curl);
print_r(json_decode($body, true, 512, JSON_THROW_ON_ERROR));
Go — complete examples

Detect caller IP

package main

import ("encoding/json"; "fmt"; "net/http"; "time")

func main() {
    client := &http.Client{Timeout: 10 * time.Second}
    resp, err := client.Get("https://ip-info.com/json")
    if err != nil { panic(err) }
    defer resp.Body.Close()
    if resp.StatusCode != 200 { panic(resp.Status) }
    var data map[string]any
    if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { panic(err) }
    fmt.Println(data)
}

Look up a supplied IP

package main

import ("encoding/json"; "fmt"; "net/http"; "time")

func main() {
    client := &http.Client{Timeout: 10 * time.Second}
    resp, err := client.Get("https://ip-info.com/json?ip=94.204.59.232")
    if err != nil { panic(err) }
    defer resp.Body.Close()
    if resp.StatusCode != 200 { panic(resp.Status) }
    var data map[string]any
    if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { panic(err) }
    fmt.Println(data)
}
Rust — complete examples

Detect caller IP

// Cargo.toml: reqwest = { version = "0.12", features = ["blocking", "json"] }
// serde_json = "1"
fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::blocking::Client::builder()
        .timeout(std::time::Duration::from_secs(10)).build()?;
    let data: serde_json::Value = client.get("https://ip-info.com/json")
        .send()?.error_for_status()?.json()?;
    println!("{data}");
    Ok(())
}

Look up a supplied IP

// Cargo.toml: reqwest = { version = "0.12", features = ["blocking", "json"] }
// serde_json = "1"
fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::blocking::Client::builder()
        .timeout(std::time::Duration::from_secs(10)).build()?;
    let data: serde_json::Value = client.get("https://ip-info.com/json?ip=94.204.59.232")
        .send()?.error_for_status()?.json()?;
    println!("{data}");
    Ok(())
}
Java — complete examples

Detect caller IP

// Java 11+
import java.net.URI;
import java.net.http.*;
import java.time.Duration;

class Lookup {
    public static void main(String[] args) throws Exception {
        var request = HttpRequest.newBuilder(URI.create("https://ip-info.com/json"))
            .timeout(Duration.ofSeconds(10)).GET().build();
        var response = HttpClient.newHttpClient()
            .send(request, HttpResponse.BodyHandlers.ofString());
        if (response.statusCode() != 200) throw new RuntimeException("Lookup failed");
        System.out.println(response.body());
    }
}

Look up a supplied IP

// Java 11+
import java.net.URI;
import java.net.http.*;
import java.time.Duration;

class Lookup {
    public static void main(String[] args) throws Exception {
        var request = HttpRequest.newBuilder(URI.create("https://ip-info.com/json?ip=94.204.59.232"))
            .timeout(Duration.ofSeconds(10)).GET().build();
        var response = HttpClient.newHttpClient()
            .send(request, HttpResponse.BodyHandlers.ofString());
        if (response.statusCode() != 200) throw new RuntimeException("Lookup failed");
        System.out.println(response.body());
    }
}
C# — complete examples

Detect caller IP

// .NET 6+
using System;
using System.Net.Http;
using System.Text.Json;

using var client = new HttpClient { Timeout = TimeSpan.FromSeconds(10) };
using var response = await client.GetAsync("https://ip-info.com/json");
response.EnsureSuccessStatusCode();
using var data = JsonDocument.Parse(await response.Content.ReadAsStringAsync());
Console.WriteLine(data.RootElement);

Look up a supplied IP

// .NET 6+
using System;
using System.Net.Http;
using System.Text.Json;

using var client = new HttpClient { Timeout = TimeSpan.FromSeconds(10) };
using var response = await client.GetAsync("https://ip-info.com/json?ip=94.204.59.232");
response.EnsureSuccessStatusCode();
using var data = JsonDocument.Parse(await response.Content.ReadAsStringAsync());
Console.WriteLine(data.RootElement);
Ruby — complete examples

Detect caller IP

require "net/http"
require "json"

uri = URI("https://ip-info.com/json")
response = Net::HTTP.start(uri.host, uri.port,
    use_ssl: uri.scheme == "https", open_timeout: 10, read_timeout: 10) do |http|
  http.get(uri.request_uri)
end
raise "HTTP #{response.code}" unless response.is_a?(Net::HTTPSuccess)
puts JSON.parse(response.body)

Look up a supplied IP

require "net/http"
require "json"

uri = URI("https://ip-info.com/json?ip=94.204.59.232")
response = Net::HTTP.start(uri.host, uri.port,
    use_ssl: uri.scheme == "https", open_timeout: 10, read_timeout: 10) do |http|
  http.get(uri.request_uri)
end
raise "HTTP #{response.code}" unless response.is_a?(Net::HTTPSuccess)
puts JSON.parse(response.body)

Verify a Shifter proxy exit

Send the caller-detection request through your proxy to see its exit IP and database location. Replace the username and password with your own Shifter credentials; do not put credentials into the IP Info URL.

curl --fail --max-time 15 \
  --proxy http://p.shifter.io:443 \
  --proxy-user "customer-USERNAME-country-us-sid-123ABC:PASSWORD" \
  https://ip-info.com/json

Compare country with your requested country code. Different services may report different city or country values.

Errors and retries

{
  "error": {
    "code": "invalid_ip",
    "message": "Supply a literal IPv4 or IPv6 address."
  }
}
400 · invalid_ip
Malformed, conflicting, non-public, or unsupported lookup input.
404 · ip_not_found
No record in the active database.
405 · method_not_allowed
Use GET for lookups.
431 · request_too_large
Request headers exceed the configured limit.
503 · database_unavailable
Database unavailable; retry later with backoff.
504 · timeout
Request timed out; retry later with backoff.

Fix 4xx inputs before retrying. For 503/504 or connection failures, use bounded exponential backoff, for example 1, 2 and 4 seconds, then report failure. Low-level malformed HTTP may be rejected before routing.

Browser access and caching

Public GET requests allow cross-origin reads. API responses use Cache-Control: no-store; do not cache visitor-specific responses in shared caches. An HTTPS page must use HTTPS for fetch requests to avoid mixed-content blocking.

Data limitations

Coordinates are approximate. Anycast IPs may be used in many locations. ASN and ISP fields do not establish a person’s identity or prove VPN/proxy status. The first subdivision maps to region and the second to district. English names only are returned. Data may be incomplete or outdated.

Download the OpenAPI 3.1 definition or read the plain-text reference.