# Response format

> The canonical JSON envelope, the 6 intelligence blocks, nullability rules, and response headers.

Every lookup endpoint returns UTF-8 JSON (`application/json; charset=utf-8`). Top-level metadata fields (`ip`, `ip_version`, `matched_network`, `dataset_version`, `updated_at`) sit alongside nested objects for each intelligence block (`geo`, `network`, `connection`, `security`, `security_extensions`).

## A complete lookup response

A lookup on a plan with all 6 intelligence blocks enabled returns:

```json 200 OK
{
  "ip": "198.51.100.7",
  "ip_version": 4,
  "matched_network": "198.51.100.0/24",
  "geo": {
    "country_code": "US",
    "declared_country_code": "US",
    "physical_country_code": "US",
    "is_virtual_location": false,
    "location_type": "physical",
    "confidence": 0.94,
    "subdivision": "Virginia",
    "city": "Ashburn",
    "postal_code": "20147",
    "latitude": 39.0438,
    "longitude": -77.4874,
    "accuracy_radius_km": 10,
    "timezone": "America/New_York"
  },
  "network": {
    "asn": 64501,
    "organization": "Example Cloud LLC"
  },
  "connection": {
    "connection_type": "hosting",
    "is_hosting": true,
    "is_residential": false,
    "is_mobile": false,
    "is_business": false
  },
  "security": {
    "is_vpn": true,
    "is_hosting": true,
    "is_tor_exit": false,
    "is_proxy": false,
    "anonymizer_type": "vpn",
    "confidence": 0.96,
    "reason_codes": [
      "vpn_handshake_confirmed",
      "datacenter_asn_range"
    ]
  },
  "security_extensions": {
    "abuse_velocity_score": 18,
    "is_known_scanner": false,
    "observation_recency_hours": 12,
    "anonymizer_reason": "vpn_handshake_confirmed",
    "threat_level": "medium",
    "abuse_velocity": "low",
    "confidence": 0.96
  },
  "dataset_version": "2026-10-27T00:00:00Z.crawl-1793088000000",
  "updated_at": "2026-10-27T00:00:00Z"
}
```

## Mapping intelligence blocks to JSON objects

| Field family (`field_families`) | JSON object key | Included attributes |
| --- | --- | --- |
| `geo_country` | `geo` | `country_code`, `declared_country_code`, `physical_country_code`, `is_virtual_location`, `location_type`, `confidence` |
| `geo_city` | `geo` | `subdivision`, `city`, `postal_code`, `latitude`, `longitude`, `accuracy_radius_km`, `timezone` |
| `network` | `network` | `asn`, `organization` |
| `connection` | `connection` | `connection_type`, `is_hosting`, `is_residential`, `is_mobile`, `is_business` |
| `anonymity` | `security` | `is_vpn`, `is_hosting`, `is_tor_exit`, `is_proxy`, `anonymizer_type`, `confidence` |
| `security_extensions` | `security_extensions` | `anonymizer_reason`, `threat_level`, `abuse_velocity`, `confidence` |

## Nullability and unknown values

- **Unknown values are `null`, never empty strings**: if an anycast prefix has no postal code, `postal_code` is `null`, never `""` or `"N/A"`.
- **Booleans are always `true` or `false`**: `is_vpn`, `is_tor_exit`, `is_proxy`, `is_hosting`, and `is_virtual_location` are never `null` when their object is present.
- **Confidence scores are calibrated floats in `[0.0, 1.0]`**: both `geo.confidence` and `security.confidence` quantify the agreement across RIR, BGP, RPKI, geofeed, and active measurement evidence.

## Response headers

| Header | Example | Meaning |
| --- | --- | --- |
| `X-Request-Id` | `req_01J8Z4K9X2` | Unique identifier for this request; include it when contacting support. |
| `X-RateLimit-Limit` | `500000` | Monthly lookup allowance for the active plan. |
| `X-RateLimit-Remaining` | `412890` | Remaining monthly lookups before prepaid wallet fallback or quota stop. |
| `X-RateLimit-Reset` | `1735689600` | Unix timestamp (UTC seconds) when the monthly counter resets. |
| `X-Dataset-Version` | `2026-10-27T00:00:00Z.crawl-1793088000000` | Active IP intelligence snapshot serving this lookup. |

## Stability guarantee

Within `/v1`, existing fields are never renamed, removed, or given a different type. New optional fields may be added to an object in minor releases; configure your JSON deserializer to ignore unknown keys (all [official IPScope SDKs](https://ipscope.io/docs/sdks) do this automatically).
