Skip to content

Latest commit

 

History

4 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ipfastcheck-python

Python client for the ipfastcheck.com IP reputation API — detect proxies, VPNs, Tor exit nodes and datacenter ranges, plus geolocation and ASN data.

  • No API key. No signup, no token to rotate, no secret to inject into your container.
  • Zero dependencies. Standard library only, Python 3.8+.
  • Caching built in. In-memory or SQLite, with TTLs chosen per result class.
  • Fails open by design. A third-party outage must never take your signup flow down.
  • IPv4 and IPv6.
pip install ipfastcheck

Quick start

from ipfastcheck import Client

client = Client()

result = client.check("185.220.101.1")
print(result.country)        # Germany
print(result.asn)            # AS60729
print(result.risk)           # 100
print(result.is_tor)         # True
print(result.is_datacenter)  # True

Making a decision

Most callers don't want raw flags, they want an action. decide() applies a configurable policy and returns one of ALLOW / REVIEW / DENY:

from ipfastcheck import Client, Action

client = Client()
decision = client.decide(request.remote_addr)

if decision.action is Action.DENY:
    return reject()
elif decision.action is Action.REVIEW:
    return require_email_verification()   # step up, don't slam the door
else:
    return proceed()

print(decision.reasons)   # ['risk_100_ge_90', 'proxy', 'datacenter_or_hosting']

REVIEW exists because blocking is usually the wrong response. Corporate VPNs, shared CGNAT and mobile carrier NAT all produce "suspicious" addresses for entirely legitimate users. Step-up verification costs an abuser real effort while costing a real user a single click.

Tuning the policy

from ipfastcheck import Client, Policy, Action

policy = Policy(
    deny_risk=90,              # risk >= 90  -> DENY
    review_risk=50,            # risk >= 50  -> REVIEW
    deny_tor=True,             # Tor exits are a hard no for signup
    review_datacenter=True,    # consumers don't browse from AS16509
    review_when_degraded=True, # see "Trusting the answer" below
    on_error=Action.ALLOW,     # fail open
)

client = Client(policy=policy)

Trusting the answer

Two things about this API are worth knowing before you gate anything on it.

Don't branch on flags.vpn alone. It under-reports. A live example — an address on AS9009 (M247), well-known VPN infrastructure, returns:

{"flags": {"vpn": false, "proxy": true, "hosting": true}, "risk": 82}

A naive if result.is_vpn: block lets that straight through. Use the composite signal instead — risk, net_type and is_hosting together — which is exactly what Policy does. is_vpn is still exposed, with a warning in its docstring.

Watch for degraded responses. When the upstream reputation backend doesn't answer, the API falls back to a geolocation-only estimate. The response still reports ok: true with a plausible risk score, and the only tell is sources.risk == "geo-only" — where vpn is always false and fraud_score is always null.

This client surfaces that as Result.degraded, caches such answers for two minutes instead of hours, and by default escalates an otherwise-clean degraded result to REVIEW rather than ALLOW:

result = client.check("45.83.91.1")
if result.degraded:
    ...  # "we didn't get an answer", not "it's clean"

Caching

The API rate-limits fresh lookups but serves cached ones freely, and a cold lookup costs roughly 450–600 ms. Any inline use needs a cache — so one is on by default.

from ipfastcheck import Client, SQLiteCache, MemoryCache

# Default: bounded in-process LRU, 10k entries.
client = Client()

# Survives restarts, safe across processes (WAL mode).
client = Client(cache=SQLiteCache("/var/cache/ipfastcheck.sqlite3"))

client = Client(cache=MemoryCache(maxsize=50_000))

TTLs are picked per result, because volatility differs by two orders of magnitude between classes:

Result TTL
Degraded (geo-only) 2 minutes
Tor exit node 15 minutes
Proxy / VPN / datacenter / risk ≥ 50 1 hour
Clean residential 24 hours

Checking your own server

check_self() needs no argument and no key, which makes it a complete health check for "is my egress address flagged?" — worth knowing before your transactional email starts silently vanishing on a freshly provisioned VPS.

result = client.check_self()
if result.risk > 75:
    alert(f"Our egress IP {result.ip} is flagged: risk={result.risk}")
# The same idea without Python at all:
curl -s https://ipfastcheck.com/check

Batch lookups

There is no bulk endpoint, so check_many() is a polite serial loop. It de-duplicates first, which matters a lot on access logs — the same address recurs constantly, and cache hits are free while fresh lookups are not.

with open("access.log") as fh:
    ips = (line.split()[0] for line in fh)

for ip, result in client.check_many(ips, delay=0.2):
    if result.ok and result.risk >= 75:
        print(f"{ip}\t{result.risk}\t{result.asn}\t{result.country_code}")

API reference

Client(base_url, timeout, retries, cache, policy, user_agent)

Method Returns Notes
check(ip, use_cache=True) Result Single lookup. Cached.
check_self() Result This host's egress address. Never cached.
my_ip() str | None Public IP as plain text.
country(ip) str | None Two-letter country code.
decide(ip, policy=None) Decision Lookup plus policy.
check_many(ips, delay=0.0) iterator of (ip, Result) De-duplicated.

Network failures never raise — check Result.ok. Only malformed input raises (InvalidAddress), since that's a caller bug rather than a runtime condition.

Result

ip · country · country_code · city · asn · isp · net_type · risk · fraud_score · flags · is_tor · is_proxy · is_vpn · is_hosting · is_datacenter · is_residential · is_mobile · degraded · age_seconds · from_cache · ok · error · raw

Why keyless matters

Comparable free tiers, as of August 2026:

Service API key HTTPS on free tier VPN/proxy flags free Commercial use
ipfastcheck not required yes yes yes
ip-api.com not required no partial non-commercial only
AbuseIPDB required yes no (abuse score only) yes
ipinfo.io required yes paid add-on yes
MaxMind GeoLite2 account + license key local database no yes

Two practical consequences: an HTTP-only endpoint can't be called from Cloudflare Workers or any TLS-only environment, and a keyless API needs no secret binding, no key rotation and no credential in your container image.

Development

git clone https://github.com/ipfastcheck/ipfastcheck-python
cd ipfastcheck-python
python -m unittest discover -s tests -v   # 37 tests, no network access

Related

License

MIT — see LICENSE.

About

Zero-dependency Python client for the ipfastcheck.com IP reputation API: detect proxies, VPNs, Tor exit nodes and datacenter IPs. No API key required.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages