Skip to content

API reference

Endpoint

GET https://api.instantgeo.info/v1/geo

GET / is an alias. There is no POST.

Authentication

The key may be supplied three ways, checked in this order:

WhereExampleNotes
Query string?key=ig_pk_…Recommended for browsers, no preflight
Bearer tokenAuthorization: Bearer ig_sk_…Triggers a CORS preflight
HeaderX-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

ParameterDescription
keyYour publishable or secret key
fieldsComma-separated list to trim the response. Narrows what your plan allows; never widens it. Unknown names are ignored.
ipAsk about an address other than the caller’s. Secret keys only. See below.

Response

{
"ip": "203.0.113.7",
"country": "GB",
"countryName": "United Kingdom",
"continent": "EU",
"isEUCountry": false,
"region": "England",
"regionCode": "ENG",
"city": "Bristol",
"postalCode": "BS1",
"latitude": "51.4536",
"longitude": "-2.5975",
"timezone": "Europe/London",
"asn": 64496,
"asOrganization": "EXAMPLE ISP",
"currency": "GBP",
"languages": ["en-GB"],
"metroCode": null
}
FieldTypePlanNotes
ipstringPaidThe caller’s IP. Never stored by us.
countrystringFreeISO 3166-1 alpha-2
countryNamestringFreeEnglish name
continentstringFreeAF AN AS EU NA OC SA
isEUCountrybooleanPaidEU membership, post-Brexit
regionstringPaidFirst-level subdivision
regionCodestringPaidSubdivision code
citystringPaid
timezonestringPaidIANA identifier
postalCodestringPaidFrequently null outside NA and Europe
latitudestringPaidCity-level, not device-level
longitudestringPaid
asnnumberPaidAutonomous system number
asOrganizationstringPaidNetwork operator name
currencystringPaidISO 4217, derived from country
languagesstring[]PaidPrimary languages, derived from country
metroCodestringPaidUS only
partialbooleann/aPresent 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”.

Looking up another address

By default you get the location of whoever made the request. Pass ip= to ask about a different address instead:

Terminal window
curl "https://api.instantgeo.info/v1/geo?key=ig_sk_...&ip=8.8.8.8"

Secret keys only. A publishable key sits in your page source where anybody can copy it. If it accepted ip=, anyone could lift it and use your account as a free lookup service, billed to you. The origin allowlist cannot prevent that, because a script calling us from a server sends no Origin at all.

A lookup counts as one request against your quota, the same as any other.

regionCode, postalCode and timezone are always null through ip=. Not sometimes: the lookup database does not carry them at all, for any address. Every other field is populated normally. If your plan includes those three and you need them, they are only available on the caller path, where they come from the network rather than from a database.

Three more things worth knowing before you build on it:

  • It is less accurate than the caller path. Answering “where is the caller” uses network-level data that is not available for an arbitrary address. A lookup uses a database instead, and the two can disagree about the same address. If you can ask the question from the visitor’s own browser, that answer is the better one.
  • Unknown addresses are not errors. Reserved ranges, recent allocations and some mobile carriers are genuinely absent from the database. You get a 200 with null fields and "partial": true, which says “we do not know” rather than “your address is wrong”.
  • ip in the response echoes what you asked about, not the caller.

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

HeaderMeaning
Cache-Controlprivate, max-age=300, so please cache it
X-RateLimit-LimitRequests included this period
X-RateLimit-RemainingRequests left
X-RateLimit-ResetUnix 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://instantgeo.info/docs/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.

StatusCode
401missing_keyNo key supplied
401malformed_keyWrong shape
401bad_signatureNot issued by us
401unknown_keyNo active record
401revoked_keyExplicitly revoked
403origin_not_allowedOrigin not on the allowlist
403ip_lookup_not_allowedip= sent with a publishable key
403invalid_ipip= was not a valid address
403ip_lookup_unavailableLookups are not enabled here
429rate_limitedPer-IP hourly limit
429quota_exceededMonthly included requests used
404not_foundNo such endpoint
500internal_errorOur 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. The same is true of an address you pass to ip=.

Attribution

Lookups through ip= use the DB-IP Lite City database, made available by DB-IP under the Creative Commons Attribution 4.0 licence. It is not used for the caller path.