DEV Community

ApogeoAPI
ApogeoAPI

Posted on • Originally published at apogeoapi.com

IP Geolocation for Fraud Detection — A Developer's Guide

IP geolocation is one of the most accessible fraud signals available. It won't stop sophisticated attackers, but it catches a significant amount of low-effort fraud with very little implementation overhead.

How IP Geolocation Helps Detect Fraud

  • Impossible travel: A user logs in from Germany, then from Brazil 10 minutes later.
  • Country mismatch: Billing address is in the US, but the IP is in a different region.
  • High-risk region matching: Signups from regions associated with specific fraud patterns.
  • VPN/proxy detection: Hiding location is sometimes a fraud signal (requires specialized APIs).

Signal 1: Impossible Travel

function haversineDistance(lat1: number, lon1: number, lat2: number, lon2: number): number {
const R = 6371; // Earth radius in km
const dLat = (lat2 - lat1) * Math.PI / 180;
const dLon = (lon2 - lon1) * Math.PI / 180;
const a = Math.sin(dLat/2) ** 2 +
Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) * Math.sin(dLon/2) ** 2;
return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
}
async function checkImpossibleTravel(userId: string, currentIp: string) {
const current = await geolocate(currentIp);
const lastLogin = await db.getLastLogin(userId);
if (!lastLogin) return false;
const distanceKm = haversineDistance(
lastLogin.latitude, lastLogin.longitude,
current.latitude, current.longitude
);
const hoursSince = (Date.now() - lastLogin.timestamp) / 3600000;
const maxPossibleSpeed = 900; // km/h (commercial flight speed)
return distanceKm / hoursSince > maxPossibleSpeed;
}
Enter fullscreen mode Exit fullscreen mode

Signal 2: Country Mismatch

async function checkCountryMismatch(billingCountry: string, ip: string): Promise {
const geo = await geolocate(ip);
return geo.country_code !== billingCountry;
}
Enter fullscreen mode Exit fullscreen mode

Signal 3: High-Risk Regions

const HIGH_RISK_COUNTRIES = new Set(['XX', 'YY']); // Your own list based on data
async function isHighRiskRegion(ip: string): Promise {
const geo = await geolocate(ip);
return HIGH_RISK_COUNTRIES.has(geo.country_code);
}
Enter fullscreen mode Exit fullscreen mode

Signal 4: Anonymous IP / VPN

Standard IP geolocation APIs (including ApogeoAPI) don't detect VPNs or proxies — that requires a dedicated proxy detection database. Services like IPQualityScore or Fraudlabs Pro specialize in this. Use them as an additional layer, not a replacement.

Putting It Together: Risk Score

interface RiskResult {
score: number; // 0–100
reasons: string[];
}
async function calculateRiskScore(
userId: string,
ip: string,
billingCountry: string
): Promise {
const reasons: string[] = [];
let score = 0;
const [impossible, mismatch, highRisk] = await Promise.all([
checkImpossibleTravel(userId, ip),
checkCountryMismatch(billingCountry, ip),
isHighRiskRegion(ip),
]);
if (impossible) { score += 40; reasons.push('Impossible travel detected'); }
if (mismatch)   { score += 30; reasons.push('IP country does not match billing country'); }
if (highRisk)   { score += 20; reasons.push('IP originates from high-risk region'); }
return { score, reasons };
}
Enter fullscreen mode Exit fullscreen mode

Important Caveats

  • Never block solely on IP. VPNs, corporate proxies, and shared IPs create false positives that block legitimate users.
  • Use as one signal among many. Combine with device fingerprinting, behavioral analysis, and payment signals.
  • Consider privacy regulations. Storing IP-derived location data may fall under GDPR or CCPA. Consult your legal team.
  • Communicate clearly. If you flag a user, give them a way to verify identity rather than a silent block.

Originally published at https://apogeoapi.com/blog/ip-geolocation-fraud-detection. Try ApogeoAPI free at apogeoapi.com.

Top comments (0)