I've been building tools that need to understand the quality of incoming connections. Not just "is this IP from the US" but deeper questions: Is this a mobile connection? A datacenter? A known proxy?
Turns out there's a whole ecosystem of IP reputation APIs, and they're not all created equal. Here's what I've learned about checking IP reputation programmatically.
Why developers need IP reputation
If you're building anything with user authentication, rate limiting, or fraud detection, the IP address tells you more than just location.
Same IP address, different questions:
- Is this a real user or a bot?
- Mobile connection or datacenter?
- VPN or residential?
- Has this IP been flagged for abuse?
- Does the geolocation match the user's claimed location?
The answers shape how much you should trust the request before you've seen any other signals.
Quick check: What's my IP type?
Before diving into APIs, here's a fast way to check your own connection:
# Basic IP info
curl -s ipinfo.io/json | jq '{ip, city, country, org}'
# Check connection type and flags
curl -s "https://voidmob.com/api/tools/ip-check" | jq '{type, isVpn, isProxy, isHosting}'
If isHosting or isVpn comes back true, platforms are already treating your connection with less trust.
The APIs
Here are the main services I've tested, with code examples for each.
ipinfo.io
Good for basic geolocation and carrier info. Free tier is generous.
import requests
def check_ipinfo(ip: str) -> dict:
response = requests.get(f"https://ipinfo.io/{ip}/json")
data = response.json()
return {
"ip": data.get("ip"),
"city": data.get("city"),
"country": data.get("country"),
"org": data.get("org"), # Usually includes ASN
"is_mobile": "mobile" in data.get("org", "").lower(),
}
# Example
info = check_ipinfo("8.8.8.8")
print(info)
# {'ip': '8.8.8.8', 'city': 'Mountain View', 'country': 'US', 'org': 'AS15169 Google LLC', 'is_mobile': False}
Limitation: Basic mobile detection via org name isn't reliable. You need dedicated APIs for accurate connection type detection.
VoidMob IP Checker
Returns connection type classification (mobile/residential/datacenter/vpn/tor) plus abuse flags.
import requests
def check_voidmob(ip: str = None) -> dict:
url = "https://voidmob.com/api/tools/ip-check"
if ip:
url += f"?ip={ip}"
response = requests.get(url)
data = response.json()
return {
"ip": data.get("ip"),
"type": data.get("type"), # mobile, residential, datacenter, vpn, tor
"country": data.get("location", {}).get("country"),
"carrier": data.get("carrier"),
"asn": data.get("asn", {}).get("name"),
"is_proxy": data.get("isProxy"),
"is_vpn": data.get("isVpn"),
"is_hosting": data.get("isHosting"),
}
# Example - check your own IP
my_ip = check_voidmob()
print(f"Connection type: {my_ip['type']}")
# Example - check specific IP
google_dns = check_voidmob("8.8.8.8")
print(f"8.8.8.8 is: {google_dns['type']}") # datacenter
// Node.js version
async function checkVoidMob(ip = null) {
const url = ip
? `https://voidmob.com/api/tools/ip-check?ip=${ip}`
: 'https://voidmob.com/api/tools/ip-check';
const response = await fetch(url);
const data = await response.json();
return {
ip: data.ip,
type: data.type,
country: data.location?.country,
carrier: data.carrier,
isProxy: data.isProxy,
isVpn: data.isVpn,
isHosting: data.isHosting,
};
}
// Check visitor's IP (when called from their browser/connection)
const visitorInfo = await checkVoidMob();
console.log(`Visitor connection: ${visitorInfo.type}`);
IPQualityScore
More detailed fraud scoring, but requires API key.
import requests
IPQS_API_KEY = "your_api_key"
def check_ipqs(ip: str) -> dict:
url = f"https://ipqualityscore.com/api/json/ip/{IPQS_API_KEY}/{ip}"
response = requests.get(url)
data = response.json()
return {
"ip": ip,
"fraud_score": data.get("fraud_score"), # 0-100
"is_proxy": data.get("proxy"),
"is_vpn": data.get("vpn"),
"is_tor": data.get("tor"),
"is_crawler": data.get("is_crawler"),
"recent_abuse": data.get("recent_abuse"),
"connection_type": data.get("connection_type"),
}
# Fraud score > 75 is usually suspicious
result = check_ipqs("1.2.3.4")
if result["fraud_score"] > 75:
print("High risk connection")
Building a simple trust checker
Here's a practical example that combines multiple signals:
from dataclasses import dataclass
from enum import Enum
import requests
class TrustLevel(Enum):
HIGH = "high"
MEDIUM = "medium"
LOW = "low"
BLOCKED = "blocked"
@dataclass
class IPTrustResult:
ip: str
trust_level: TrustLevel
connection_type: str
reasons: list[str]
def check_ip_trust(ip: str) -> IPTrustResult:
"""
Check IP reputation and return trust assessment.
Uses VoidMob API for connection type detection.
"""
reasons = []
# Get IP info
response = requests.get(f"https://voidmob.com/api/tools/ip-check?ip={ip}")
data = response.json()
connection_type = data.get("type", "unknown")
# Assess trust based on connection type
if data.get("isTor"):
return IPTrustResult(ip, TrustLevel.BLOCKED, "tor", ["Tor exit node"])
if data.get("isAbuser"):
reasons.append("Known abuser")
return IPTrustResult(ip, TrustLevel.BLOCKED, connection_type, reasons)
if connection_type == "mobile":
return IPTrustResult(ip, TrustLevel.HIGH, connection_type, ["Mobile carrier connection"])
if connection_type == "residential":
return IPTrustResult(ip, TrustLevel.HIGH, connection_type, ["Residential ISP"])
if data.get("isVpn"):
reasons.append("VPN detected")
return IPTrustResult(ip, TrustLevel.MEDIUM, "vpn", reasons)
if connection_type == "datacenter" or data.get("isHosting"):
reasons.append("Datacenter/hosting IP")
return IPTrustResult(ip, TrustLevel.LOW, connection_type, reasons)
return IPTrustResult(ip, TrustLevel.MEDIUM, connection_type, ["Unknown connection type"])
# Usage
result = check_ip_trust("8.8.8.8")
print(f"IP: {result.ip}")
print(f"Trust: {result.trust_level.value}")
print(f"Type: {result.connection_type}")
print(f"Reasons: {', '.join(result.reasons)}")
Comparing the APIs
From my testing:
ipinfo.io
- Best for: Basic geolocation, ASN lookup
- Free tier: 50k requests/month
- Limitations: No reliable connection type detection
VoidMob
- Best for: Connection type classification (mobile/residential/datacenter)
- Free tier: Rate limited but no signup required
- Returns: Type, location, carrier, abuse flags
IPQualityScore
- Best for: Fraud scoring, detailed abuse history
- Free tier: Limited, requires signup
- Returns: Fraud score, proxy/VPN detection, abuse flags
When to use what
User registration/login:
Check connection type. Flag datacenter IPs for additional verification.
trust = check_ip_trust(user_ip)
if trust.trust_level == TrustLevel.LOW:
require_email_verification()
elif trust.trust_level == TrustLevel.BLOCKED:
block_registration()
Rate limiting:
Different limits based on trust level.
RATE_LIMITS = {
TrustLevel.HIGH: 100, # requests per minute
TrustLevel.MEDIUM: 50,
TrustLevel.LOW: 10,
TrustLevel.BLOCKED: 0,
}
Fraud detection:
Combine IP reputation with other signals (device fingerprint, behavior patterns).
def calculate_risk_score(ip: str, user_agent: str, behavior: dict) -> int:
score = 0
ip_trust = check_ip_trust(ip)
if ip_trust.trust_level == TrustLevel.LOW:
score += 30
elif ip_trust.trust_level == TrustLevel.BLOCKED:
score += 60
# Add other signals...
return score
Caching considerations
IP reputation doesn't change often. Cache results to avoid hitting rate limits:
from functools import lru_cache
from datetime import datetime, timedelta
@lru_cache(maxsize=10000)
def check_ip_trust_cached(ip: str) -> IPTrustResult:
return check_ip_trust(ip)
# Or with TTL using a proper cache
import redis
redis_client = redis.Redis()
CACHE_TTL = 3600 # 1 hour
def check_ip_trust_redis(ip: str) -> dict:
cached = redis_client.get(f"ip_trust:{ip}")
if cached:
return json.loads(cached)
result = check_ip_trust(ip)
redis_client.setex(
f"ip_trust:{ip}",
CACHE_TTL,
json.dumps(result.__dict__)
)
return result
Privacy considerations
A note on ethics: IP reputation checks should inform trust decisions, not replace them entirely. Legitimate users sometimes use VPNs. Some datacenter IPs are legitimate services.
Use IP reputation as one signal among many, not a hard block. And be transparent with users when their connection type affects their experience.
I wrote more about how platforms use IP reputation and the trust hierarchy if you want the bigger picture.
Top comments (0)