Look up where an IP is: no signup, no key, no 300-page manual.
Browsers can call it directly (CORS *) and it works from a single curl line.
/geoLoading…
/geo/jsonLoading…
/img1| Path | Content-Type | Notes |
|---|---|---|
| /geo | text/plain | 4 lines of plain text; defaults to your own IP |
| /geo/<ip or domain> | text/plain | Explicit target; domains are resolved to their A record |
| /geo/json[/<ip>] | application/json | Structured fields including coordinates, timezone and risk score |
| /geo/jsonp?callback=fn | application/javascript | Old-school JSONP for pages that can only add a <script> |
| /img1[/<ip>] | image/svg+xml | Badge image; defaults to the visitor's IP |
Every path also accepts ?ip=, e.g. /geo/json?ip=1.1.1.1 — both forms are equivalent.
Always four lines in a fixed order, with - for anything unavailable — so line numbers never shift:
line 1 IP address line 2 country region city (space separated) line 3 ASN, e.g. AS15169 line 4 ISP / organization full name
| Field | Type | Notes |
|---|---|---|
| ip | string | The IP actually queried (domains are resolved first) |
| input | string | Only present when a domain was given; keeps the original input |
| country / country_code | string | Country name and ISO code; Hong Kong / Macao / Taiwan are reported as "Hong Kong SAR, China" / "Macao SAR, China" / "Taiwan, China" |
| region / city | string | Region and city |
| location | string | The three above joined for you |
| lat / lon | number | Coordinates (city level, not street level) |
| timezone | string | IANA timezone name |
| asn / asn_org | string | e.g. AS13335 / Cloudflare, Inc. |
| isp / org | string | Carrier and registered organisation |
| ip_type | string | Datacenter / IDC, CDN edge node, residential / fixed-line, mobile network, VPN / proxy, Tor exit node |
| risk_score | number|null | 0-100 risk score; null means this IP was never scored here (no blacklists were run, so we don't guess) |
| risk_level | string|null | Low / moderate-low / moderate-high / high risk |
| maps | string|null | OpenStreetMap link |
| cached | boolean | Whether the edge cache was hit |
Drop it into a forum signature, your homepage or a README: it shows the visitor's own IP location, so a thousand people see a thousand different results — that is the fun part. Pure SVG, no external requests, under 2KB.
| Param | Value | Notes |
|---|---|---|
| ip | IP / domain | Omitted: shows the visitor's own IP |
| theme | light (default) / dark | Dark background for dark forums |
| mask | 1 | Mask it: IPv4 becomes 1.2.*.*, IPv6 keeps the first 4 groups |
| lang | en / zh | Badge labels and country name language |
The image endpoint has a wider quota — 180 req/min — because one forum page can carry a dozen badges.
| Kind | Quota | Window |
|---|---|---|
| Text / JSON | 60 requests | per minute, per IP |
| Badge image | 180 requests | per minute, per IP |
Every response carries these three headers, so you can see the limit coming:
X-RateLimit-Limit: 60 X-RateLimit-Remaining: 57 X-RateLimit-Reset: 1761495060 # unix timestamp of the window reset
Over quota returns 429 + Retry-After. Responses for an explicit IP are cached 10 minutes at the edge
and 1 minute locally; responses for your own IP are never cached (otherwise you would be served someone else's address).
| Code | Meaning |
|---|---|
| 200 | OK |
| 400 | Bad parameters (invalid IP/domain, illegal JSONP callback) |
| 404 | Not found (private address, domain without an A record, no upstream data) |
| 405 | Anything other than GET |
| 429 | Over quota — check Retry-After |
| 504 | DNS resolution timed out |
null — we will not invent a number.Use it for whatever you like, no attribution needed. Questions or a field you want? Just ask.