IP Intelligence for Fraud Prevention - A Complete Guide for 2026
Learn how to use IP intelligence data including geolocation, risk scoring, and proxy detection to prevent online fraud, account takeover, and payment fraud.
Online fraud costs businesses over $48 billion annually. As fraudsters become more sophisticated, traditional rule-based systems struggle to keep pace. IP intelligence provides a critical layer of defense by analyzing connection metadata that fraudsters cannot easily hide or spoof.
This guide covers practical techniques for using IP data to detect and prevent fraud across authentication, payments, and account management workflows.
Understanding IP-Based Fraud Signals
Every internet connection carries metadata that reveals valuable context about the user. IP intelligence transforms raw IP addresses into actionable risk signals.
Core Fraud Indicators
Geographic Anomalies
- Billing address in New York, IP geolocation in Lagos
- Account registration from one country, login from another within minutes
- Transactions from countries where you have no legitimate customers
Anonymization Signals
- VPN usage during high-value transactions
- Tor exit nodes attempting account access
- Residential proxies masking true origin
- Datacenter IPs pretending to be residential users
Network Characteristics
- Multiple accounts from the same IP address
- IP ranges associated with hosting providers
- ASNs known for abuse or bulletproof hosting
- IPs appearing on threat intelligence feeds
The Risk Scoring Approach
Rather than blocking based on single signals, modern fraud prevention uses risk scores that aggregate multiple factors:
function calculateFraudRisk(ipData, transactionData, userHistory) { let riskScore = 0; const reasons = [];
// IP-based signals (0-40 points) if (ipData.security.is_vpn) { riskScore += 15; reasons.push("vpn_detected"); }
if (ipData.security.is_tor) { riskScore += 30; reasons.push("tor_exit_node"); }
if (ipData.security.is_datacenter) { riskScore += 10; reasons.push("datacenter_ip"); }
if (ipData.security.threat_lists.length > 0) { riskScore += 20; reasons.push("threat_list_match"); }
// Geographic signals (0-30 points) const countryMismatch = transactionData.billingCountry !== ipData.location.country_code;
if (countryMismatch) { riskScore += 25; reasons.push("country_mismatch"); }
// Behavioral signals (0-30 points) const isNewLocation = !userHistory.locations.includes( ipData.location.country_code, );
if (isNewLocation && userHistory.accountAge < 30) { riskScore += 20; reasons.push("new_location_new_account"); }
return { score: Math.min(riskScore, 100), reasons, recommendation: getRecommendation(riskScore), };}
function getRecommendation(score) { if (score >= 80) return "BLOCK"; if (score >= 60) return "MANUAL_REVIEW"; if (score >= 40) return "STEP_UP_AUTH"; if (score >= 20) return "MONITOR"; return "ALLOW";}Fraud Prevention Use Cases
Payment Fraud Prevention
E-commerce payment fraud is the most financially damaging fraud type. IP intelligence helps identify suspicious transactions before authorization.
Pre-Authorization Checks
async function checkPaymentFraud(ip, payment) { const ipData = await fetch(`https://api.ipbot.com/${ip}`).then((r) => r.json(), );
const checks = { // Geographic verification locationMatch: ipData.location.country_code === payment.billingCountry,
// Anonymization detection isAnonymized: ipData.security.is_vpn || ipData.security.is_proxy || ipData.security.is_tor,
// Risk assessment riskScore: ipData.security.risk_score,
// Network type isResidential: !ipData.security.is_datacenter, };
// High-risk indicators if (payment.amount > 500 && checks.isAnonymized) { return { action: "REQUIRE_3DS", reason: "High-value transaction from anonymized connection", }; }
if (!checks.locationMatch && checks.riskScore > 50) { return { action: "DECLINE", reason: "Geographic mismatch with elevated risk", }; }
if (checks.riskScore > 70) { return { action: "MANUAL_REVIEW", reason: "High risk score", }; }
return { action: "APPROVE" };}Velocity Checks
Track transaction patterns from individual IPs:
class VelocityTracker { constructor(redisClient) { this.redis = redisClient; }
async checkVelocity(ip, userId) { const now = Date.now(); const hourAgo = now - 3600000; const dayAgo = now - 86400000;
// Track transactions per IP const ipKey = `velocity:ip:${ip}`; const userKey = `velocity:user:${userId}`;
const [ipTxns, userTxns] = await Promise.all([ this.redis.zrangebyscore(ipKey, hourAgo, now), this.redis.zrangebyscore(userKey, dayAgo, now), ]);
const alerts = [];
// More than 5 transactions from same IP in 1 hour if (ipTxns.length > 5) { alerts.push({ type: "IP_VELOCITY", message: `${ipTxns.length} transactions from IP in last hour`, severity: "high", }); }
// More than 20 transactions from user in 24 hours if (userTxns.length > 20) { alerts.push({ type: "USER_VELOCITY", message: `${userTxns.length} transactions in last 24 hours`, severity: "medium", }); }
return alerts; }
async recordTransaction(ip, userId, transactionId) { const now = Date.now(); await Promise.all([ this.redis.zadd(`velocity:ip:${ip}`, now, transactionId), this.redis.zadd(`velocity:user:${userId}`, now, transactionId), // Expire keys after 24 hours this.redis.expire(`velocity:ip:${ip}`, 86400), this.redis.expire(`velocity:user:${userId}`, 86400), ]); }}Account Takeover Prevention
Account takeover (ATO) is a growing threat where attackers gain access to legitimate user accounts. IP intelligence helps detect unauthorized access.
Login Anomaly Detection
async function detectLoginAnomaly(userId, ip) { const [ipData, loginHistory] = await Promise.all([ fetch(`https://api.ipbot.com/${ip}`).then((r) => r.json()), getLoginHistory(userId, 30), // Last 30 days ]);
const anomalies = [];
// Check if this is a new location const knownCountries = new Set(loginHistory.map((l) => l.country_code));
if (!knownCountries.has(ipData.location.country_code)) { anomalies.push({ type: "NEW_COUNTRY", severity: "high", details: { newCountry: ipData.location.country_code, knownCountries: Array.from(knownCountries), }, }); }
// Check for impossible travel const lastLogin = loginHistory[0]; if (lastLogin) { const hoursSinceLastLogin = (Date.now() - lastLogin.timestamp) / 3600000; const distanceKm = calculateDistance( lastLogin.latitude, lastLogin.longitude, ipData.location.latitude, ipData.location.longitude, );
// Assume max travel speed of 900 km/h (commercial flight) const possibleDistance = hoursSinceLastLogin * 900;
if (distanceKm > possibleDistance) { anomalies.push({ type: "IMPOSSIBLE_TRAVEL", severity: "critical", details: { distanceKm, hoursSinceLastLogin, possibleDistance, }, }); } }
// Check for anonymization during login if (ipData.security.is_vpn || ipData.security.is_tor) { // Only flag if user doesn't normally use VPN const vpnLogins = loginHistory.filter((l) => l.is_vpn).length; const vpnRatio = vpnLogins / loginHistory.length;
if (vpnRatio < 0.1) { // Less than 10% VPN use historically anomalies.push({ type: "UNUSUAL_VPN", severity: "medium", details: { historicalVpnRatio: vpnRatio, currentConnection: ipData.security.is_tor ? "tor" : "vpn", }, }); } }
return anomalies;}
function calculateDistance(lat1, lon1, lat2, lon2) { const R = 6371; // Earth's radius in km const dLat = ((lat2 - lat1) * Math.PI) / 180; const dLon = ((lon2 - lon1) * Math.PI) / 180; const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos((lat1 * Math.PI) / 180) * Math.cos((lat2 * Math.PI) / 180) * Math.sin(dLon / 2) * Math.sin(dLon / 2); const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); return R * c;}Account Creation Fraud
Fraudsters create fake accounts for various purposes: promotional abuse, review manipulation, spam, and as staging for future attacks.
Registration Fraud Detection
import requestsfrom datetime import datetime, timedelta
class RegistrationFraudDetector: def __init__(self, redis_client): self.redis = redis_client
def analyze_registration(self, ip_address: str, email: str) -> dict: # Get IP intelligence ip_data = requests.get(f'https://api.ipbot.com/{ip_address}').json()
signals = [] risk_score = 0
# Check for datacenter/hosting IP if ip_data['security']['is_datacenter']: signals.append('datacenter_ip') risk_score += 25
# Check for proxy/VPN if ip_data['security']['is_proxy'] or ip_data['security']['is_vpn']: signals.append('anonymized_connection') risk_score += 20
# Check for Tor if ip_data['security']['is_tor']: signals.append('tor_exit_node') risk_score += 35
# Check threat lists if ip_data['security']['threat_lists']: signals.append('threat_list_match') risk_score += 30
# Check registration velocity from this IP reg_count = self.get_recent_registrations(ip_address) if reg_count > 3: signals.append('high_registration_velocity') risk_score += 20
# Check disposable email if self.is_disposable_email(email): signals.append('disposable_email') risk_score += 25
# Determine action if risk_score >= 70: action = 'BLOCK' elif risk_score >= 50: action = 'REQUIRE_PHONE_VERIFICATION' elif risk_score >= 30: action = 'SHOW_CAPTCHA' else: action = 'ALLOW'
return { 'risk_score': min(risk_score, 100), 'signals': signals, 'action': action, 'ip_data': { 'country': ip_data['location']['country_code'], 'is_vpn': ip_data['security']['is_vpn'], 'risk_score': ip_data['security']['risk_score'] } }
def get_recent_registrations(self, ip: str) -> int: key = f'registrations:{ip}' return self.redis.zcount(key, '-inf', '+inf')
def record_registration(self, ip: str, user_id: str): key = f'registrations:{ip}' self.redis.zadd(key, {user_id: datetime.now().timestamp()}) self.redis.expire(key, 86400) # 24 hour window
def is_disposable_email(self, email: str) -> bool: domain = email.split('@')[1].lower() disposable_domains = {'tempmail.com', 'throwaway.email', '10minutemail.com'} return domain in disposable_domainsBuilding a Fraud Prevention Pipeline
A robust fraud prevention system combines multiple signals and uses a layered approach.
Architecture Overview
User Action --> IP Intelligence --> Risk Engine --> Decision | | v v [Cache Layer] [ML Model] | v [Rules Engine] | +-----------+-----------+ | | | ALLOW CHALLENGE BLOCKImplementation
class FraudPreventionPipeline { constructor(config) { this.cache = new IPCache({ ttl: 3600000 }); this.rules = config.rules || []; this.mlModel = config.mlModel; }
async evaluate(context) { const { ip, userId, action, metadata } = context;
// Step 1: Get IP intelligence (with caching) const ipData = await this.getIPData(ip);
// Step 2: Enrich context const enrichedContext = { ...context, ip: { ...ipData, risk_score: ipData.security.risk_score, is_anonymized: ipData.security.is_vpn || ipData.security.is_proxy || ipData.security.is_tor, threat_level: ipData.security.threat_level, }, };
// Step 3: Apply rule-based checks const ruleResults = this.evaluateRules(enrichedContext);
// Step 4: ML scoring (optional) let mlScore = 0; if (this.mlModel) { mlScore = await this.mlModel.predict(enrichedContext); }
// Step 5: Combine scores const combinedScore = this.combineScores(ruleResults, mlScore);
// Step 6: Make decision const decision = this.makeDecision(combinedScore, action);
// Step 7: Log for analysis await this.logDecision(enrichedContext, decision);
return decision; }
async getIPData(ip) { // Check cache let data = this.cache.get(ip); if (data) return data;
// Fetch from API const response = await fetch(`https://api.ipbot.com/${ip}`); data = await response.json();
// Cache based on risk const ttl = data.security.risk_score > 50 ? 1800000 : 3600000; this.cache.set(ip, data, ttl);
return data; }
evaluateRules(context) { const results = [];
for (const rule of this.rules) { if (this.matchesCondition(rule.condition, context)) { results.push({ rule: rule.name, score: rule.score, action: rule.action, }); } }
return results; }
matchesCondition(condition, context) { // Simple condition matching const { field, operator, value } = condition; const contextValue = this.getNestedValue(context, field);
switch (operator) { case "eq": return contextValue === value; case "gt": return contextValue > value; case "lt": return contextValue < value; case "in": return value.includes(contextValue); case "contains": return contextValue?.includes(value); default: return false; } }
getNestedValue(obj, path) { return path.split(".").reduce((o, p) => o?.[p], obj); }
combineScores(ruleResults, mlScore) { // Weight rule-based and ML scores const ruleScore = ruleResults.reduce((sum, r) => sum + r.score, 0);
if (this.mlModel) { // 60% rules, 40% ML return ruleScore * 0.6 + mlScore * 0.4; }
return ruleScore; }
makeDecision(score, action) { // Action-specific thresholds const thresholds = { login: { block: 80, challenge: 50, monitor: 30 }, payment: { block: 70, challenge: 40, monitor: 20 }, registration: { block: 60, challenge: 35, monitor: 15 }, };
const t = thresholds[action] || thresholds.login;
if (score >= t.block) return { action: "BLOCK", score }; if (score >= t.challenge) return { action: "CHALLENGE", score }; if (score >= t.monitor) return { action: "MONITOR", score }; return { action: "ALLOW", score }; }
async logDecision(context, decision) { // Log for analysis and model training console.log({ timestamp: new Date().toISOString(), ip: context.ip, userId: context.userId, action: context.action, decision: decision.action, score: decision.score, }); }}
// Example usageconst pipeline = new FraudPreventionPipeline({ rules: [ { name: "tor_block", condition: { field: "ip.security.is_tor", operator: "eq", value: true }, score: 40, action: "BLOCK", }, { name: "high_risk_ip", condition: { field: "ip.risk_score", operator: "gt", value: 70 }, score: 30, action: "CHALLENGE", }, { name: "datacenter_payment", condition: { field: "ip.security.is_datacenter", operator: "eq", value: true, }, score: 20, action: "MONITOR", }, ],});
const decision = await pipeline.evaluate({ ip: "185.220.101.42", userId: "user_123", action: "payment", metadata: { amount: 499.99 },});Reducing False Positives
High false positive rates erode customer trust and increase operational costs. Balance security with user experience.
Contextual Decisioning
Consider the full context, not just IP signals:
function adjustForContext(ipRisk, context) { let adjustment = 0;
// Returning customer with purchase history if (context.customerLifetimeValue > 1000) { adjustment -= 15; }
// Account age reduces risk if (context.accountAgeDays > 365) { adjustment -= 10; }
// Previous successful transactions from this IP if (context.previousSuccessfulTxns > 5) { adjustment -= 20; }
// Device fingerprint matches known device if (context.isKnownDevice) { adjustment -= 15; }
return Math.max(0, ipRisk + adjustment);}VPN Handling
Not all VPN users are fraudsters. Handle them thoughtfully:
function handleVPNUser(ipData, context) { // Allow VPNs for privacy-sensitive actions const privacySensitiveActions = ["medical", "legal", "financial_planning"]; if (privacySensitiveActions.includes(context.category)) { return "ALLOW_WITH_LOGGING"; }
// Known VPN user (historically uses VPN) if (context.userHistoricalVpnRate > 0.5) { return "ALLOW"; }
// Corporate VPN ranges if (isKnownCorporateVPN(ipData.network.asn)) { return "ALLOW"; }
// First-time VPN use on high-value transaction if (context.transactionValue > 500 && context.userHistoricalVpnRate < 0.1) { return "STEP_UP_AUTH"; }
return "MONITOR";}Metrics and Monitoring
Track key metrics to optimize your fraud prevention:
| Metric | Description | Target |
|---|---|---|
| Detection Rate | % of fraud caught | > 95% |
| False Positive Rate | % of legitimate users flagged | < 2% |
| Review Rate | % of transactions needing manual review | < 5% |
| Block Rate | % of transactions blocked | < 1% |
| Customer Friction | Average extra steps per transaction | < 0.1 |
Monitoring Dashboard
class FraudMetrics { constructor(redis) { this.redis = redis; }
async recordDecision(decision, wasActuallyFraud) { const date = new Date().toISOString().split("T")[0]; const key = `fraud:metrics:${date}`;
await this.redis.hincrby(key, "total", 1); await this.redis.hincrby(key, `decision:${decision}`, 1);
if (wasActuallyFraud) { await this.redis.hincrby(key, "actual_fraud", 1); if (decision === "BLOCK" || decision === "CHALLENGE") { await this.redis.hincrby(key, "true_positive", 1); } else { await this.redis.hincrby(key, "false_negative", 1); } } else { if (decision === "BLOCK" || decision === "CHALLENGE") { await this.redis.hincrby(key, "false_positive", 1); } } }
async getMetrics(date) { const key = `fraud:metrics:${date}`; const data = await this.redis.hgetall(key);
const total = parseInt(data.total) || 1; const truePositive = parseInt(data.true_positive) || 0; const falsePositive = parseInt(data.false_positive) || 0; const falseNegative = parseInt(data.false_negative) || 0;
return { total, detectionRate: truePositive / (truePositive + falseNegative) || 0, falsePositiveRate: falsePositive / total, blockRate: (parseInt(data["decision:BLOCK"]) || 0) / total, reviewRate: (parseInt(data["decision:CHALLENGE"]) || 0) / total, }; }}Conclusion
IP intelligence is a foundational layer of modern fraud prevention. By combining geolocation, risk scoring, and proxy detection with behavioral signals and contextual data, you can build robust defenses that protect your business while maintaining a good user experience.
Key takeaways:
- Use risk scores, not binary decisions - Aggregate multiple signals for nuanced decisioning
- Cache intelligently - IP data is stable; cache for hours, not seconds
- Consider context - The same IP signal means different things in different scenarios
- Monitor and iterate - Track false positives and continuously tune thresholds
- Layer your defenses - IP intelligence is one component; combine with device fingerprinting, behavioral analysis, and ML
Ready to implement IP-based fraud prevention? Get started with IPBot - no API key required.