# info.description
Self-hosted IP intelligence. Geolocation, network ownership, routing and
infrastructure classification, from published allocation and routing data.

### What the numbers mean

Everything here is a statement about a **network**, not about a person or a
device. The location of an address is the location of the network it is
announced from -- typically a city-level centroid with a confidence radius,
frequently the wrong city, occasionally the wrong country. `location.lat` and
`location.lon` are never coordinates of anything; read them together with
`location.accuracy_radius_km` or not at all.

Every field is optional and `null` means *no enabled source could answer*,
which is a different statement from *the answer is empty*. `meta.sources` lists
which datasets contributed, and `meta.confidence` is the minimum of their
per-field confidences rather than the mean, so one weak source cannot hide
behind four strong ones.

### Data protection

An IP address is personal data (CJEU C-582/14, *Breyer*). This service is
built on that assumption:

* Query logging is off by default. When it is on, addresses are truncated to a
  network or replaced by a rotating HMAC pseudonym **before** any row is
  constructed -- there is no column that can hold a full address.
* Lookups do not leave the deployment. The default provider set is entirely
  local datasets; providers that would transmit the address being looked up to
  a third party are disabled unless egress is explicitly enabled.
* `/api/v1/me` is always `Cache-Control: private, no-store`.

Results must not be used to identify, single out, or make a decision about an
individual. The accuracy on offer does not support it, and neither does the
lawful basis.

### Output formats

JSON by default; `?format=text|csv|yaml|json` to override. Command-line clients
get aligned plain text unless they say otherwise, so `curl` output is readable
and `curl .../country` prints `US` with no quotes and a trailing newline.

# info.x-accuracy-notice
Approximate, network-level location. Derived from published allocation and routing data, not from the device or its user. Must not be used to identify or single out an individual.

# info.x-attribution[]
IP geolocation by DB-IP (https://db-ip.com) — licensed under CC BY 4.0

# info.x-attribution[]
Allocation data from the Regional Internet Registries (RIPE NCC, ARIN, APNIC, LACNIC, AFRINIC).

# info.x-attribution[]
Routing data from the RIPE NCC Routing Information Service (RIS).

# info.x-attribution[]
Network information from PeeringDB (https://www.peeringdb.com), used under its Acceptable Use Policy: Internet operational purposes only, no advertising targeting, no bulk redistribution without approval.

# info.x-attribution[]
Tor exit relay list published by the Tor Project.

# paths./api/v1/me.get.description
Resolves the address this request came from. Which address that is depends on `ORIGEM_TRUSTED_PROXIES` and `ORIGEM_XFF_TRUSTED_HOPS`: forwarded headers are ignored entirely unless the socket peer is a proxy the deployment has declared, so an unconfigured install cannot be spoofed.

Always `Cache-Control: private, no-store`. The answer is about the person asking.

# paths./api/v1/me.get.parameters[].description
Override content negotiation. Without it the server answers JSON, except to command-line clients, which get aligned plain text.

# paths./api/v1/batch.post.description
Accepts `{"ips": [...]}` or a bare JSON array. Results come back in the order they were submitted.

A malformed entry produces an error object in its own slot rather than failing the request: one bad address must not cost the caller the other 999. Exceeding the limit is a 413 that names it.

# paths./api/v1/batch.post.summary
Resolve up to ORIGEM_BATCH_MAX addresses

# paths./api/v1/batch.post.parameters[].description
Override content negotiation. Without it the server answers JSON, except to command-line clients, which get aligned plain text.

# paths./api/v1/health.get.description
200 whenever the service can still answer, including when its data is old: staleness is a warning, not an outage, and a monitor that pages on a 45-day-old ASN file will be ignored within a week. 503 is reserved for the case where no enabled source is ready at all.

`attribution` carries the licence notices that must be reproduced wherever this data is displayed -- machine-discoverable so that a consumer can render them without hard-coding which sources this deployment happens to run.

# paths./api/v1/health.get.summary
Dataset freshness, cache state and attribution

# paths./api/v1/health.get.parameters[].description
Override content negotiation. Without it the server answers JSON, except to command-line clients, which get aligned plain text.

# paths./api/v1/asn/{asn}.get.description
Accepts `AS15169`, `as15169` and `15169`. Returns registry detail plus the prefixes currently seen announced by that AS.

# paths./api/v1/asn/{asn}.get.parameters[].description
AS number, with or without the `AS` prefix.

# paths./api/v1/asn/{asn}.get.parameters[].description
Override content negotiation. Without it the server answers JSON, except to command-line clients, which get aligned plain text.

# paths./api/v1/{ip}.get.description
Every field is optional and `null` means "no enabled source could answer", which is a different statement from an empty value. Non-global addresses -- private ranges, documentation ranges, CGNAT -- resolve rather than 404, with the classification in `meta.scope`; finding one in your logs is itself the finding.

# paths./api/v1/{ip}.get.parameters[].description
Override content negotiation. Without it the server answers JSON, except to command-line clients, which get aligned plain text.

# paths./api/v1/{ip}.get.parameters[].description
IPv4 or IPv6 address. Non-global addresses answer with a scope rather than a 404.

# paths./api/v1/{ip}/{field}.get.description
`text/plain` with a trailing newline and no quoting, so `curl .../country` pipes cleanly. Booleans render as `true`/`false`.

A field that exists but is unknown for this address renders as an empty line with status 200, not 204: a shell cannot distinguish a zero-byte body from a dropped connection, and the request did succeed.

Whole blocks are addressable (`/8.8.8.8/location`) and render as JSON, since there is no sensible unquoted form for a structure. Unknown field names return 404 with the valid list.

# paths./api/v1/{ip}/{field}.get.parameters[].description
Dotted path (`location.country`) or an unambiguous leaf name (`country`, `asn`). Case-insensitive; hyphens are accepted for underscores.

# paths./api/v1/{ip}/{field}.get.parameters[].description
Override content negotiation. Without it the server answers JSON, except to command-line clients, which get aligned plain text.

# paths./api/v1/{ip}/{field}.get.parameters[].description
IPv4 or IPv6 address. Non-global addresses answer with a scope rather than a 404.

# paths./api/v1/{ip}/{field}.get.responses.200.description
The value, unquoted, with a trailing newline.

# components.schemas.BatchRequest.description
``{"ips": [...]}``.

A bare JSON array is accepted too; it cannot be expressed alongside this in
one schema, so it is documented on the operation instead.

# components.schemas.BatchRequest.properties.ips.description
Addresses to resolve. Order is preserved in the response.

# components.schemas.BatchRequest.properties.fields.description
Restrict the lookup to these field paths. Providers that cannot contribute to them are skipped entirely.

# components.schemas.BatchResponse.description
Order-preserving results, one entry per submitted address.

# components.schemas.Error.description
Every non-2xx response body, whatever produced it.

# components.schemas.Health.description
``/health``. 200 whenever the service can still answer.

# components.schemas.Health.properties.attribution.description
Licence notices that must be reproduced wherever this data is shown.

# components.schemas.StatusEnum.description
* `ok` - ok
* `degraded` - degraded
* `unavailable` - unavailable

# components.securitySchemes.bearerAuth.description
Optional. Raises the request quota from the anonymous rate to the token rate; anonymous callers are served either way.

# tags[].description
Single-address queries. The endpoints you will actually use.

# tags[].description
Many addresses in one request, order preserved, failures isolated.

# tags[].description
Autonomous systems and the prefixes they announce.

# tags[].description
Dataset freshness, cache state and licence attribution.