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 |
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.
Code Examples copy & paste
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.