---
title: "ASN geographic distribution from Sugra NetAtlas"
method: GET
path: "/api/v1/network/asn/{asn}/geo-distribution"
tags: ["Sugra NetAtlas"]
---

# ASN geographic distribution from Sugra NetAtlas

`GET /api/v1/network/asn/{asn}/geo-distribution`

Derive country distribution by iterating atlas records for prefixes
matching the given ASN.

Semantic note (SNT-P1-10.1, post 2026-05-25): The ``hq_country`` and
``top_countries`` fields reflect **actual usage country** (geofeed >
cloud-region > peering-registry > RIR allocation priority chain), NOT
the RIR allocation country alone. For globally-distributed ASNs (cloud
providers, CDNs), the distribution now spreads across the regions
where prefixes are actually announced (e.g., AWS AS16509 splits
across us-east-1=US, eu-west-1=IE, ap-northeast-1=JP, etc.) rather
than collapsing to a single RIR-alloc country. For traditional ISPs
the distribution is usually unchanged because they announce from
the same country their RIR allocated to. ``hq_country`` is the
dominant (most-common) actual-usage country, not necessarily the
legal headquarters - the name predates the SNT-P1-10 semantic clarification.

Implementation note: the atlas store does not provide a reverse index
(ASN -> prefixes), so we walk the database. For top ASNs (most
prefixes) this is O(N) in the atlas size (~1 M entries). v1.2-A
caches the response keyed by ``(asn, atlas_etag)`` so subsequent
hits within the same atlas build bypass the full walk; the cache
invalidates automatically when the atlas blob ETag rotates (daily
rebuild).

## Path parameters

- `asn` integer, required

## Response `200`

Successful Response

- NetworkAsnAsnGeoDistributionData
  - `asn` union
    - integer
    - number
  - `holder` string, nullable
  - `geo_aggregate` GeoAggregate
    - `hq_country` string, nullable
    - `top_countries` TopCountry[], nullable
      - `code` string, nullable
      - `prefix_count` union
        - integer
        - number
    - `prefix_count_total` union
      - integer
      - number
    - `country_count` union
      - integer
      - number
  - `_meta` AtlasMeta
    - `product` string, required — Always 'Sugra NetAtlas'.
    - `atlas_built_at` string, nullable — UTC ISO-8601 build time of the atlas snapshot that answered; null only when no connector can vouch for one.
    - `privacy_signal_version` string, required — Version of the privacy/default-route signal set.
    - `confidence` string, required — Confidence of the privacy/default-route signal: high, medium or low.
    - `accuracy` string, required — Accuracy class of the answer (e.g. public, city, country, unknown).
    - `sources` string[], required — Sugra-branded upstream families that contributed.
    - `data_time` string, nullable — When the DATA is from (UTC ISO-8601); null when nothing can vouch for it.
    - `response_time` string, required — When Sugra answered (UTC ISO-8601).
    - `partial` boolean, required — True when at least one upstream failed and the answer is incomplete.
    - `geo_confidence` string, nullable — IP-geo responses only: how trustworthy the resolved city/country is (downgrades for anycast/CDN).
    - `served_from` string, nullable — Where the answer came from (local atlas, live proxy, cache).
    - `fallback_reason` string, nullable — Why a fallback path served the answer, when one did.
    - `sources_coverage` unknown
    - `cached` boolean, nullable — True when the answer was served from the response cache (routes that cache whole answers).
    - `atlas_sha256` string, nullable — SHA-256 of the atlas snapshot (sources/coverage).
    - `endpoint_version` string, nullable — Endpoint contract version where a route declares one (sources/coverage: v1).

## Other responses

- `401` — Missing or invalid `x-api-key` header. JSON body with a stable `code` distinguishing `missing_api_key` (no header sent) from `invalid_api_key` (header sent, key not accepted); any other 401 source carries the generic `unauthorized` with its detail as `reason`. Plus `hint`. `plan` is always null on 401 - an unauthenticated request has no plan; quota exhaustion is 429, not 401.
- `422` — Validation Error
- `429` — Daily rate limit exceeded. Check `X-RateLimit-Reset` for the next window.
- `500` — This API answered a shape its own schema refuses. Typed body `error: response_shape_invalid`. Stays a 500 (WEB-28). Not an upstream failure.
- `503` — Upstream source is temporarily unavailable. Retry after a short delay.

## Changes

> 32 revisions in range; 1 not diffed.

- **2026-09-13** `d2472ec2cf81` — 1 info
  - added the non-success response with the status `500`
- **2026-08-22** `914af3d38c7c` — 1 breaking, 4 info
  - the response's body type changed from no type to `object` for status `200`
  - added the optional property `_meta` to the response with the `200` status
  - added the optional property `asn` to the response with the `200` status
  - added the optional property `geo_aggregate` to the response with the `200` status
  - …1 more
- **2026-08-08** `4c4530760ba1` — 12 info
  - added the optional property `code` to the response with the `401` status
  - added the optional property `code` to the response with the `429` status
  - added the optional property `code` to the response with the `503` status
  - added the optional property `hint` to the response with the `401` status
  - …8 more

[Change history](https://skmtc.dev/sugra/apis/sugra-api/changes/api/v1/network/asn/:asn/geo-distribution/get.md)

---

[API](https://skmtc.dev/sugra/apis/sugra-api.md) · [All operations](https://skmtc.dev/sugra/apis/sugra-api/llms.txt) · [OpenAPI document](https://skmtc.dev/sugra/apis/sugra-api/revisions/d2472ec2cf81?raw)
