IP GeolocationSecurityTutorial

IP Geolocation for Fraud Detection — A Developer's Guide

ApogeoAPI6 min read

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;
}

Signal 2: Country Mismatch

async function checkCountryMismatch(billingCountry: string, ip: string): Promise<boolean> {
  const geo = await geolocate(ip);
  return geo.country_code !== billingCountry;
}

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<boolean> {
  const geo = await geolocate(ip);
  return HIGH_RISK_COUNTRIES.has(geo.country_code);
}

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<RiskResult> {
  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 };
}

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.

Try ApogeoAPI free

1,000 requests/month forever. 14-day full-access trial. No credit card.

Get your free API key