Docs

Migration mode

Migrating from IPinfo? Add ?format=ipinfo to a /v1/check request and GeoQ returns an IPinfo-shaped response. Your existing parsing code keeps working while you point the base URL at GeoQ — switch first, refactor later.

This is a migration aid, not the recommended format. The compatibility shape can't carry GeoQ's native extras — there's no reasons[] and no per-signal evidence here. Once you've switched, drop format=ipinfo and read the native response so you can audit why a score fired. It's the only vendor compatibility format we ship.

Request

Same endpoint and auth as a normal lookup — add one query parameter:

ParameterValue
formatipinfo — return the IPinfo-compatible shape. Omit it (or any other value) for the native GeoQ response.
$ curl "https://api.geoq.io/v1/check?ip=8.8.8.8&format=ipinfo" \
    -H "x-api-key: $GEOQ_API_KEY"

Response

A flat object that mirrors IPinfo's full-detail response, including a privacy object. GeoQ's own extras live under a vendor-prefixed geoq key, so an existing IPinfo parser ignores them:

{
  "ip": "8.8.8.8",
  "city": "Mountain View",
  "region": "California",
  "country": "US",
  "loc": "37.4,-122.1",
  "org": "AS15169 Google LLC",
  "timezone": "America/Los_Angeles",
  "privacy": {
    "vpn": false,
    "proxy": false,
    "tor": false,
    "relay": false,
    "hosting": true,
    "service": "gcp"
  },
  "geoq": {
    "risk_score": 35,
    "attribution": "https://geoq.io/attributions",
    "note": "Migration mode. Drop format=ipinfo for risk.reasons[] + evidence{}."
  }
}

Fields

FieldTypeDescription
ipstringThe IP that was checked.
citystring|nullFrom geo.city.
regionstring|nullFrom geo.region.
countrystring|nullISO 3166-1 alpha-2 code, from geo.country_code (note: this is the code, not the full name).
locstring|null"lat,lng" string, composed from geo.latitude + geo.longitude.
orgstring|null"AS<n> <org>" string, composed from network.asn + network.as_org.
timezonestring|nullFrom geo.timezone.
privacy.vpnbooleanFrom signals.is_vpn.
privacy.proxybooleanFrom signals.is_proxy (residential-proxy detection is beta).
privacy.torbooleanFrom signals.is_tor.
privacy.relaybooleanFrom signals.is_relay (e.g. Apple iCloud Private Relay).
privacy.hostingbooleanTrue when signals.connection_type === "datacenter".
privacy.servicestringFrom signals.relay_provider, else signals.datacenter_provider (e.g. "gcp"), else empty string.
geoq.risk_scorenumberGeoQ's 0–100 risk.score, surfaced under a vendor-prefixed key.
geoq.attributionstringAttribution URL (honour on the Free tier).
geoq.notestringA reminder that this is migration mode.

The privacy mapping

The privacy object maps directly to GeoQ signals: vpn → is_vpn, proxy → is_proxy, tor → is_tor, relay → is_relay, and hosting is true when connection_type === "datacenter". service carries the relay provider (e.g. icloud) when the IP is a relay, otherwise the datacenter provider code. (Earlier versions hardcoded relay: false; GeoQ now has a real relay signal, so it maps through.)

What you give up in this mode

  • No risk.reasons[] — you only get the numeric geoq.risk_score, not the signals that produced it.
  • No per-signal evidence labels (authoritative / inferred / beta).
  • No verified-crawler fields (is_verified_bot, verified_bot_name).
  • country is the ISO code only; the native response also gives you the full country name.

For all of those, switch to the native format. See the full IPinfo migration guide for field-by-field mapping and before/after code.