Skip to main content

iporigin

Tell a datacenter IP from a home connection — offline, with no API key.

>>> import iporigin
>>> iporigin.classify("52.95.110.1")
Origin(ip='52.95.110.1', kind='hosting', provider='Amazon AWS', source='local')
>>> iporigin.is_datacenter("8.8.8.8")
True
>>> iporigin.is_datacenter("127.0.0.1")
False

No network calls. No signup. No runtime dependencies. The answer comes from a bundled table of 33,000 ranges covering 37 hosting providers, CDNs, consumer VPNs, Tor and declared crawlers.

Install

pip install iporigin

Why

"Is this visitor on a server or on a home connection?" comes up constantly — scoring signups, filtering scrapers out of analytics, deciding whether an abuse report is worth acting on, flagging logins from hosting ranges. The usual answers are a paid API or a hand-maintained list of CIDRs that goes stale in a month.

This is a third option: the providers publish their own ranges, so the list can be compiled from source and rebuilt on a schedule. You get a local lookup in microseconds, and you can verify every byte of the data by re-running the build script.

Use

Classify one address

import iporigin

origin = iporigin.classify("140.82.121.4")
origin.kind          # 'hosting'
origin.provider      # 'GitHub'
origin.is_datacenter # True

kind is one of:

kind meaning examples
hosting a machine in a cloud or hosting provider AWS, Hetzner, OVHcloud
cdn edge infrastructure fronting other people's sites Cloudflare, Akamai
vpn a consumer VPN or private relay exit Mullvad, ProtonVPN, Apple Private Relay
tor a Tor exit node
bot a declared crawler Googlebot, GPTBot, ClaudeBot
reserved private, loopback, link-local, documentation 127.0.0.1, 10.0.0.0/8
unknown in none of our lists — most often a residential ISP

Two sets are exported for the common decisions:

origin.kind in iporigin.DATACENTER_KINDS   # a machine, not a home line
origin.kind in iporigin.ANONYMIZER_KINDS   # vpn or tor

They are deliberately different questions. Someone arriving through Mullvad is a person at home, but the address they arrive from is a server — so it is in both sets, and you may well want to rate-limit one and refuse the other.

unknown means absence of evidence. It is not a positive claim that the address is residential, and the difference matters if you are going to block someone over it.

Scan a lot of them

for origin in iporigin.classify_many(ip_list):
    if origin.is_datacenter:
        print(origin.ip, origin.provider)

classify_many loads the table once for the whole batch.

From the shell

$ iporigin 8.8.8.8 140.82.121.4 192.168.1.1
8.8.8.8                                  hosting   Google
140.82.121.4                             hosting   GitHub
192.168.1.1                              reserved

$ cut -d' ' -f1 access.log | iporigin --datacenter-only --json
{"ip": "34.82.1.5", "kind": "hosting", "provider": "Google Cloud", ...}

iporigin --info prints what is in the bundled dataset and when it was built.

Going beyond the bundled table

The table covers the VPN providers whose exits are publicly tracked — Mullvad, ProtonVPN, Apple Private Relay. Most VPN companies are not in that set. When you need broader coverage, iporigin.online asks the free Unblock Master IP API, which does its own detection:

from iporigin.online import classify_online

classify_online("203.0.113.10")   # may return kind='vpn'

It falls back to the offline answer if the request fails, so it is safe in a request path. Nothing else in the library touches the network — you have to import this module on purpose. No key required.

What is in the dataset

37 providers, in two tiers.

Published by the provider. The authoritative tier — each of these is the company's own feed, fetched at build time:

Provider Feed
Amazon AWS ip-ranges.amazonaws.com/ip-ranges.json
Google, Google Cloud gstatic.com/ipranges/goog.json, cloud.json
Microsoft Azure Service Tags JSON
DigitalOcean digitalocean.com/geo/google.csv
Linode RFC 8805 geofeed
Vultr geofeed.constant.com
Oracle Cloud public_ip_ranges.json
GitHub api.github.com/meta
Cloudflare cloudflare.com/ips-v4, ips-v6
Fastly api.fastly.com/public-ip-list

Community-maintained lists. Some providers publish nothing machine-readable — Hetzner and OVH being the two that matter most, since a large share of abusive traffic comes from them. Consumer VPN exits, Tor and crawler ranges have the same problem for a different reason: nobody with the data has an interest in publishing it. Those come from two community repos, and are second-hand by definition:

Covering Hetzner, OVHcloud, Scaleway, Alibaba Cloud, Leaseweb, UpCloud, IBM Cloud, Huawei Cloud, Tencent Cloud, Rackspace, Akamai, Gcore, Mullvad, ProtonVPN, Apple Private Relay, Tor, and ten declared crawlers.

Both are CC0, which is why these two and not the half-dozen other repos covering the same ground. Redistributing an unlicensed list inside an MIT package is not something a dependency should ask of the people who install it.

About 449,000 published prefixes collapse into 33,647 disjoint ranges (17,169 IPv4, 16,478 IPv6). Rebuild it yourself at any time:

python tools/build_dataset.py

A GitHub Action re-runs that weekly and opens a PR when the ranges move.

Known gaps

Being explicit about these is more useful than pretending they are not there:

  • Most consumer VPNs. Only the ones whose exits are publicly tracked are in the table. Use iporigin.online for the rest.
  • Some provider-owned addresses sit outside the ranges the provider publishes. 1.1.1.1 is Cloudflare's resolver but is not in Cloudflare's published edge list, so it comes back unknown.
  • Second-hand data is second-hand. The community tier is as good as those repos are, and they are not the provider speaking.
  • The data is as accurate as the feeds. A range reassigned yesterday is wrong until the next rebuild.

How the lookup works

Ranges are stored as inclusive integer start/end pairs in sorted, disjoint order, so a lookup is one bisect plus one comparison.

Making them disjoint is the part that matters. Feeds overlap each other — GitHub runs on Azure and AWS, so its prefixes sit inside theirs. A bisect inspects exactly one candidate, and with overlapping ranges that candidate can be a narrow range that ends before the address while a wider range still contains it, which returns unknown for an address plainly in the table. The build script therefore sweeps the ranges into a disjoint partition, and where they overlap the narrowest one wins — GitHub inside Azure answers GitHub, which is the more specific truth.

The table loads lazily on the first lookup, so importing the library and never calling it costs nothing.

Compatibility

Python 3.8+. No dependencies.

License

MIT. The compiled dataset is derived from the providers' own public feeds, each published for exactly this purpose.


Built by Yuix Networks, who also run Unblock Master and its free IP lookup API.

Metadata

Release files for iporigin 1.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for iporigin 1.0.0
File Size Uploaded
iporigin-1.0.0.tar.gz 205.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for iporigin 1.0.0
File Interpreter ABI Platform
iporigin-1.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 401.2 kB

Release files / iporigin-1.0.0.tar.gz

Download URL iporigin-1.0.0.tar.gz
Size 205.1 kB
Tags Source
SHA-256 checksum
How to use checksums
dc2dd0d99856ce031f7342ebe248c4f442b9cb13110c80e5d19005724403a190
BLAKE2b-256 checksum
How to use checksums
d3bf7bf3cb192e287e994fdb2da27c75060238062a6505ae3ac59f7e5a33ee93
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.2

Release files / iporigin-1.0.0-py3-none-any.whl

Download URL iporigin-1.0.0-py3-none-any.whl
Size 196.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
bf525ccfe36029940d7e42bbdf8ed5323b20cc8cf9fe24343cfc49c09816e4d1
BLAKE2b-256 checksum
How to use checksums
b8cc1971f9d9497e26d1064bf8ac7359759ed5805ecc2ed924579498a5fee1a4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.2

Release history Release notifications | RSS feed

1.1.2

2 release files

1.1.1

2 release files

1.1.0

2 release files

This release

1.0.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page