Guide
Reduce IP false positives without loosening your rules.
The surest way to make an IP fraud check worse is to react to every false positive by relaxing a rule. GeoQ takes the opposite approach: it recognises the benign network kinds that cause false positives — relay, satellite, public resolver — and caps their risk score at 20, so your strict rules stay strict for everyone else.
The three false-positive reducers
| Signal | Benign network kind | The false positive it fixes |
|---|---|---|
is_relay | iCloud Private Relay | Apple users on hosting-style relay exits scored as datacenter. |
connection_type === "satellite" | Starlink / satellite | Satellite users appear to travel impossibly fast between ground stations. |
is_public_resolver | Public DNS resolvers | 8.8.8.8, 1.1.1.1 and peers look like datacenters and score as fraud. |
When any reducer is true, the score is capped at 20 and
benign_network_kind is added to reasons[]. It's a
cap applied after the weighted sum — not a negative weight, not a block
override. See the risk-score methodology.
One call, then a small branch
You don't need to special-case Apple, SpaceX or Google's resolver IPs yourself. One /v1/check call returns the signals and a score that already accounts for the benign cases:
const res = await fetch(`https://api.geoq.io/v1/check?ip=${ip}`, { headers: { "x-api-key": process.env.GEOQ_KEY }, }); const { signals, risk } = await res.json(); // Keep your strict datacenter / velocity rules. Carve out benign kinds: const benign = signals.is_relay || signals.connection_type === "satellite" || signals.is_public_resolver; if (benign) { // risk.score is already capped at 20; reasons[] includes benign_network_kind. allowWithLightFriction(); } else if (risk.level === "high") { blockOrReview(); }
Precision signals do the other half
Reducers stop you punishing good users. The routing and allocation signals help you catch the bad ones with more confidence, so you're not leaning on blunt rules that over-fire:
Route-origin validation and bogon detection from public BGP — RPKI-invalid +20, bogon +30.
How long ago the RIR delegated a range. Freshly-allocated space is worth a second look.
Every score lists the exact signals that fired. Audit it, tune your own thresholds.
A note on what this is and isn't
An IP signal is one input, never a verdict. The reducers make GeoQ less likely to flag a real user, but they don't authenticate anyone — a relay or satellite exit just tells you the network kind is benign. Keep step-up friction (a captcha, an email check) for ambiguous cases, and don't make GeoQ the sole basis of an automated decision about a person. See the acceptable use policy.
FAQ
Reducing IP false positives — FAQ
Why do IP fraud rules produce false positives?
How does GeoQ reduce false positives?
is_relay, connection_type === "satellite" and is_public_resolver. When any is true, the risk score is capped at 20 and benign_network_kind is added to reasons[]. You keep your strict rules and carve out the benign cases explicitly, instead of loosening rules for everyone. Do I lose information when the score is capped?
connection_type still reads datacenter and that reason is still listed. You can always inspect every signal and decide your own policy. What about RPKI and allocation context — do those help?
rpki and is_bogon add weight to genuinely suspicious routing; allocation_age_days tells you whether a range was delegated recently. Together they let you raise risk on the right IPs while the reducers lower it on the benign ones — fewer false positives and fewer false negatives. Is the score still auditable after the cap?
reasons[] lists every signal that fired plus benign_network_kind when the cap applies, and the weights are published. You can reproduce any score by hand — see the risk-score methodology. Is this available on the free tier?
Get a free key — 5,000 lookups/day, no card.
Every signal and the same risk score as every paid plan. Upgrade only when you outgrow it.