Quick Start 30 seconds
Make a single HTTP GET request — no authentication required:
# Get your public IP as JSON curl https://ippubblico.org/?api=1
Response:
{
"status": "ok",
"ip": "93.45.12.88",
"ipv4": "93.45.12.88",
"ipv6": null,
"isp": "Fastweb SPA",
"asn": "AS30722",
"tz": "Europe/Rome",
"dt": "2026-05-26T14:30:00+02:00",
"city": "Naples",
"region":"Campania",
"country":"Italy",
"lat": 40.85,
"lon": 14.26,
"cached":false
}
Endpoints reference
https://ippubblico.org/
All endpoints respond to GET requests only.
| Endpoint | Returns | Content-Type | Description |
|---|---|---|---|
| GET /?api=1 | JSON | application/json |
Full data object — all available fields |
| GET /?text=1 | text/plain | text/plain |
IPv4 and IPv6 on two lines, or NONE |
| GET https://ipv4.ippubblico.org/ | text/plain | text/plain |
IPv4 address only, or NONE |
| GET https://ipv6.ippubblico.org/ | text/plain | text/plain |
IPv6 address only, or NONE |
Access-Control-Allow-Origin: * — safe to call from browser JavaScript on any domain.
Response Format ?api=1
| Field | Type | Description |
|---|---|---|
status | string | "ok" or "partial" if geolocation failed |
ip | string | Detected IP — may be IPv4 or IPv6 |
ipv4 | string|null | IPv4 address if available |
ipv6 | string|null | IPv6 address if available |
isp | string|null | Internet Service Provider name |
asn | string|null | Autonomous System Number, e.g. "AS3269" |
tz | string|null | IANA timezone, e.g. "Europe/Rome" |
dt | string|null | ISO 8601 datetime in the caller's timezone |
city | string|null | Approximate city name |
region | string|null | Region / state name |
country | string|null | Country name |
lat | float|null | Approximate latitude |
lon | float|null | Approximate longitude |
cached | bool | true if served from 1-hour geolocation cache |
?lang=xx to the JSON endpoint to receive city, country and region in the requested language. Supported: de es fr ja pt ru zh. Example: /?api=1&lang=ja returns "city":"ナポリ".
Plain text endpoints
/?text=1 returns exactly two lines:
93.45.12.88 NONE
Line 1 = IPv4 (or NONE), Line 2 = IPv6 (or NONE). https://ipv4.ippubblico.org/ and https://ipv6.ippubblico.org/ return a single line.
Multilingual Responses unique feature
IPPubblico.org is the only free IP geolocation API that returns city, region and country in the user's language. Add the ?lang=XX parameter to any JSON request.
https://ippubblico.org/?api=1&lang=ja
→ returns city/region/country in Japanese
Examples by language
Code examples
# Japanese — city/region/country in Japanese curl "https://ippubblico.org/?api=1&lang=ja" # Chinese simplified curl "https://ippubblico.org/?api=1&lang=zh" # Russian curl "https://ippubblico.org/?api=1&lang=ru"
import requests # Set lang=ja to get geo fields in Japanese data = requests.get("https://ippubblico.org/?api=1", params={"lang": "ja"}).json() print(data["geo"]["city"]) # 東京 print(data["geo"]["region"]) # 東京都 print(data["geo"]["country"]) # 日本
// Detect user language and request localized geo fields const lang = navigator.language.split("-")[0]; // e.g. "ja", "zh", "ru" const res = await fetch(`https://ippubblico.org/?api=1&lang=${lang}`); const data = await res.json(); console.log(data.geo.city); // localized city name console.log(data.geo.country); // localized country name
Supported languages
The following languages return localized geo names via MaxMind GeoLite2. All other languages return English.
| Code | Language | MaxMind locale |
|---|---|---|
de | Deutsch | de |
es | Español | es |
fr | Français | fr |
ja | 日本語 | ja |
pt | Português | pt-BR |
ru | Русский | ru |
zh | 中文 | zh-CN |
Note: the lang parameter affects only the API response. The database always stores country/city in English for consistency.
All 43 interface languages
The site interface and documentation are available in 43 languages via ?lang=XX. For geo fields, only the 7 languages above are localized; all others return English geo names.
Code Examples copy & paste
Ready-to-use examples for ESP32, Arduino, Raspberry Pi, MicroPython, OpenWRT, Python, Node.js and Go — no API key required.
Python
import requests r = requests.get("https://ippubblico.org/?api=1", timeout=5) r.raise_for_status() data = r.json() print(data["ip"]) # "93.45.12.88" print(data["country"]) # "Italy" print(data["isp"]) # "Fastweb SPA" print(data["tz"]) # "Europe/Rome"
import urllib.request, json with urllib.request.urlopen("https://ippubblico.org/?api=1") as res: data = json.loads(res.read()) print(data["ip"], data["country"])
import asyncio, httpx async def get_ip(): async with httpx.AsyncClient() as client: r = await client.get("https://ippubblico.org/?api=1", timeout=5) return r.json() data = asyncio.run(get_ip()) print(data["ip"])
Node.js
const res = await fetch("https://ippubblico.org/?api=1"); const data = await res.json(); console.log(data.ip); // "93.45.12.88" console.log(data.country); // "Italy" console.log(data.isp); // "Fastweb SPA"
const axios = require("axios"); const { data } = await axios.get("https://ippubblico.org/?api=1"); console.log(data.ip, data.country);
const https = require("https"); https.get("https://ippubblico.org/?api=1", res => { let body = ""; res.on("data", chunk => body += chunk); res.on("end", () => { const data = JSON.parse(body); console.log(data.ip, data.country); }); });
Browser JavaScript
const data = await (await fetch("https://ippubblico.org/?api=1")).json(); document.getElementById("my-ip").textContent = data.ip;
PHP
$ch = curl_init("https://ippubblico.org/?api=1"); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 5, CURLOPT_USERAGENT => "MyApp/1.0", ]); $json = curl_exec($ch); curl_close($ch); $data = json_decode($json, true); echo $data["ip"]; // "93.45.12.88" echo $data["country"]; // "Italy"
$json = file_get_contents("https://ippubblico.org/?api=1"); $data = json_decode($json, true); echo $data["country"];
curl
# Full JSON (IPv4 + IPv6 + ISP + ASN + geolocation) curl https://ippubblico.org/?api=1 # Plain text — IPv4 and IPv6 on two lines curl https://ippubblico.org/?text=1 # IPv4 address only (or NONE) curl https://ipv4.ippubblico.org/ # IPv6 address only (or NONE) curl https://ipv6.ippubblico.org/ # Pretty print JSON with jq curl -s https://ippubblico.org/?api=1 | jq .
Go
package main import ( "encoding/json" "fmt" "net/http" ) type IPData struct { IP string `json:"ip"` Country string `json:"country"` ISP string `json:"isp"` Lat float64 `json:"lat"` Lon float64 `json:"lon"` } func main() { resp, _ := http.Get("https://ippubblico.org/?api=1") defer resp.Body.Close() var d IPData json.NewDecoder(resp.Body).Decode(&d) fmt.Println(d.IP, d.Country) }
Use Cases real world
Accept-Language header for a two-signal detection approach that covers most edge cases.Geofencing example — Python
import requests # Allowed countries for your service ALLOWED = {"Italy", "Germany", "France", "Spain"} def check_access(): data = requests.get("https://ippubblico.org/?api=1", timeout=5).json() country = data.get("country", "") if country not in ALLOWED: raise PermissionError(f"Access denied from {country}") return data # Fraud detection — IP country vs billing country def verify_checkout(billing_country: str) -> bool: data = requests.get("https://ippubblico.org/?api=1", timeout=5).json() ip_country = data.get("country", "") is_datacenter = data.get("asn", "").startswith(("AS14618", "AS16509", "AS15169")) if is_datacenter: return False # AWS / GCP / Azure — likely VPN/proxy return ip_country == billing_country
Network monitoring — Node.js
// Poll every 60 seconds, alert on IP change let lastIP = null; setInterval(async () => { const data = await (await fetch("https://ippubblico.org/?api=1")).json(); if (lastIP && data.ip !== lastIP) { console.warn(`IP changed: ${lastIP} → ${data.ip}`); // trigger alert, update DNS, notify team... } lastIP = data.ip; }, 60_000);
Error Handling resilience
| HTTP Status | Meaning | Action |
|---|---|---|
| 200 | OK — full data | Use normally |
| 200* | status: "partial" — IP detected, geolocation failed | IP fields are reliable; location fields may be null |
| 429 | Rate limit exceeded (if enforced in future) | Retry after 60s with exponential backoff |
| 500 | Server error | Retry up to 3 times, then fail gracefully |
| 503 | Temporary unavailability | Retry after 30s |
import requests, time def get_ip_data(retries=3, timeout=5): for attempt in range(retries): try: r = requests.get("https://ippubblico.org/?api=1", timeout=timeout) r.raise_for_status() data = r.json() if data.get("status") == "partial": # IP ok, location fields may be null pass return data except (requests.RequestException, ValueError): if attempt == retries - 1: return None # fail gracefully time.sleep(2 ** attempt) # exponential backoff
Limits & Cache performance
cached: true field in the JSON response indicates a cache hit. The IP address itself is always live and accurate.
Recommended polling intervals
| Use case | Recommended interval |
|---|---|
| Dynamic IP monitoring | 60 seconds |
| Server/IoT heartbeat | 5 minutes |
| Application startup check | Once, cache result |
| User session (web app) | Once per session |
Force IPv4 / IPv6 dual-stack
Use dedicated subdomains to force the connection protocol, regardless of your client's default stack:
| Subdomain | DNS record | Use case |
|---|---|---|
ipv4.ippubblico.org |
A record only | Retrieve IPv4 even on dual-stack clients |
ipv6.ippubblico.org |
AAAA record only | Retrieve IPv6 even on dual-stack clients |
ippubblico.org |
A + AAAA | OS default — usually IPv6 preferred |
# What is my IPv4? curl https://ipv4.ippubblico.org/ # What is my IPv6? curl https://ipv6.ippubblico.org/ # Full JSON (uses OS default stack — usually IPv6 preferred) curl https://ippubblico.org/?api=1
FAQ common questions
Do I need to send any headers?
No. Just make a plain GET request. No Authorization, no X-API-Key, nothing.
Will this API stay free?
The free tier will always exist. We may introduce optional paid plans for higher volume or SLA guarantees in the future, but basic access will remain free with no registration required.
Can I use this in a commercial product?
Yes. There are no restrictions on commercial use of the free API tier.
Why does my IP show the wrong city?
IP geolocation databases map IP ranges to locations based on ISP registration data, not physical device location. The displayed location is often the ISP's regional infrastructure, not your actual address. Discrepancies of 50–200 km are normal for residential connections.
What is CGNAT and does it affect the response?
Carrier-Grade NAT means your ISP shares one public IPv4 across multiple customers. If your router's "public IP" differs from what this API returns, you are behind CGNAT. The API always returns the actual public IP visible to the internet.
Is there an SDK or official library?
Not yet. The API is simple enough that the code examples above are all you need. Official client libraries for Python and Node.js are planned.
How do I report an issue or request a feature?
Use the contact form on the main site. For enterprise enquiries see the Enterprise page.