DEV Community

Julian Hart
Julian Hart

Posted on

Checking IP Reputation Programmatically: A Developer's Guide

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?
Enter fullscreen mode Exit fullscreen mode

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}'
Enter fullscreen mode Exit fullscreen mode

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}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
// 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}`);
Enter fullscreen mode Exit fullscreen mode

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")
Enter fullscreen mode Exit fullscreen mode

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)}")
Enter fullscreen mode Exit fullscreen mode

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()
Enter fullscreen mode Exit fullscreen mode

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,
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)