Detecting financial fraud with Neo4j
Building geolocation-enriched graphs using IP metadata.
Online insurance applications generate a trail of events—submissions, underwriting decisions, payments—each tagged with an IP address. By enriching these events with Fastah IP geolocation and loading them into Neo4j, you can detect fraud patterns that tabular data would hide.
This guide extends Neo4j's fraud detection documentation by adding privacy-safe geographic attributes to application events. When submission and payment originate from distant locations—or cluster with known bad actors—you can trigger a human review.
Graph relationships reveal fraud rings that tabular queries miss. Source: Neo4j.
The fraud pattern
Each insurance application generates events with IP addresses:
applicationSubmitted— applicant submits from IP address ApaymentMade— payment arrives from IP address B
If the distance between A and B exceeds a threshold (e.g., 500 km), flag for review.
Event schema:
| Field | Description |
|---|---|
customerApplicationId | Unique application identifier (e.g., INSURE-ME-1708) |
eventtype | Either applicationSubmitted or paymentMade |
timestamp | Unix epoch milliseconds |
ip | Public IPv4 address |
locationData.lat | Latitude from Fastah API |
locationData.lng | Longitude from Fastah API |
Load geolocated events into Neo4j
This Cypher query ingests Fastah API responses, creates IPAddress nodes with spatial points, and links them to Application nodes:
// Ingest events and create graph structure
UNWIND [
{
ip: "15.220.176.1",
timestamp: 1705124935277,
eventtype: "paymentMade",
customerApplicationId: "INSURE-ME-1708",
locationData: { lat: 39.04, lng: -77.49 }
},
{
ip: "13.107.54.1",
timestamp: 1706199630411,
eventtype: "applicationSubmitted",
customerApplicationId: "INSURE-ME-1708",
locationData: { lat: 47.61, lng: -122.33 }
}
] AS event
// Create IP node with Neo4j spatial point (SRID 4326)
MERGE (ip:IPAddress { ip: event.ip })
SET ip.location = point({
latitude: event.locationData.lat,
longitude: event.locationData.lng
})
// Create application node
MERGE (app:Application { id: event.customerApplicationId })
// Link based on event type
FOREACH (_ IN CASE WHEN event.eventtype = "applicationSubmitted" THEN [1] ELSE [] END |
MERGE (app)-[:SUBMITTED_FROM { timestamp: event.timestamp }]->(ip)
)
FOREACH (_ IN CASE WHEN event.eventtype = "paymentMade" THEN [1] ELSE [] END |
MERGE (app)-[:PAID_FROM { timestamp: event.timestamp }]->(ip)
)In production, stream events from your application or ETL pipeline rather than hard-coding them.
Detect suspicious applications
Find applications where submission and payment IPs sit far apart:
// Find applications with >500 km between submission and payment locations
MATCH (app:Application)-[:SUBMITTED_FROM]->(submitIp:IPAddress),
(app)-[:PAID_FROM]->(payIp:IPAddress)
WITH app,
submitIp,
payIp,
point.distance(submitIp.location, payIp.location) / 1000.0 AS distanceKm
WHERE distanceKm > 500
RETURN app.id AS applicationId,
round(distanceKm) AS distanceKm,
submitIp.ip AS submittedFromIp,
payIp.ip AS paidFromIp
ORDER BY distanceKm DESCThe point.distance() function returns meters, so we divide by 1000 for kilometers.
Visualize with Neo4j Bloom
Use Neo4j Bloom to explore relationships between applications and shared IP addresses:

Multiple applications sharing the same IP address and/or a geographical cluster may trigger a review
Neo4j's graph structure reveals patterns you'd miss in tabular data—such as fraud rings where multiple applications share IP addresses, or account takeovers where submission and payment originate from different continents.
Updated about 24 hours ago
