API reference
Endpoint
GET https://api.instantgeo.info/v1/geoGET / is an alias. There is no POST.
Authentication
The key may be supplied three ways, checked in this order:
| Where | Example | Notes |
|---|---|---|
| Query string | ?key=ig_pk_… | Recommended for browsers — no preflight |
| Bearer token | Authorization: Bearer ig_sk_… | Triggers a CORS preflight |
| Header | X-API-Key: ig_sk_… | Triggers a CORS preflight |
Requests with no key are served at the free tier with a hard limit of 60 per hour per IP.
Parameters
| Parameter | Description |
|---|---|
key | Your publishable or secret key |
fields | Comma-separated list to trim the response. Narrows what your plan allows; never widens it. Unknown names are ignored. |
Response
{ "ip": "203.0.113.7", "country": "ID", "countryName": "Indonesia", "continent": "AS", "isEUCountry": false, "region": "Jakarta", "regionCode": "JK", "city": "Jakarta", "postalCode": "10110", "latitude": "-6.2088", "longitude": "106.8456", "timezone": "Asia/Jakarta", "asn": 64496, "asOrganization": "EXAMPLE ISP", "currency": "IDR", "languages": ["id"], "metroCode": null}| Field | Type | Plan | Notes |
|---|---|---|---|
ip | string | Paid | The caller’s IP. Never stored by us. |
country | string | Free | ISO 3166-1 alpha-2 |
countryName | string | Free | English name |
continent | string | Free | AF AN AS EU NA OC SA |
isEUCountry | boolean | Paid | EU membership, post-Brexit |
region | string | Paid | First-level subdivision |
regionCode | string | Paid | Subdivision code |
city | string | Paid | |
timezone | string | Paid | IANA identifier |
postalCode | string | Paid | Frequently null outside NA and Europe |
latitude | string | Paid | City-level, not device-level |
longitude | string | Paid | |
asn | number | Paid | Autonomous system number |
asOrganization | string | Paid | Network operator name |
currency | string | Paid | ISO 4217, derived from country |
languages | string[] | Paid | Primary languages, derived from country |
metroCode | string | Paid | US only |
partial | boolean | — | Present and true when geo data was incomplete |
Every field can be null. A field your plan does not include is absent
rather than null, so you can tell “not on your plan” apart from “we could not
determine it”.
Accuracy
Coordinates are city-level, derived from the IP address. They are not GPS and should not be treated as a device location. A VPN, a corporate proxy, or a mobile carrier’s routing will place a caller somewhere they are not.
partial
When we cannot fully place an IP, you get a 200 with null fields and
"partial": true. We do not return a 500 for missing geolocation — a page that
breaks because a visitor is on an unusual network is worse than a page that
falls back to a default.
Response headers
| Header | Meaning |
|---|---|
Cache-Control | private, max-age=300 — cache it, please |
X-RateLimit-Limit | Requests included this period |
X-RateLimit-Remaining | Requests left |
X-RateLimit-Reset | Unix seconds until the period resets |
Errors
Every non-2xx response has this shape:
{ "error": { "code": "origin_not_allowed", "message": "Origin \"https://evil.com\" is not allowed for key ig_pk_3lSd…. Add it at …", "docs": "https://docs.instantgeo.info/errors/origin_not_allowed" }}The message is written to be shown to a developer and acted on directly. The
docs link goes to a page explaining that specific code.
| Status | Code | |
|---|---|---|
| 401 | missing_key | No key supplied |
| 401 | malformed_key | Wrong shape |
| 401 | bad_signature | Not issued by us |
| 401 | unknown_key | No active record |
| 401 | revoked_key | Explicitly revoked |
| 403 | origin_not_allowed | Origin not on the allowlist |
| 429 | rate_limited | Per-IP hourly limit |
| 429 | quota_exceeded | Monthly included requests used |
| 404 | not_found | No such endpoint |
| 500 | internal_error | Our fault |
CORS
OPTIONS is answered immediately with Access-Control-Max-Age: 86400.
CORS headers are present on error responses too, so a 403 is readable by the calling page rather than showing up as an opaque network failure.
Putting the key in the query string avoids the preflight entirely — see caching for why that matters more than it usually would.
Privacy
We do not store the caller’s IP address. It appears in the response body and nowhere else — not in a log line, not in a table. That is enforced by a check in our CI, not just a policy.