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/jsonCompare 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.