Catch Bulk Signups Without Banning a Building: IP Risk at Registration
Bulk registration rarely looks like a datacenter flood anymore. The signup IP often sits on ordinary broadband (usage_type: DYN), sometimes even on a cloud phone that looks like a handset. A network-type blocklist misses
Bulk registration rarely looks like a datacenter flood anymore. The signup IP often sits on ordinary broadband (usage_type: DYN), sometimes even on a cloud phone that looks like a handset. A network-type blocklist misses most of it; a hard ban on every "risky" address can take out a whole building on CGNAT. What you need at registration is a fresh, typed signal you can step up on — not a permanent label.
IP99 by ThreatHunter exposes that as a free GET /v1/ip/{ip} call: risk signals such as proxy, dialup_pool, and cloud_phone, plus evidence_state and a 0–100 score that measures freshness and strength of verifiable evidence, not "probability this person is an attacker."
What bulk signup actually buys
Fraud teams and promo farms still need three cheap inputs: phone numbers (or SMS inboxes), device surfaces, and egress IPs that rotate. The IP side has shifted off pure hosting space:
| Pattern | What you tend to see | Why a DYN allowlist fails |
|---|---|---|
| Rotating residential / dial-up pool |
proxy + often dialup_pool on residential ASN |
Address is real broadband |
| Cloud phone farm |
cloud_phone (and often hosting attributes) |
Looks like mobile UX, not a VPS |
| Hijacked home exit | hijacked_proxy |
Line owner is often a victim |
| Classic VPS / VPN |
proxy / vpn + usage_type: IDC
|
Network-type rules catch only this row |
If your registration gate only asks "is this IDC?", you are fighting last decade's inventory.
A practical registration gate (not a ban hammer)
Official triage order on IP99 is: evidence_state → score/level → which signals → (if you ever ban) share scope. On the free API you do not get Pulse share_tier; you still have usage_type and evidence_state, which is enough for a signup funnel:
-
evidence_state: none— no current evidence. Do not stamp the account "clean." Treat as unknown; keep normal KYC / rate limits. -
stale— seen before, evidence aged out, score 0. Weak feature only; do not hard-block alone. -
active+ low (1–29) — log and watch; optional soft friction (email confirm, slower signup). -
active+ medium (30–69) — step up: captcha, device attestation, SMS or app challenge, throttle new accounts from that ASN or/24for an hour — still prefer verify over ban. -
active+ high (70–100) — decide by business cost. Prefer challenge + delay over IP ban whenusage_typeisMOB/DYN(shared egress). Ifhijacked_proxyis present, do not treat the subscriber as the fraud actor; challenge the session, do not nuke the household forever.
Signals matter for the kind of step-up: cloud_phone and dialup_pool are stronger registration smells than bare hosting (network attribute — Google DNS carries hosting with score 0). crawler is benign and does not score.
Live curls (2026-10-24, no API key)
Anonymous quota is 500 lookups/day per client IP (60/min). Sign in for 5,000/day; overseas self-serve Pro is volume-only ($29 / $99) — same evidence fields. Mainland China: free + enterprise contract (no self-serve card plans on the Chinese terms page).
# Your own egress before you test signup from that network
curl https://ip99.com/v1/ip
# Public demo often used in IP99 docs (residential dial-up / proxy supply — masks in tickets)
curl https://ip99.com/v1/ip/27.22.66.200
# Datacenter control: hosting attribute, score 0
curl https://ip99.com/v1/ip/8.8.8.8
# Quota remaining (same anon bucket as lookups)
curl https://ip99.com/v1/me
Sample: residential dial-up / proxy supply (mask last octet in tickets)
Captured ~2026-10-24 13:08 UTC. Full address works in the curl above; display masks the last octet.
{
"ip": "27.22.66.x",
"computed_at": "2026-10-24T13:08:34Z",
"evidence_state": "active",
"geo": {
"country": "CN",
"region": "Hubei",
"city": "Xiangyang",
"district": "Nanzhang",
"admin_code": "420624",
"tz": "Asia/Shanghai"
},
"network": { "asn": 4134, "usage_type": "DYN" },
"risk": {
"score": 26,
"level": "low",
"signals": ["dialup_pool", "hijacked_proxy", "proxy"]
}
}
Read left to right: still DYN home broadband; signals say dial-up pool + proxy presence, and hijacked_proxy warns the line owner may be a victim; evidence_state: active with a low score means act with friction, not a permanent ban. Scores move as evidence ages — the same IP an hour later can look different, which is intentional.
Sample: hosting control
{
"ip": "8.8.8.8",
"evidence_state": "none",
"network": { "asn": 15169, "usage_type": "IDC" },
"risk": { "score": 0, "level": "none", "signals": ["hosting"] }
}
hosting alone is not a registration reject reason. Pair network type with risk signals and freshness.
Wire it into signup (minimal shape)
Pseudo-flow at POST /register after you have the client IP (trusted proxy headers only if you terminate TLS yourself):
lookup = GET https://ip99.com/v1/ip/{client_ip}
if lookup.evidence_state == "active":
if "hijacked_proxy" in signals: challenge_session(); never permanent_ban_ip()
elif score >= 70: challenge_or_delay(); optional short IP throttle
elif score >= 30: captcha_or_otp(); raise_new_account_rate_limit()
else: log_for_review()
else:
# none / stale — unknown or expired evidence
apply_baseline_limits_only()
CORS is open on /v1/ip, so a browser can call it, but do the gate on your server — clients lie about their IP. Import the machine-readable contract from https://ip99.com/openapi.json into Postman or your codegen if you prefer typed clients. For agents, the same lookup is also available as MCP tool ip_risk_lookup at https://ip99.com/mcp.
What this article is not promising
- IP risk is not a substitute for phone / device / behavior checks; it is one cheap early signal.
- Score is not attacker probability;
0/evidence_state: noneis not "safe." - Free
/v1/ipanswers now. Historical "what did we know at chargeback time?" needs Pulse?at=(professional contract) — do not invent that on the free tier. - Do not advertise Tor detection; IP99's public signal set does not center on that.
- Mask real residential or mobile customer IPs in tickets and blog posts (last octet is enough).
Try it
Paste a signup egress into ip99.com or hit the free curl above. If you are building registration or promo abuse controls, start with evidence_state + typed signals, and keep hard IP bans rare — especially on shared residential and mobile egress.
Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.