<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: IP99</title>
    <description>The latest articles on DEV Community by IP99 (@ip99).</description>
    <link>https://dev.to/ip99</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4174889%2Fbd2ba501-747f-48fe-b7f5-e3c0f6d94b3b.webp</url>
      <title>DEV Community: IP99</title>
      <link>https://dev.to/ip99</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ip99"/>
    <language>en</language>
    <item>
      <title>This Week's Proxy Exits: 10.9M New Addresses, Still Almost All Off Datacenter Space</title>
      <dc:creator>IP99</dc:creator>
      <pubDate>Fri, 30 Oct 2026 13:19:06 +0000</pubDate>
      <link>https://dev.to/ip99/this-weeks-proxy-exits-109m-new-addresses-still-almost-all-off-datacenter-space-1h9m</link>
      <guid>https://dev.to/ip99/this-weeks-proxy-exits-109m-new-addresses-still-almost-all-off-datacenter-space-1h9m</guid>
      <description>&lt;p&gt;Datacenter blocklists are still the default proxy defence in a lot of fraud stacks. The addresses showing up in proxy supply this week mostly were not in datacenters.&lt;/p&gt;

&lt;p&gt;Between &lt;strong&gt;23–29 October 2026 (UTC)&lt;/strong&gt;, the &lt;a href="https://ip99.com?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=ip99" rel="noopener noreferrer"&gt;IP99&lt;/a&gt; risk feed captured &lt;strong&gt;10,930,592&lt;/strong&gt; proxy exit addresses it had not seen in the previous 7 days (&lt;strong&gt;−4%&lt;/strong&gt; vs the prior week). Source page: &lt;a href="https://ip99.com/en/proxy-exits?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=ip99" rel="noopener noreferrer"&gt;ip99.com/en/proxy-exits&lt;/a&gt; (generated 30 Oct 2026 UTC).&lt;/p&gt;

&lt;h2&gt;
  
  
  What those addresses sit on
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Usage type&lt;/th&gt;
&lt;th&gt;Addresses&lt;/th&gt;
&lt;th&gt;Share&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Residential broadband&lt;/td&gt;
&lt;td&gt;8,701,207&lt;/td&gt;
&lt;td&gt;79.6%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mobile network&lt;/td&gt;
&lt;td&gt;1,425,387&lt;/td&gt;
&lt;td&gt;13.0%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gateway / NAT egress&lt;/td&gt;
&lt;td&gt;488,101&lt;/td&gt;
&lt;td&gt;4.5%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Datacenter&lt;/td&gt;
&lt;td&gt;240,834&lt;/td&gt;
&lt;td&gt;2.2%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Education network&lt;/td&gt;
&lt;td&gt;47,096&lt;/td&gt;
&lt;td&gt;0.4%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unclassified&lt;/td&gt;
&lt;td&gt;27,967&lt;/td&gt;
&lt;td&gt;0.3%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;About &lt;strong&gt;62%&lt;/strong&gt; were rated high-risk at the moment of capture. The rest still carried a proxy-class signal at a lower risk level.&lt;/p&gt;

&lt;p&gt;IP99 counts an address as "captured" when it enters the feed with a proxy-class signal (&lt;code&gt;proxy&lt;/code&gt;, &lt;code&gt;VPN&lt;/code&gt;, dial-up pool, cloud phone, or hijacked shared proxy) and was &lt;strong&gt;not&lt;/strong&gt; in the live 7-day risk mirror. Addresses that go quiet and return can be counted again. The page describes what &lt;strong&gt;68&lt;/strong&gt; upstream collection sources delivered — not the total number of proxies in the world. No address lists are published.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the 2% matters
&lt;/h2&gt;

&lt;p&gt;If your defence is mainly an ASN or hosting blocklist, you are mostly looking at that &lt;strong&gt;2.2%&lt;/strong&gt; datacenter slice. Residential and mobile space is where home-broadband proxies and dial-up pools live. An address that keeps turning up in proxy supply is a proxy exit whatever its ISP label says.&lt;/p&gt;

&lt;p&gt;That is the same reason a residential allowlist fails the other way: usage type answers "what kind of network is this," not "is this address currently sold as an exit." Rotating residential proxies and dial-up pools are designed to sit in DYN / MOB space. Detecting them needs supply-side observation with an age attached, not a permanent network-type label.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which networks show up most
&lt;/h2&gt;

&lt;p&gt;Top ASNs in this week's capture window (by distinct address count):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;ASN&lt;/th&gt;
&lt;th&gt;Addresses&lt;/th&gt;
&lt;th&gt;Share&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;AS4134&lt;/td&gt;
&lt;td&gt;1,018,153&lt;/td&gt;
&lt;td&gt;9.3%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AS4837&lt;/td&gt;
&lt;td&gt;748,461&lt;/td&gt;
&lt;td&gt;6.8%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AS9808&lt;/td&gt;
&lt;td&gt;370,984&lt;/td&gt;
&lt;td&gt;3.4%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AS6057&lt;/td&gt;
&lt;td&gt;261,568&lt;/td&gt;
&lt;td&gt;2.4%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AS36947&lt;/td&gt;
&lt;td&gt;209,880&lt;/td&gt;
&lt;td&gt;1.9%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Large access networks appearing here are a clue about where proxy supply is being sold from — not a claim that every customer on those networks is a proxy. Ban a whole ASN and you take out legitimate home and mobile users. Prefer per-address evidence at decision time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Day-to-day shape
&lt;/h2&gt;

&lt;p&gt;Captured addresses (new to the prior 7-day mirror that day), with how many were high-risk at capture:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Day (UTC)&lt;/th&gt;
&lt;th&gt;Captured&lt;/th&gt;
&lt;th&gt;High-risk at capture&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2026-10-23&lt;/td&gt;
&lt;td&gt;1,785,796&lt;/td&gt;
&lt;td&gt;1,147,796&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-10-24&lt;/td&gt;
&lt;td&gt;1,658,044&lt;/td&gt;
&lt;td&gt;1,025,424&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-10-25&lt;/td&gt;
&lt;td&gt;1,570,214&lt;/td&gt;
&lt;td&gt;957,858&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-10-26&lt;/td&gt;
&lt;td&gt;1,506,506&lt;/td&gt;
&lt;td&gt;921,527&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-10-27&lt;/td&gt;
&lt;td&gt;1,496,376&lt;/td&gt;
&lt;td&gt;927,608&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-10-28&lt;/td&gt;
&lt;td&gt;1,482,695&lt;/td&gt;
&lt;td&gt;919,210&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-10-29&lt;/td&gt;
&lt;td&gt;1,430,961&lt;/td&gt;
&lt;td&gt;917,972&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Volume eased week-over-week (&lt;strong&gt;−4%&lt;/strong&gt; after last week's rebound to 11.5M), while upstream sources rose from 54 to &lt;strong&gt;68&lt;/strong&gt;. More sources did not mean a bigger headline total — the soft dip is about what those collectors delivered into the 7-day mirror, not a claim that proxy supply worldwide shrank. The mix held: residential near &lt;strong&gt;80%&lt;/strong&gt;, datacenter still about &lt;strong&gt;2%&lt;/strong&gt;. Daily capture drifted gently lower through the window, staying in a band roughly &lt;strong&gt;1.4–1.8M&lt;/strong&gt; addresses — no single spike day that would make a static list look "caught up."&lt;/p&gt;

&lt;h2&gt;
  
  
  How to use this without over-blocking
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Branch on evidence, not network type.&lt;/strong&gt; Residential is where your customers live; do not hard-block on DYN / MOB alone.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step up, don't stop.&lt;/strong&gt; A fresh proxy-class signal on signup, a coupon claim, or a login from a new device justifies a second factor or a delay — not an immediate ban.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read the age.&lt;/strong&gt; &lt;code&gt;evidence_state: "stale"&lt;/code&gt; means the address was in supply before and is not now; treat it as a weak prior. Re-query at decision time rather than caching a verdict for days.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep the timestamp.&lt;/strong&gt; Store &lt;code&gt;computed_at&lt;/code&gt; with the decision so a disputed block can be traced back to when the observation was made.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Check one yourself
&lt;/h2&gt;

&lt;p&gt;Free, no signup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://ip99.com/v1/ip/8.8.8.8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look at &lt;code&gt;evidence_state&lt;/code&gt; (&lt;code&gt;active&lt;/code&gt; / &lt;code&gt;stale&lt;/code&gt; / &lt;code&gt;none&lt;/code&gt;), &lt;code&gt;risk.score&lt;/code&gt;, and the proxy-class signals. A score of 0 is not "safe" — it means IP99 holds no current verifiable evidence against that address. The score is the freshness and strength of that evidence, not a probability that the address "is an attacker."&lt;/p&gt;

&lt;p&gt;The same lookup is available to agents as an MCP tool at &lt;code&gt;https://ip99.com/mcp&lt;/code&gt; (&lt;code&gt;ip_risk_lookup&lt;/code&gt;). Field reference and routing notes live on the &lt;a href="https://ip99.com/api?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=ip99" rel="noopener noreferrer"&gt;API docs&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>security</category>
      <category>devops</category>
      <category>api</category>
      <category>fraudprevention</category>
    </item>
    <item>
      <title>Don't Rate-Limit Googlebot Like a Proxy Farm: Route Crawlers by IP Signal</title>
      <dc:creator>IP99</dc:creator>
      <pubDate>Thu, 29 Oct 2026 13:12:33 +0000</pubDate>
      <link>https://dev.to/ip99/dont-rate-limit-googlebot-like-a-proxy-farm-route-crawlers-by-ip-signal-3ifo</link>
      <guid>https://dev.to/ip99/dont-rate-limit-googlebot-like-a-proxy-farm-route-crawlers-by-ip-signal-3ifo</guid>
      <description>&lt;p&gt;Your content and API edge sees two kinds of automation that look similar in the logs: &lt;strong&gt;search-engine crawlers&lt;/strong&gt; you usually want indexed, and &lt;strong&gt;scrapers&lt;/strong&gt; that rotate through proxy exits to drain pages, prices, or inventory. A single requests-per-IP throttle treats them the same. That is how you starve SEO while the farm simply dials a fresh residential address.&lt;/p&gt;

&lt;p&gt;IP99 by ThreatHunter puts a typed signal on the free &lt;code&gt;GET /v1/ip/{ip}&lt;/code&gt; response for the first case: &lt;code&gt;crawler&lt;/code&gt; means search-engine crawler egress. Official docs treat it as &lt;strong&gt;benign&lt;/strong&gt; — it &lt;strong&gt;does not participate in scoring&lt;/strong&gt; and never becomes a scored piece of evidence. Proxy farms show up under different slugs (&lt;code&gt;proxy&lt;/code&gt;, &lt;code&gt;vpn&lt;/code&gt;, &lt;code&gt;dialup_pool&lt;/code&gt;, &lt;code&gt;cloud_phone&lt;/code&gt;, …) with a score that measures &lt;strong&gt;freshness and strength of verifiable evidence&lt;/strong&gt;, not "probability this visitor is an attacker."&lt;/p&gt;

&lt;h2&gt;
  
  
  What you are actually sorting
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Traffic&lt;/th&gt;
&lt;th&gt;What you want&lt;/th&gt;
&lt;th&gt;Naïve edge rule&lt;/th&gt;
&lt;th&gt;Better branch&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Googlebot / Bingbot / similar&lt;/td&gt;
&lt;td&gt;Allow (or polite crawl budget)&lt;/td&gt;
&lt;td&gt;Same QPS bucket as scrapers&lt;/td&gt;
&lt;td&gt;Prefer allow / dedicated budget when &lt;code&gt;crawler&lt;/code&gt; fires&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Proxy-rotating scraper&lt;/td&gt;
&lt;td&gt;Cap, challenge, or block&lt;/td&gt;
&lt;td&gt;Rate limit one IP → farm switches&lt;/td&gt;
&lt;td&gt;Branch on proxy-class signals + &lt;code&gt;evidence_state&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Corporate / CDN egress&lt;/td&gt;
&lt;td&gt;Usually allow&lt;/td&gt;
&lt;td&gt;Ban all &lt;code&gt;hosting&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;hosting&lt;/code&gt; / &lt;code&gt;mobile&lt;/code&gt; describe the &lt;strong&gt;network&lt;/strong&gt;, not risk&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Real user behind CGNAT&lt;/td&gt;
&lt;td&gt;Allow&lt;/td&gt;
&lt;td&gt;Ban shared IP → whole building&lt;/td&gt;
&lt;td&gt;Prefer step-up over hard-ban&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Official signal table (ten slugs): eight risk labels (&lt;code&gt;proxy&lt;/code&gt;, &lt;code&gt;vpn&lt;/code&gt;, &lt;code&gt;dialup_pool&lt;/code&gt;, &lt;code&gt;hijacked_proxy&lt;/code&gt;, &lt;code&gt;osint&lt;/code&gt;, &lt;code&gt;cloud_service&lt;/code&gt;, &lt;code&gt;cloud_phone&lt;/code&gt;, plus &lt;code&gt;crawler&lt;/code&gt; listed among them but &lt;strong&gt;benign&lt;/strong&gt;), and two network attributes (&lt;code&gt;hosting&lt;/code&gt;, &lt;code&gt;mobile&lt;/code&gt;) that are &lt;strong&gt;not&lt;/strong&gt; risk verdicts on their own. Google Public DNS (&lt;code&gt;8.8.8.8&lt;/code&gt;) carries &lt;code&gt;hosting&lt;/code&gt; with score 0 — a reminder that "datacenter space" ≠ "scraper."&lt;/p&gt;

&lt;p&gt;From IP99's own content/API anti-crawl note: scrapers rotate exits across proxy pools; routing by &lt;strong&gt;risk type&lt;/strong&gt; (and share scope when you have it) mis-fires less than pure frequency limits.&lt;/p&gt;

&lt;h2&gt;
  
  
  Triage order at the edge
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;evidence_state&lt;/code&gt;&lt;/strong&gt; — can this conclusion be trusted &lt;em&gt;now&lt;/em&gt;? &lt;code&gt;active&lt;/code&gt; = fresh evidence still scoring; &lt;code&gt;stale&lt;/code&gt; = seen before, aged out, score 0 (weak feature only); &lt;code&gt;none&lt;/code&gt; = never seen (coverage gap), &lt;strong&gt;not "safe."&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;risk.signals&lt;/code&gt;&lt;/strong&gt; — &lt;em&gt;why&lt;/em&gt; it matches. If &lt;code&gt;crawler&lt;/code&gt; is present, treat as known search egress: do &lt;strong&gt;not&lt;/strong&gt; fold it into your "proxy farm" ban path. If proxy-class signals fire without &lt;code&gt;crawler&lt;/code&gt;, that is the scrape / abuse path.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;risk.score&lt;/code&gt; / &lt;code&gt;risk.level&lt;/code&gt;&lt;/strong&gt; — only after state. Official disposition guidance (generic, not a contract promise): 0 → do not stamp "clean"; 1–29 → log/observe; 30–69 → verify (CAPTCHA, second factor, rate limit, demote) rather than hard-ban; 70–100 → block when business cost allows.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Share scope&lt;/strong&gt; — Pulse &lt;code&gt;share_tier&lt;/code&gt; (T0–T3) if you have professional history; on free responses use &lt;code&gt;usage_type&lt;/code&gt; (&lt;code&gt;IDC&lt;/code&gt; vs &lt;code&gt;DYN&lt;/code&gt; / &lt;code&gt;MOB&lt;/code&gt; / &lt;code&gt;GTW&lt;/code&gt;) as a rough prior before one ban equals one building.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;degraded: true&lt;/code&gt;&lt;/strong&gt; — discount the answer when sources are partial.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;UA strings alone are not enough: scrapers forge Googlebot. Combine UA with &lt;strong&gt;egress identity&lt;/strong&gt; (published bot prefixes where vendors publish them, plus an IP risk lookup). IP99's &lt;code&gt;crawler&lt;/code&gt; tag is the supply-side observation that the address is search-crawler infrastructure — complementary to vendor prefix lists, not a replacement for verifying the ones you care about most.&lt;/p&gt;

&lt;h2&gt;
  
  
  Live curls (2026-10-29, no API key)
&lt;/h2&gt;

&lt;p&gt;Anonymous quota: &lt;strong&gt;500 lookups/day&lt;/strong&gt; per client IP, 60/min. Sign in for 5,000/day. Overseas self-serve Pro / Pro+ buys &lt;strong&gt;volume&lt;/strong&gt;, not a different score — official line: you pay for volume, not a better answer. Per-item evidence rows, &lt;code&gt;share_tier&lt;/code&gt;, and &lt;code&gt;?at=&lt;/code&gt; history are a &lt;strong&gt;separate professional / contracted&lt;/strong&gt; Pulse capability.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Datacenter control: hosting attribute, score 0 — not a scraper by itself&lt;/span&gt;
curl https://ip99.com/v1/ip/8.8.8.8

&lt;span class="c"&gt;# Docs demo residential address — mask the last octet in tickets/logs&lt;/span&gt;
curl https://ip99.com/v1/ip/27.22.66.200

&lt;span class="c"&gt;# Remaining anon quota&lt;/span&gt;
curl https://ip99.com/v1/me
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Sample A: hosting ≠ crawler ≠ proxy farm
&lt;/h3&gt;

&lt;p&gt;Captured ~2026-10-29 13:08 UTC:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ip"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"8.8.8.8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"computed_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-10-29T13:08:06Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"evidence_state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"none"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"geo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"country"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"US"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"region"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"California"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"city"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Mountain View"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"tz"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"America/Los_Angeles"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"network"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"asn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;15169&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"usage_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"IDC"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"risk"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"level"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"none"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"signals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"hosting"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;usage_type: IDC&lt;/code&gt; plus &lt;code&gt;hosting&lt;/code&gt; describes where the address lives. There is no &lt;code&gt;crawler&lt;/code&gt; and no proxy-class signal. A rule that bans every IDC egress will also ban DNS resolvers, CI runners, and your monitoring probes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sample B: residential proxy churn (the scraper-shaped path)
&lt;/h3&gt;

&lt;p&gt;Same window; last octet shown because this is IP99's documented demo address — still mask real customer IPs in your tickets:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ip"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"27.22.66.200"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"computed_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-10-29T13:08:32Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"evidence_state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"active"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"geo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"country"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CN"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"region"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Hubei"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"city"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Xiangyang"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"district"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Yicheng"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"tz"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Asia/Shanghai"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"network"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"asn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4134&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"usage_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DYN"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"risk"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"level"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"low"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"signals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"dialup_pool"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hijacked_proxy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"proxy"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is residential broadband with dial-up-pool / proxy evidence — the rotating-exit pattern scrapers buy. Disposition differs from Googlebot: prefer verify / tighter crawl budget / delay expensive endpoints. &lt;code&gt;hijacked_proxy&lt;/code&gt; still argues for careful appeals (the line owner may be a victim), not "delete the building forever."&lt;/p&gt;

&lt;p&gt;When your edge &lt;em&gt;does&lt;/em&gt; see &lt;code&gt;crawler&lt;/code&gt; (often on known search infrastructure), route it to the allow / polite-crawl lane even if the same IP sits in hosting space. Do not let a global "block hosting" or "block anything with a risk slug" rule catch it.&lt;/p&gt;

&lt;h2&gt;
  
  
  A minimal edge hook
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Server-observed client IP only — never trust a body field&lt;/span&gt;
&lt;span class="nv"&gt;IP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CLIENT_IP&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;RESP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-sS&lt;/span&gt; &lt;span class="s2"&gt;"https://ip99.com/v1/ip/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;IP&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Pseudocode:&lt;/span&gt;
&lt;span class="c"&gt;# state = RESP.evidence_state&lt;/span&gt;
&lt;span class="c"&gt;# signals = RESP.risk.signals or []&lt;/span&gt;
&lt;span class="c"&gt;# if "crawler" in signals:&lt;/span&gt;
&lt;span class="c"&gt;#     allow_or_polite_budget()&lt;/span&gt;
&lt;span class="c"&gt;# elif state == "active" and any(s in signals for s in&lt;/span&gt;
&lt;span class="c"&gt;#         ("proxy","vpn","dialup_pool","cloud_phone","cloud_service")):&lt;/span&gt;
&lt;span class="c"&gt;#     if RESP.risk.score &amp;gt;= 70: challenge_or_block()&lt;/span&gt;
&lt;span class="c"&gt;#     elif RESP.risk.score &amp;gt;= 30: captcha_or_slow()&lt;/span&gt;
&lt;span class="c"&gt;#     else: observe()&lt;/span&gt;
&lt;span class="c"&gt;# else:&lt;/span&gt;
&lt;span class="c"&gt;#     normal_rate_limits()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Re-query at decision time. Do not cache a verdict across days: scores decay with evidence age, and &lt;code&gt;evidence_state&lt;/code&gt; is how you tell "seen before, expired" from "never seen." Store &lt;code&gt;computed_at&lt;/code&gt; with the decision if you may need to defend it later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Context from this week's proxy exits
&lt;/h2&gt;

&lt;p&gt;Between &lt;strong&gt;22–28 October 2026 (UTC)&lt;/strong&gt;, the IP99 risk feed captured &lt;strong&gt;11,196,585&lt;/strong&gt; proxy exit addresses it had not seen in the previous 7 days — about &lt;strong&gt;80%&lt;/strong&gt; residential broadband, &lt;strong&gt;datacenter only ~2.3%&lt;/strong&gt;, from &lt;strong&gt;68&lt;/strong&gt; upstream sources (&lt;a href="https://ip99.com/en/proxy-exits" rel="noopener noreferrer"&gt;proxy-exits&lt;/a&gt;). Most of the scrape supply you are fighting is &lt;strong&gt;not&lt;/strong&gt; sitting in the ASN blocklist you already maintain. That is why signal-aware routing beats "ban hosting + rate-limit everyone else."&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;Free lookup (no key): &lt;a href="https://ip99.com" rel="noopener noreferrer"&gt;ip99.com&lt;/a&gt; or &lt;code&gt;curl https://ip99.com/v1/ip/8.8.8.8&lt;/code&gt;. Field reference: &lt;a href="https://ip99.com/api" rel="noopener noreferrer"&gt;API docs&lt;/a&gt;. Agents can call the same verdict as MCP tool &lt;code&gt;ip_risk_lookup&lt;/code&gt; at &lt;code&gt;https://ip99.com/mcp&lt;/code&gt;.&lt;/p&gt;

</description>
      <category>security</category>
      <category>api</category>
      <category>devops</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Cloud Phones Look Like Real Mobiles: Catch Farms at the IP Layer</title>
      <dc:creator>IP99</dc:creator>
      <pubDate>Wed, 28 Oct 2026 13:15:43 +0000</pubDate>
      <link>https://dev.to/ip99/cloud-phones-look-like-real-mobiles-catch-farms-at-the-ip-layer-5ccm</link>
      <guid>https://dev.to/ip99/cloud-phones-look-like-real-mobiles-catch-farms-at-the-ip-layer-5ccm</guid>
      <description>&lt;p&gt;Your signup funnel sees a "phone": WebView UA, touch events, maybe a real device fingerprint. The account farm on the other side is a rack of &lt;strong&gt;cloud phones&lt;/strong&gt; — real or virtual handsets hosted in a datacenter, remoted into by operators who spin up hundreds of profiles a day.&lt;/p&gt;

&lt;p&gt;Device-side checks often say "mobile." Network-type blocklists often say nothing useful: the egress sits in hosting space, or it sits behind whatever the vendor leased. What you need at registration, claim, or payout is an &lt;strong&gt;IP-layer signal that names the pattern&lt;/strong&gt;, not a guess about the handset.&lt;/p&gt;

&lt;p&gt;IP99 by ThreatHunter exposes that as &lt;code&gt;cloud_phone&lt;/code&gt; on the free &lt;code&gt;GET /v1/ip/{ip}&lt;/code&gt; response — one of ten typed signals — alongside &lt;code&gt;evidence_state&lt;/code&gt; and a 0–100 score that measures &lt;strong&gt;freshness and strength of verifiable evidence&lt;/strong&gt;, not "probability this person is an attacker."&lt;/p&gt;

&lt;h2&gt;
  
  
  What a cloud phone is (and is not)
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Thing&lt;/th&gt;
&lt;th&gt;What it is&lt;/th&gt;
&lt;th&gt;What a naive rule sees&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Real cellular user&lt;/td&gt;
&lt;td&gt;Handset on a mobile ASN (&lt;code&gt;usage_type: MOB&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Mobile UX + mobile network&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cloud phone farm&lt;/td&gt;
&lt;td&gt;Handset (or emulator) &lt;strong&gt;in a rack&lt;/strong&gt;, remoted for bulk signup / farming&lt;/td&gt;
&lt;td&gt;Mobile UX; network often hosting or unrelated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Residential proxy&lt;/td&gt;
&lt;td&gt;Home broadband resold as an exit&lt;/td&gt;
&lt;td&gt;Residential ASN; no phone at all&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dial-up pool&lt;/td&gt;
&lt;td&gt;ISP lines that redial for a new address&lt;/td&gt;
&lt;td&gt;Residential space; churning egress&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Official IP99 signal table: &lt;code&gt;cloud_phone&lt;/code&gt; means a cloud phone (a phone device in a datacenter). It is a &lt;strong&gt;risk&lt;/strong&gt; signal. Contrast &lt;code&gt;mobile&lt;/code&gt; and &lt;code&gt;hosting&lt;/code&gt;: those describe the &lt;strong&gt;network&lt;/strong&gt;, not risk evidence — Google Public DNS carries &lt;code&gt;hosting&lt;/code&gt; with score 0. Do not ban on &lt;code&gt;hosting&lt;/code&gt; alone. Do not treat every &lt;code&gt;MOB&lt;/code&gt; address as a farm.&lt;/p&gt;

&lt;p&gt;Also leave &lt;code&gt;crawler&lt;/code&gt; alone: search-engine exits are &lt;strong&gt;benign and do not participate in scoring&lt;/strong&gt;. Cloud-phone farms are a different problem from Googlebot.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why device signals are not enough
&lt;/h2&gt;

&lt;p&gt;Farms buy the stack you already trust: real Android images, real WebViews, residential-looking fingerprints sold as a service. Your client SDK reports "phone." Your fraud model sees mobile-shaped behavior. The cheap tell that survives that arms race is often &lt;strong&gt;where the TCP session comes from&lt;/strong&gt; and whether that egress has been observed as cloud-phone infrastructure.&lt;/p&gt;

&lt;p&gt;That observation expires. An address used by a farm yesterday may be quiet today. IP99's product stance is a &lt;strong&gt;judgement that expires&lt;/strong&gt;: scores decay with evidence age; &lt;code&gt;evidence_state&lt;/code&gt; tells you whether anything fresh is still on the board.&lt;/p&gt;

&lt;h2&gt;
  
  
  Triage order (same as every other IP99 gate)
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;evidence_state&lt;/code&gt;&lt;/strong&gt; — can this conclusion be trusted &lt;em&gt;now&lt;/em&gt;? &lt;code&gt;active&lt;/code&gt; = fresh evidence scoring; &lt;code&gt;stale&lt;/code&gt; = seen before, aged out, score 0 (weak feature only); &lt;code&gt;none&lt;/code&gt; = never seen (coverage gap), &lt;strong&gt;not "safe."&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;risk.score&lt;/code&gt; / &lt;code&gt;risk.level&lt;/code&gt;&lt;/strong&gt; — only after you read state. 0 with &lt;code&gt;none&lt;/code&gt; is not a clean bill of health.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;risk.signals&lt;/code&gt;&lt;/strong&gt; — &lt;em&gt;why&lt;/em&gt; it scored. &lt;code&gt;cloud_phone&lt;/code&gt; is a stronger farm smell than bare &lt;code&gt;hosting&lt;/code&gt;. &lt;code&gt;hijacked_proxy&lt;/code&gt; still means the line owner is likely a victim — step up, don't hard-ban the household forever.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Share scope&lt;/strong&gt; — Pulse &lt;code&gt;share_tier&lt;/code&gt; (T0–T3) if you have professional history; on free responses use &lt;code&gt;usage_type&lt;/code&gt; (&lt;code&gt;IDC&lt;/code&gt; vs &lt;code&gt;DYN&lt;/code&gt; / &lt;code&gt;MOB&lt;/code&gt;) as a rough prior before you equate one ban with one building.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;degraded: true&lt;/code&gt;&lt;/strong&gt; — discount the answer when sources are partial.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Official disposition guidance (generic, not a contract promise): score 0 → do not stamp "clean"; 1–29 → log/observe; 30–69 → verify (CAPTCHA, second factor, rate limit, demote) rather than hard-ban; 70–100 → block only when business cost allows, and still respect share scope.&lt;/p&gt;

&lt;h2&gt;
  
  
  Live curls (2026-10-28, no API key)
&lt;/h2&gt;

&lt;p&gt;Anonymous quota: &lt;strong&gt;500 lookups/day&lt;/strong&gt; per client IP, 60/min. Sign in for 5,000/day. Overseas self-serve Pro / Pro+ ($29 / $99) buys &lt;strong&gt;volume&lt;/strong&gt;, not a different score — official line: you pay for volume, not a better answer. Per-item evidence rows, &lt;code&gt;share_tier&lt;/code&gt;, and &lt;code&gt;?at=&lt;/code&gt; history are a &lt;strong&gt;separate professional / contracted&lt;/strong&gt; Pulse capability.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Datacenter control: hosting attribute, score 0 — not a farm by itself&lt;/span&gt;
curl https://ip99.com/v1/ip/8.8.8.8

&lt;span class="c"&gt;# Docs demo residential address — mask the last octet in tickets/logs&lt;/span&gt;
curl https://ip99.com/v1/ip/27.22.66.200

&lt;span class="c"&gt;# Remaining anon quota&lt;/span&gt;
curl https://ip99.com/v1/me
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Sample A: hosting is not cloud_phone
&lt;/h3&gt;

&lt;p&gt;Captured ~2026-10-28 13:10 UTC:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ip"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"8.8.8.8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"computed_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-10-28T13:10:41Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"evidence_state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"none"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"geo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"country"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"US"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"region"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"California"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"city"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Mountain View"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"tz"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"America/Los_Angeles"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"network"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"asn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;15169&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"usage_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"IDC"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"risk"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"level"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"none"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"signals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"hosting"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;usage_type: IDC&lt;/code&gt; plus &lt;code&gt;hosting&lt;/code&gt; describes where the address lives. There is no &lt;code&gt;cloud_phone&lt;/code&gt; here. A rule that bans every IDC egress will also ban DNS resolvers, CI runners, and your own monitoring.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sample B: residential churn ≠ handset farm
&lt;/h3&gt;

&lt;p&gt;Same window; last octet shown because this is IP99's documented demo address — still mask real customer IPs in your tickets:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ip"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"27.22.66.200"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"computed_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-10-28T13:10:44Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"degraded"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"evidence_state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"active"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"geo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"country"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CN"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"region"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Hubei"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"city"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Xiangyang"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"district"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Yicheng"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"tz"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Asia/Shanghai"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"network"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"asn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4134&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"usage_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DYN"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"risk"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"level"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"low"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"signals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"dialup_pool"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hijacked_proxy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"proxy"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is residential broadband with dial-up-pool / proxy evidence — the &lt;strong&gt;second-dial / shared-exit&lt;/strong&gt; problem, not a rack of remoted phones. Disposition differs: &lt;code&gt;hijacked_proxy&lt;/code&gt; argues for step-up and careful appeals, not "delete the building." When your gate &lt;em&gt;does&lt;/em&gt; see &lt;code&gt;cloud_phone&lt;/code&gt; (often with hosting-side attributes), treat it as a farm-shaped smell: challenge identity, tighten device binding, delay payout — still prefer verify-over-ban on medium scores unless your loss model says otherwise.&lt;/p&gt;

&lt;h2&gt;
  
  
  A minimal signup / claim hook
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# At registration or high-value claim — server-observed client IP only&lt;/span&gt;
&lt;span class="nv"&gt;IP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CLIENT_IP&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;   &lt;span class="c"&gt;# from the reverse proxy, never from a JSON body field&lt;/span&gt;
&lt;span class="nv"&gt;RESP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-sS&lt;/span&gt; &lt;span class="s2"&gt;"https://ip99.com/v1/ip/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;IP&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Pseudocode:&lt;/span&gt;
&lt;span class="c"&gt;# state = RESP.evidence_state&lt;/span&gt;
&lt;span class="c"&gt;# signals = RESP.risk.signals or []&lt;/span&gt;
&lt;span class="c"&gt;# if state == "active" and "cloud_phone" in signals:&lt;/span&gt;
&lt;span class="c"&gt;#     step_up(challenge="phone_otp_or_manual_review")&lt;/span&gt;
&lt;span class="c"&gt;# elif state == "active" and RESP.risk.score &amp;gt;= 70:&lt;/span&gt;
&lt;span class="c"&gt;#     decide_with_share_scope(...)&lt;/span&gt;
&lt;span class="c"&gt;# elif state in ("stale", "none") and score == 0:&lt;/span&gt;
&lt;span class="c"&gt;#     do_not_mark_clean(); continue_normal_friction()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Persist &lt;code&gt;computed_at&lt;/code&gt;, &lt;code&gt;evidence_state&lt;/code&gt;, score/level, and signals with the decision so chargeback week can replay what you knew — the free &lt;code&gt;/v1/ip&lt;/code&gt; endpoint answers &lt;strong&gt;now&lt;/strong&gt; only; historical &lt;code&gt;?at=&lt;/code&gt; needs Pulse.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this article is not claiming
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;IP99 does &lt;strong&gt;not&lt;/strong&gt; ship a Tor-specific signal; do not market one.&lt;/li&gt;
&lt;li&gt;Risk score is &lt;strong&gt;not&lt;/strong&gt; attacker probability.&lt;/li&gt;
&lt;li&gt;Free &lt;code&gt;/v1/ip&lt;/code&gt; and professional Pulse share &lt;strong&gt;one score&lt;/strong&gt; for one IP; Pulse adds evidence detail and optional history, not a second verdict.&lt;/li&gt;
&lt;li&gt;IP99 and ThreatHunter datasets are related products under the same company — do not claim they are identical feeds.&lt;/li&gt;
&lt;li&gt;No invented accuracy rates, customers, or funding numbers appear above. Mask real residential or mobile subscriber IPs in anything you publish or paste into tickets.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;Open &lt;a href="https://ip99.com" rel="noopener noreferrer"&gt;ip99.com&lt;/a&gt;, paste an egress, and read &lt;code&gt;risk.signals&lt;/code&gt; before you trust a "mobile" client claim. Or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://ip99.com/v1/ip/8.8.8.8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;API shape, quotas, and field semantics: &lt;a href="https://ip99.com/api" rel="noopener noreferrer"&gt;ip99.com/api&lt;/a&gt;. When &lt;code&gt;cloud_phone&lt;/code&gt; shows up &lt;code&gt;active&lt;/code&gt; on a signup that already looked like a handset, you finally have a name for the farm — and a score that will forget it when the evidence goes stale.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>security</category>
      <category>api</category>
      <category>devops</category>
    </item>
    <item>
      <title>What Did We Know at Chargeback Time? Replaying IP Risk with a Timestamp</title>
      <dc:creator>IP99</dc:creator>
      <pubDate>Tue, 27 Oct 2026 13:16:11 +0000</pubDate>
      <link>https://dev.to/ip99/what-did-we-know-at-chargeback-time-replaying-ip-risk-with-a-timestamp-409b</link>
      <guid>https://dev.to/ip99/what-did-we-know-at-chargeback-time-replaying-ip-risk-with-a-timestamp-409b</guid>
      <description>&lt;p&gt;A chargeback lands weeks after checkout. Your fraud queue opens the order, copies the billing IP, and hits today's IP reputation API. The score is 0. Someone stamps the dispute "clean egress" and moves on.&lt;/p&gt;

&lt;p&gt;That workflow asks the wrong question. IP risk on IP99 by ThreatHunter is a &lt;strong&gt;judgement that expires&lt;/strong&gt;: the score measures &lt;strong&gt;freshness and strength of verifiable evidence&lt;/strong&gt;, not "probability this cardholder is an attacker." An address that looked like a dial-up proxy exit at purchase may show &lt;code&gt;evidence_state: none&lt;/code&gt; and score 0 by the time Finance asks. Re-querying &lt;em&gt;now&lt;/em&gt; does not reconstruct &lt;em&gt;then&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Live gate vs chargeback clock
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Moment&lt;/th&gt;
&lt;th&gt;Question&lt;/th&gt;
&lt;th&gt;Free &lt;code&gt;GET /v1/ip/{ip}&lt;/code&gt;
&lt;/th&gt;
&lt;th&gt;Pulse &lt;code&gt;GET /pulse/v1/ip/{ip}?at=&lt;/code&gt;
&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Checkout / claim&lt;/td&gt;
&lt;td&gt;Is this egress risky &lt;em&gt;right now&lt;/em&gt;?&lt;/td&gt;
&lt;td&gt;Yes — no key required&lt;/td&gt;
&lt;td&gt;Same score, more evidence detail&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Chargeback / audit&lt;/td&gt;
&lt;td&gt;What did we know &lt;em&gt;at order time&lt;/em&gt;?&lt;/td&gt;
&lt;td&gt;No — &lt;code&gt;at&lt;/code&gt; returns 400 &lt;code&gt;at_unsupported&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Yes — professional tier + history enabled, ~30 days back&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Official product split: basic endpoint for real-time control (sign-up, sign-in, pre-order); Pulse for after-the-fact review, reconciliation, and strategy back-testing. &lt;strong&gt;One IP, one score&lt;/strong&gt; — Pulse is not a "better verdict," only a deeper and time-travel view of the same judgement.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to store at decision time (so replay is possible)
&lt;/h2&gt;

&lt;p&gt;If you only keep the final allow/deny bit, chargeback week has nothing to replay. At the gate that matters (checkout, claim, payout), persist at least:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Client egress IP&lt;/strong&gt; (server-observed — never trust a body field the browser sent).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;computed_at&lt;/code&gt;&lt;/strong&gt; from the IP99 response (RFC 3339).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;evidence_state&lt;/code&gt;&lt;/strong&gt;, &lt;code&gt;risk.score&lt;/code&gt; / &lt;code&gt;risk.level&lt;/code&gt;, and &lt;code&gt;risk.signals&lt;/code&gt; (or "signals absent").&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;network.asn&lt;/code&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;code&gt;network.usage_type&lt;/code&gt;&lt;/strong&gt; for blast-radius context when you lack Pulse &lt;code&gt;share_tier&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That snapshot is your audit trail on the free tier. Historical &lt;code&gt;?at=&lt;/code&gt; later lets you &lt;em&gt;re-ask the feed&lt;/em&gt; as of that moment; it does not replace logging what your gate actually used.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to read a replay (same triage order)
&lt;/h2&gt;

&lt;p&gt;Whether you look at the stored snapshot or a Pulse history response, official reading order still applies:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;evidence_state&lt;/code&gt;&lt;/strong&gt; — can this conclusion be trusted at that moment? &lt;code&gt;none&lt;/code&gt; means never seen (coverage gap), not "safe." &lt;code&gt;stale&lt;/code&gt; means seen before but evidence aged out (score 0, weak feature only).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;risk.score&lt;/code&gt; / &lt;code&gt;risk.level&lt;/code&gt;&lt;/strong&gt; — only meaningful once state is &lt;code&gt;active&lt;/code&gt; or &lt;code&gt;stale&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;risk.signals&lt;/code&gt;&lt;/strong&gt; — &lt;em&gt;why&lt;/em&gt; it scored: &lt;code&gt;proxy&lt;/code&gt;, &lt;code&gt;vpn&lt;/code&gt;, &lt;code&gt;dialup_pool&lt;/code&gt;, &lt;code&gt;cloud_phone&lt;/code&gt;, &lt;code&gt;hijacked_proxy&lt;/code&gt;, … matter differently for dispute letters than bare &lt;code&gt;hosting&lt;/code&gt; (a network property — Google Public DNS carries &lt;code&gt;hosting&lt;/code&gt; with score 0).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Share scope&lt;/strong&gt; — Pulse &lt;code&gt;share_tier&lt;/code&gt; (T0–T3) if you ever blocked; on free responses use &lt;code&gt;usage_type&lt;/code&gt; (&lt;code&gt;DYN&lt;/code&gt; / &lt;code&gt;MOB&lt;/code&gt; vs &lt;code&gt;IDC&lt;/code&gt;) as a rough prior. Do not equate one residential NAT ban with one exclusive VPS ban.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Degraded marker&lt;/strong&gt; — basic body &lt;code&gt;degraded: true&lt;/code&gt;, or Pulse header &lt;code&gt;X-Pulse-Degraded: 1&lt;/code&gt; — discount the answer.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;hijacked_proxy&lt;/code&gt; still means the line owner is likely a victim; a chargeback write-up that says "banned the household forever" is usually the wrong story even when proxy evidence was active at purchase.&lt;/p&gt;

&lt;h2&gt;
  
  
  Live curls (2026-10-27, no API key)
&lt;/h2&gt;

&lt;p&gt;Anonymous quota: &lt;strong&gt;500 lookups/day&lt;/strong&gt; per client IP, 60/min. Sign in for 5,000/day. Overseas self-serve Pro / Pro+ buys &lt;strong&gt;volume only&lt;/strong&gt; ($29 / $99) — same evidence fields on &lt;code&gt;/v1/ip&lt;/code&gt;, not a better score. Per-item evidence, &lt;code&gt;share_tier&lt;/code&gt;, and &lt;code&gt;?at=&lt;/code&gt; history are a &lt;strong&gt;separate professional / contracted&lt;/strong&gt; Pulse capability.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# What the free API answers: now only&lt;/span&gt;
curl https://ip99.com/v1/ip/8.8.8.8

&lt;span class="c"&gt;# Docs demo residential address — mask the last octet in tickets&lt;/span&gt;
curl https://ip99.com/v1/ip/27.22.66.200

&lt;span class="c"&gt;# Remaining anon quota (does not spend a lookup on some setups; still cheap to check)&lt;/span&gt;
curl https://ip99.com/v1/me

&lt;span class="c"&gt;# History needs Pulse + professional key — free /v1/ip rejects at=&lt;/span&gt;
&lt;span class="c"&gt;# curl -H "X-API-Key: YOUR_KEY" \&lt;/span&gt;
&lt;span class="c"&gt;#   "https://ip99.com/pulse/v1/ip/86.111.136.23?at=2026-09-14T09:29:23Z"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Sample A: datacenter control (score 0 is not a chargeback win)
&lt;/h3&gt;

&lt;p&gt;Captured ~2026-10-27 13:13 UTC:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ip"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"8.8.8.8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"computed_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-10-27T13:13:13Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"evidence_state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"none"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"geo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"country"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"US"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"region"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"California"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"city"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Mountain View"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"tz"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"America/Los_Angeles"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"network"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"asn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;15169&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"usage_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"IDC"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"risk"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"level"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"none"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"signals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"hosting"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;hosting&lt;/code&gt; here is network shape, not proxy proof. &lt;code&gt;evidence_state: none&lt;/code&gt; with score 0 means IP99 holds &lt;strong&gt;no current verifiable risk evidence&lt;/strong&gt; — not that the payment was legitimate.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sample B: residential dial-up / proxy supply (mask last octet)
&lt;/h3&gt;

&lt;p&gt;Same day live lookup. Full address works in the curl above; tickets should show &lt;code&gt;27.22.66.x&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ip"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"27.22.66.x"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"computed_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-10-27T13:13:15Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"evidence_state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"active"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"geo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"country"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CN"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"region"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Hubei"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"city"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Xiangyang"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"district"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Nanzhang"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"tz"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Asia/Shanghai"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"network"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"asn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4134&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"usage_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DYN"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"risk"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"level"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"low"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"signals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"dialup_pool"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hijacked_proxy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"proxy"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Still &lt;strong&gt;DYN&lt;/strong&gt; home broadband; signals say dial-up pool + proxy presence; &lt;code&gt;hijacked_proxy&lt;/code&gt; warns collateral. &lt;code&gt;active&lt;/code&gt; + &lt;strong&gt;low&lt;/strong&gt; is friction territory at checkout — and the score will move as evidence ages. If you only re-check this IP at chargeback time without a stored &lt;code&gt;computed_at&lt;/code&gt;, you may see a different story and wrongly conclude the gate was wrong.&lt;/p&gt;

&lt;p&gt;Docs also show the same idea on Pulse history: one Luxembourg residential proxy exit scored &lt;strong&gt;90&lt;/strong&gt; at a named past moment and &lt;strong&gt;77&lt;/strong&gt; later — same address, same feed, evidence aged. That drift is intentional.&lt;/p&gt;

&lt;h2&gt;
  
  
  Minimal gate sketch (store the snapshot)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timezone&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lookup_now&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://ip99.com/v1/ip/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;urlopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;snapshot_for_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;lookup_now&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;egress_ip&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ip&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;computed_at&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;computed_at&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;evidence_state&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;evidence_state&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;score&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;risk&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;score&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;level&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;risk&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;level&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;signals&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;risk&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;signals&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;asn&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;network&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{}).&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;asn&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;usage_type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;network&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{}).&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;usage_type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stored_at&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;utc&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;isoformat&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# At chargeback: compare dispute narrative to snapshot_for_order(...).
# To re-ask the feed as of order time, use Pulse ?at= with a professional key —
# do not pretend free /v1/ip can time-travel.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;CORS is open on &lt;code&gt;/v1/ip&lt;/code&gt;, but &lt;strong&gt;run the gate on your server&lt;/strong&gt;. Import &lt;code&gt;https://ip99.com/openapi.json&lt;/code&gt; into Postman or codegen if you want a typed client. Agents can call the same live lookup as MCP tool &lt;code&gt;ip_risk_lookup&lt;/code&gt; at &lt;code&gt;https://ip99.com/mcp&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Honest limits
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Score is &lt;strong&gt;not&lt;/strong&gt; attacker probability; &lt;code&gt;0&lt;/code&gt; / &lt;code&gt;evidence_state: none&lt;/code&gt; is &lt;strong&gt;not&lt;/strong&gt; "safe."&lt;/li&gt;
&lt;li&gt;Free &lt;code&gt;/v1/ip&lt;/code&gt; answers &lt;strong&gt;now&lt;/strong&gt;. Passing &lt;code&gt;at&lt;/code&gt; yields &lt;code&gt;at_unsupported&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Pulse &lt;code&gt;?at=&lt;/code&gt; needs professional access and history enabled separately; history indexes &lt;strong&gt;IPv4&lt;/strong&gt; in the documented window (~30 days). Do not invent free historical fields.&lt;/li&gt;
&lt;li&gt;IP99 and ThreatHunter branding: IP99 is the product; do not claim the free API and every ThreatHunter dataset are identical.&lt;/li&gt;
&lt;li&gt;Mask the last octet of real residential or mobile addresses in public tickets and posts.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;Paste a checkout egress into &lt;a href="https://ip99.com" rel="noopener noreferrer"&gt;ip99.com&lt;/a&gt; or run the free curls above. For live gates, branch on &lt;code&gt;evidence_state&lt;/code&gt; + typed signals and &lt;strong&gt;store &lt;code&gt;computed_at&lt;/code&gt; with the order&lt;/strong&gt;. For chargebacks, argue from that snapshot — and use Pulse history when you truly need "what did the feed say at purchase," not today's re-score of a recycled address.&lt;/p&gt;

</description>
      <category>security</category>
      <category>api</category>
      <category>devops</category>
      <category>webdev</category>
    </item>
    <item>
      <title>This Week's Proxy Exits: 11.5M New Addresses, Still Almost All Off Datacenter Space</title>
      <dc:creator>IP99</dc:creator>
      <pubDate>Mon, 26 Oct 2026 13:17:45 +0000</pubDate>
      <link>https://dev.to/ip99/this-weeks-proxy-exits-115m-new-addresses-still-almost-all-off-datacenter-space-1p08</link>
      <guid>https://dev.to/ip99/this-weeks-proxy-exits-115m-new-addresses-still-almost-all-off-datacenter-space-1p08</guid>
      <description>&lt;p&gt;Datacenter blocklists are still the default proxy defence in a lot of fraud stacks. The addresses showing up in proxy supply this week mostly were not in datacenters.&lt;/p&gt;

&lt;p&gt;Between &lt;strong&gt;19–25 October 2026 (UTC)&lt;/strong&gt;, the &lt;a href="https://ip99.com?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=ip99" rel="noopener noreferrer"&gt;IP99&lt;/a&gt; risk feed captured &lt;strong&gt;11,525,472&lt;/strong&gt; proxy exit addresses it had not seen in the previous 7 days (&lt;strong&gt;+6%&lt;/strong&gt; vs the prior week). Source page: &lt;a href="https://ip99.com/en/proxy-exits?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=ip99" rel="noopener noreferrer"&gt;ip99.com/en/proxy-exits&lt;/a&gt; (generated 26 Oct 2026 UTC).&lt;/p&gt;

&lt;h2&gt;
  
  
  What those addresses sit on
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Usage type&lt;/th&gt;
&lt;th&gt;Addresses&lt;/th&gt;
&lt;th&gt;Share&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Residential broadband&lt;/td&gt;
&lt;td&gt;9,151,795&lt;/td&gt;
&lt;td&gt;79.4%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mobile network&lt;/td&gt;
&lt;td&gt;1,503,531&lt;/td&gt;
&lt;td&gt;13.0%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gateway / NAT egress&lt;/td&gt;
&lt;td&gt;526,366&lt;/td&gt;
&lt;td&gt;4.6%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Datacenter&lt;/td&gt;
&lt;td&gt;260,847&lt;/td&gt;
&lt;td&gt;2.3%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Education network&lt;/td&gt;
&lt;td&gt;49,677&lt;/td&gt;
&lt;td&gt;0.4%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unclassified&lt;/td&gt;
&lt;td&gt;33,256&lt;/td&gt;
&lt;td&gt;0.3%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;About &lt;strong&gt;64%&lt;/strong&gt; were rated high-risk at the moment of capture. The rest still carried a proxy-class signal at a lower risk level.&lt;/p&gt;

&lt;p&gt;IP99 counts an address as "captured" when it enters the feed with a proxy-class signal (&lt;code&gt;proxy&lt;/code&gt;, &lt;code&gt;VPN&lt;/code&gt;, dial-up pool, cloud phone, or hijacked shared proxy) and was &lt;strong&gt;not&lt;/strong&gt; in the live 7-day risk mirror. Addresses that go quiet and return can be counted again. The page describes what &lt;strong&gt;54&lt;/strong&gt; upstream collection sources delivered — not the total number of proxies in the world. No address lists are published.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the 2% matters
&lt;/h2&gt;

&lt;p&gt;If your defence is mainly an ASN or hosting blocklist, you are mostly looking at that &lt;strong&gt;2.3%&lt;/strong&gt; datacenter slice. Residential and mobile space is where home-broadband proxies and dial-up pools live. An address that keeps turning up in proxy supply is a proxy exit whatever its ISP label says.&lt;/p&gt;

&lt;p&gt;That is the same reason a residential allowlist fails the other way: usage type answers "what kind of network is this," not "is this address currently sold as an exit." Rotating residential proxies and dial-up pools are designed to sit in DYN / MOB space. Detecting them needs supply-side observation with an age attached, not a permanent network-type label.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which networks show up most
&lt;/h2&gt;

&lt;p&gt;Top ASNs in this week's capture window (by distinct address count):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;ASN&lt;/th&gt;
&lt;th&gt;Addresses&lt;/th&gt;
&lt;th&gt;Share&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;AS4134&lt;/td&gt;
&lt;td&gt;998,961&lt;/td&gt;
&lt;td&gt;8.7%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AS4837&lt;/td&gt;
&lt;td&gt;725,984&lt;/td&gt;
&lt;td&gt;6.3%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AS9808&lt;/td&gt;
&lt;td&gt;367,102&lt;/td&gt;
&lt;td&gt;3.2%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AS6057&lt;/td&gt;
&lt;td&gt;269,928&lt;/td&gt;
&lt;td&gt;2.3%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AS36947&lt;/td&gt;
&lt;td&gt;242,659&lt;/td&gt;
&lt;td&gt;2.1%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Large access networks appearing here are a clue about where proxy supply is being sold from — not a claim that every customer on those networks is a proxy. Ban a whole ASN and you take out legitimate home and mobile users. Prefer per-address evidence at decision time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Day-to-day shape
&lt;/h2&gt;

&lt;p&gt;Captured addresses (new to the prior 7-day mirror that day), with how many were high-risk at capture:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Day (UTC)&lt;/th&gt;
&lt;th&gt;Captured&lt;/th&gt;
&lt;th&gt;High-risk at capture&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2026-10-19&lt;/td&gt;
&lt;td&gt;1,638,525&lt;/td&gt;
&lt;td&gt;1,098,049&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-10-20&lt;/td&gt;
&lt;td&gt;1,496,080&lt;/td&gt;
&lt;td&gt;1,062,230&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-10-21&lt;/td&gt;
&lt;td&gt;1,679,859&lt;/td&gt;
&lt;td&gt;1,061,464&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-10-22&lt;/td&gt;
&lt;td&gt;1,696,954&lt;/td&gt;
&lt;td&gt;1,060,504&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-10-23&lt;/td&gt;
&lt;td&gt;1,785,796&lt;/td&gt;
&lt;td&gt;1,147,796&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-10-24&lt;/td&gt;
&lt;td&gt;1,658,044&lt;/td&gt;
&lt;td&gt;1,025,424&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-10-25&lt;/td&gt;
&lt;td&gt;1,570,214&lt;/td&gt;
&lt;td&gt;957,858&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Volume rebounded week-over-week (&lt;strong&gt;+6%&lt;/strong&gt; after last week's soft dip), and the mix held: residential near &lt;strong&gt;79%&lt;/strong&gt;, datacenter still about &lt;strong&gt;2%&lt;/strong&gt;. Daily capture stayed in a band roughly &lt;strong&gt;1.5–1.8M&lt;/strong&gt; addresses — no single spike day that would make a static list look "caught up."&lt;/p&gt;

&lt;h2&gt;
  
  
  How to use this without over-blocking
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Branch on evidence, not network type.&lt;/strong&gt; Residential is where your customers live; do not hard-block on DYN / MOB alone.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step up, don't stop.&lt;/strong&gt; A fresh proxy-class signal on signup, a coupon claim, or a login from a new device justifies a second factor or a delay — not an immediate ban.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read the age.&lt;/strong&gt; &lt;code&gt;evidence_state: "stale"&lt;/code&gt; means the address was in supply before and is not now; treat it as a weak prior. Re-query at decision time rather than caching a verdict for days.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep the timestamp.&lt;/strong&gt; Store &lt;code&gt;computed_at&lt;/code&gt; with the decision so a disputed block can be traced back to when the observation was made.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Check one yourself
&lt;/h2&gt;

&lt;p&gt;Free, no signup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://ip99.com/v1/ip/8.8.8.8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look at &lt;code&gt;evidence_state&lt;/code&gt; (&lt;code&gt;active&lt;/code&gt; / &lt;code&gt;stale&lt;/code&gt; / &lt;code&gt;none&lt;/code&gt;), &lt;code&gt;risk.score&lt;/code&gt;, and the proxy-class signals. A score of 0 is not "safe" — it means IP99 holds no current verifiable evidence against that address. The score is the freshness and strength of that evidence, not a probability that the address "is an attacker."&lt;/p&gt;

&lt;p&gt;The same lookup is available to agents as an MCP tool at &lt;code&gt;https://ip99.com/mcp&lt;/code&gt; (&lt;code&gt;ip_risk_lookup&lt;/code&gt;). Field reference and routing notes live on the &lt;a href="https://ip99.com/api?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=ip99" rel="noopener noreferrer"&gt;API docs&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>security</category>
      <category>devops</category>
      <category>api</category>
      <category>fraudprevention</category>
    </item>
    <item>
      <title>Stop Coupon Farms Without Banning a Building: IP Risk at Claim Time</title>
      <dc:creator>IP99</dc:creator>
      <pubDate>Sun, 25 Oct 2026 13:15:21 +0000</pubDate>
      <link>https://dev.to/ip99/stop-coupon-farms-without-banning-a-building-ip-risk-at-claim-time-1d1p</link>
      <guid>https://dev.to/ip99/stop-coupon-farms-without-banning-a-building-ip-risk-at-claim-time-1d1p</guid>
      <description>&lt;p&gt;Yesterday’s playbook for bulk signup still applies when the prize moves downstream: &lt;strong&gt;new-user coupons, referral bonuses, and first-order subsidies&lt;/strong&gt;. Promo farms rarely need a datacenter flood. They rotate residential or dial-up exits, sometimes through cloud phones, claim once per identity surface, and leave. A static “block all hosting” rule misses most of that traffic; a hard ban on every flagged address can still take out a whole building on shared broadband.&lt;/p&gt;

&lt;p&gt;IP99 by ThreatHunter gives you a free &lt;code&gt;GET /v1/ip/{ip}&lt;/code&gt; answer you can call at &lt;strong&gt;claim time&lt;/strong&gt;: typed signals (&lt;code&gt;proxy&lt;/code&gt;, &lt;code&gt;dialup_pool&lt;/code&gt;, &lt;code&gt;cloud_phone&lt;/code&gt;, …), &lt;code&gt;evidence_state&lt;/code&gt;, and a 0–100 score that measures &lt;strong&gt;freshness and strength of verifiable evidence&lt;/strong&gt; — not “probability this shopper is an attacker.”&lt;/p&gt;

&lt;h2&gt;
  
  
  Why claim time is a different gate than signup
&lt;/h2&gt;

&lt;p&gt;Signup gates care about account inventory. Promo gates care about &lt;strong&gt;burn rate on a finite budget&lt;/strong&gt;. The same egress pattern shows up for both, but the cost of a false hard-ban is different: locking out a CGNAT neighborhood during a flash sale can spike support tickets and kill conversion faster than a delayed coupon.&lt;/p&gt;

&lt;p&gt;Typical claim-side patterns:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;Signals you often see&lt;/th&gt;
&lt;th&gt;What a naive rule does wrong&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Rotating residential / dial-up pool&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;proxy&lt;/code&gt; + often &lt;code&gt;dialup_pool&lt;/code&gt; on &lt;code&gt;usage_type: DYN&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;“Allow residential” lets the farm through&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cloud phone farm&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;cloud_phone&lt;/code&gt; (often with hosting attributes)&lt;/td&gt;
&lt;td&gt;Looks like a handset in the UX; network-type rules miss it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hijacked home exit&lt;/td&gt;
&lt;td&gt;&lt;code&gt;hijacked_proxy&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Banning the IP punishes the line owner, not the farm&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Classic VPS / VPN&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;proxy&lt;/code&gt; / &lt;code&gt;vpn&lt;/code&gt; + &lt;code&gt;IDC&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Network-type blocklists catch &lt;em&gt;only&lt;/em&gt; this row&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If your coupon service only asks “is this IDC?”, you are still fighting last decade’s inventory. Weekly public aggregates on &lt;a href="https://ip99.com/en/proxy-exits" rel="noopener noreferrer"&gt;proxy exits&lt;/a&gt; keep showing that newly captured proxy-class addresses are overwhelmingly off datacenter space — so residential allowlists are not a promo shield.&lt;/p&gt;

&lt;h2&gt;
  
  
  A claim-time triage (step up, don’t flatten the building)
&lt;/h2&gt;

&lt;p&gt;Official reading order on IP99 is still: &lt;strong&gt;evidence_state → score/level → which signals → (only if you ever ban) share scope&lt;/strong&gt;. On the free API you do not get Pulse &lt;code&gt;share_tier&lt;/code&gt;; &lt;code&gt;usage_type&lt;/code&gt; plus &lt;code&gt;evidence_state&lt;/code&gt; is enough for a coupon funnel:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;evidence_state: none&lt;/code&gt;&lt;/strong&gt; — no current evidence. Do &lt;strong&gt;not&lt;/strong&gt; mark the claim “safe.” Keep normal fraud checks (device, payment, velocity).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;stale&lt;/code&gt;&lt;/strong&gt; — seen before, evidence aged out, score 0. Weak feature only; do not deny the coupon alone.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;active&lt;/code&gt; + low (1–29)&lt;/strong&gt; — log, optional soft friction (email confirm, slower claim, cap per ASN/&lt;code&gt;/24&lt;/code&gt; for the campaign hour).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;active&lt;/code&gt; + medium (30–69)&lt;/strong&gt; — &lt;strong&gt;step up before payout&lt;/strong&gt;: captcha, second factor, delay grant, or hold the reward for manual/risk review — prefer verify over IP ban when &lt;code&gt;usage_type&lt;/code&gt; is &lt;code&gt;DYN&lt;/code&gt; or &lt;code&gt;MOB&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;active&lt;/code&gt; + high (70–100)&lt;/strong&gt; — decide by campaign burn. Still prefer challenge + delay on shared residential/mobile egress. If &lt;code&gt;hijacked_proxy&lt;/code&gt; is present, treat the household as likely collateral: challenge the session, do not permanently ban the address for a promo.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Signal choice matters for &lt;em&gt;how&lt;/em&gt; you step up: &lt;code&gt;cloud_phone&lt;/code&gt; and &lt;code&gt;dialup_pool&lt;/code&gt; are stronger promo smells than bare &lt;code&gt;hosting&lt;/code&gt; (a network attribute — Google Public DNS carries &lt;code&gt;hosting&lt;/code&gt; with score 0). &lt;code&gt;crawler&lt;/code&gt; is benign and does &lt;strong&gt;not&lt;/strong&gt; participate in scoring; do not treat search-engine exits like coupon farms.&lt;/p&gt;

&lt;h2&gt;
  
  
  Live curls (2026-10-25, no API key)
&lt;/h2&gt;

&lt;p&gt;Anonymous quota is &lt;strong&gt;500 lookups/day&lt;/strong&gt; per client IP (60/min). Sign in for 5,000/day. Overseas self-serve Pro / Pro+ buys volume only ($29 / $99) — same evidence fields, not a “better score.” Professional evidence detail and &lt;code&gt;?at=&lt;/code&gt; history are a separate contracted tier.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Egress you are about to claim from&lt;/span&gt;
curl https://ip99.com/v1/ip

&lt;span class="c"&gt;# Public demo address often used in IP99 docs (mask last octet in tickets)&lt;/span&gt;
curl https://ip99.com/v1/ip/27.22.66.200

&lt;span class="c"&gt;# Datacenter control: hosting attribute, score 0 — not a promo farm by itself&lt;/span&gt;
curl https://ip99.com/v1/ip/8.8.8.8

&lt;span class="c"&gt;# Remaining anon quota&lt;/span&gt;
curl https://ip99.com/v1/me
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Sample: residential dial-up / proxy supply (mask last octet)
&lt;/h3&gt;

&lt;p&gt;Captured ~2026-10-25 13:12 UTC. Full address works in the curl above.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ip"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"27.22.66.x"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"computed_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-10-25T13:12:11Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"evidence_state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"active"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"degraded"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"geo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"country"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CN"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"region"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Hubei"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"city"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Xiangyang"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"district"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Nanzhang"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"admin_code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"420624"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"tz"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Asia/Shanghai"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"network"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"asn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4134&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"usage_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DYN"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"risk"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"level"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"low"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"signals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"dialup_pool"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hijacked_proxy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"proxy"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read left to right: still &lt;strong&gt;DYN&lt;/strong&gt; home broadband; signals say dial-up pool + proxy presence; &lt;code&gt;hijacked_proxy&lt;/code&gt; warns the line owner may be a victim; &lt;code&gt;evidence_state: active&lt;/code&gt; with a &lt;strong&gt;low&lt;/strong&gt; score means friction at claim time, not a permanent ban. &lt;code&gt;degraded: true&lt;/code&gt; means part of the feed was unavailable — treat the answer as soft. Scores move as evidence ages; the same IP an hour later can look different on purpose.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sample: hosting control
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ip"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"8.8.8.8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"evidence_state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"none"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"network"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"asn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;15169&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"usage_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"IDC"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"risk"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"level"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"none"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"signals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"hosting"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;hosting&lt;/code&gt; alone is not a reason to deny a coupon. Pair typed proxy-class signals with campaign velocity and payment risk.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wire it where the budget burns
&lt;/h2&gt;

&lt;p&gt;Call the lookup on your &lt;strong&gt;server&lt;/strong&gt; with the client’s real egress (reverse proxies lie if you only trust &lt;code&gt;X-Forwarded-For&lt;/code&gt; without a trusted hop list). CORS is open on &lt;code&gt;/v1/ip&lt;/code&gt;, but clients can forge their IP — keep the gate server-side.&lt;/p&gt;

&lt;p&gt;Minimal shape for a claim endpoint:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Resolve public client IP.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET https://ip99.com/v1/ip/{ip}&lt;/code&gt; (optional &lt;code&gt;X-API-Key&lt;/code&gt; when you outgrow anon).&lt;/li&gt;
&lt;li&gt;Branch on &lt;code&gt;evidence_state&lt;/code&gt; then &lt;code&gt;risk.level&lt;/code&gt; / &lt;code&gt;risk.signals&lt;/code&gt; as above.&lt;/li&gt;
&lt;li&gt;Persist &lt;code&gt;computed_at&lt;/code&gt;, score, signals, and your decision for dispute review later.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Free &lt;code&gt;/v1/ip&lt;/code&gt; answers &lt;strong&gt;now&lt;/strong&gt;. “What did we know when this coupon was granted?” needs Pulse &lt;code&gt;?at=&lt;/code&gt; (professional contract). Do not invent historical replay on the free tier.&lt;/p&gt;

&lt;p&gt;Import the contract from &lt;code&gt;https://ip99.com/openapi.json&lt;/code&gt; into Postman or codegen if you want typed clients. Agents can use the same lookup as MCP tool &lt;code&gt;ip_risk_lookup&lt;/code&gt; at &lt;code&gt;https://ip99.com/mcp&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this article is not claiming
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;No Tor signal — do not invent one.&lt;/li&gt;
&lt;li&gt;Risk score ≠ attacker probability.&lt;/li&gt;
&lt;li&gt;IP99 public API evidence and ThreatHunter enterprise feeds are related products, not “the same data.”&lt;/li&gt;
&lt;li&gt;Mask real home or mobile customer IPs in tickets and screenshots.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Paste a claim egress into &lt;a href="https://ip99.com" rel="noopener noreferrer"&gt;ip99.com&lt;/a&gt; or run the free curls above. If you are shipping referral or new-user campaigns, start with &lt;code&gt;evidence_state&lt;/code&gt; + typed signals at &lt;strong&gt;claim&lt;/strong&gt; time, and keep hard IP bans rare — especially on shared residential and mobile egress.&lt;/p&gt;

</description>
      <category>security</category>
      <category>api</category>
      <category>devops</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Catch Bulk Signups Without Banning a Building: IP Risk at Registration</title>
      <dc:creator>IP99</dc:creator>
      <pubDate>Sat, 24 Oct 2026 13:11:10 +0000</pubDate>
      <link>https://dev.to/ip99/catch-bulk-signups-without-banning-a-building-ip-risk-at-registration-4j6b</link>
      <guid>https://dev.to/ip99/catch-bulk-signups-without-banning-a-building-ip-risk-at-registration-4j6b</guid>
      <description>&lt;p&gt;Bulk registration rarely looks like a datacenter flood anymore. The signup IP often sits on ordinary broadband (&lt;code&gt;usage_type: DYN&lt;/code&gt;), 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 &lt;strong&gt;fresh, typed signal&lt;/strong&gt; you can step up on — not a permanent label.&lt;/p&gt;

&lt;p&gt;IP99 by ThreatHunter exposes that as a free &lt;code&gt;GET /v1/ip/{ip}&lt;/code&gt; call: risk signals such as &lt;code&gt;proxy&lt;/code&gt;, &lt;code&gt;dialup_pool&lt;/code&gt;, and &lt;code&gt;cloud_phone&lt;/code&gt;, plus &lt;code&gt;evidence_state&lt;/code&gt; and a 0–100 score that measures &lt;strong&gt;freshness and strength of verifiable evidence&lt;/strong&gt;, not "probability this person is an attacker."&lt;/p&gt;

&lt;h2&gt;
  
  
  What bulk signup actually buys
&lt;/h2&gt;

&lt;p&gt;Fraud teams and promo farms still need three cheap inputs: phone numbers (or SMS inboxes), device surfaces, and &lt;strong&gt;egress IPs that rotate&lt;/strong&gt;. The IP side has shifted off pure hosting space:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;What you tend to see&lt;/th&gt;
&lt;th&gt;Why a DYN allowlist fails&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Rotating residential / dial-up pool&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;proxy&lt;/code&gt; + often &lt;code&gt;dialup_pool&lt;/code&gt; on residential ASN&lt;/td&gt;
&lt;td&gt;Address is real broadband&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cloud phone farm&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;cloud_phone&lt;/code&gt; (and often hosting attributes)&lt;/td&gt;
&lt;td&gt;Looks like mobile UX, not a VPS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hijacked home exit&lt;/td&gt;
&lt;td&gt;&lt;code&gt;hijacked_proxy&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Line owner is often a victim&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Classic VPS / VPN&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;proxy&lt;/code&gt; / &lt;code&gt;vpn&lt;/code&gt; + &lt;code&gt;usage_type: IDC&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Network-type rules catch &lt;em&gt;only&lt;/em&gt; this row&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If your registration gate only asks "is this IDC?", you are fighting last decade's inventory.&lt;/p&gt;

&lt;h2&gt;
  
  
  A practical registration gate (not a ban hammer)
&lt;/h2&gt;

&lt;p&gt;Official triage order on IP99 is: &lt;strong&gt;evidence_state → score/level → which signals → (if you ever ban) share scope&lt;/strong&gt;. On the free API you do not get Pulse &lt;code&gt;share_tier&lt;/code&gt;; you still have &lt;code&gt;usage_type&lt;/code&gt; and &lt;code&gt;evidence_state&lt;/code&gt;, which is enough for a signup funnel:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;evidence_state: none&lt;/code&gt;&lt;/strong&gt; — no current evidence. Do &lt;strong&gt;not&lt;/strong&gt; stamp the account "clean." Treat as unknown; keep normal KYC / rate limits.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;stale&lt;/code&gt;&lt;/strong&gt; — seen before, evidence aged out, score 0. Weak feature only; do not hard-block alone.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;active&lt;/code&gt; + low (1–29)&lt;/strong&gt; — log and watch; optional soft friction (email confirm, slower signup).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;active&lt;/code&gt; + medium (30–69)&lt;/strong&gt; — &lt;strong&gt;step up&lt;/strong&gt;: captcha, device attestation, SMS or app challenge, throttle new accounts from that ASN or &lt;code&gt;/24&lt;/code&gt; for an hour — still prefer verify over ban.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;active&lt;/code&gt; + high (70–100)&lt;/strong&gt; — decide by business cost. Prefer challenge + delay over IP ban when &lt;code&gt;usage_type&lt;/code&gt; is &lt;code&gt;MOB&lt;/code&gt;/&lt;code&gt;DYN&lt;/code&gt; (shared egress). If &lt;code&gt;hijacked_proxy&lt;/code&gt; is present, do &lt;strong&gt;not&lt;/strong&gt; treat the subscriber as the fraud actor; challenge the session, do not nuke the household forever.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Signals matter for the &lt;em&gt;kind&lt;/em&gt; of step-up: &lt;code&gt;cloud_phone&lt;/code&gt; and &lt;code&gt;dialup_pool&lt;/code&gt; are stronger registration smells than bare &lt;code&gt;hosting&lt;/code&gt; (network attribute — Google DNS carries &lt;code&gt;hosting&lt;/code&gt; with score 0). &lt;code&gt;crawler&lt;/code&gt; is benign and does not score.&lt;/p&gt;

&lt;h2&gt;
  
  
  Live curls (2026-10-24, no API key)
&lt;/h2&gt;

&lt;p&gt;Anonymous quota is &lt;strong&gt;500 lookups/day&lt;/strong&gt; 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).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Your own egress before you test signup from that network&lt;/span&gt;
curl https://ip99.com/v1/ip

&lt;span class="c"&gt;# Public demo often used in IP99 docs (residential dial-up / proxy supply — masks in tickets)&lt;/span&gt;
curl https://ip99.com/v1/ip/27.22.66.200

&lt;span class="c"&gt;# Datacenter control: hosting attribute, score 0&lt;/span&gt;
curl https://ip99.com/v1/ip/8.8.8.8

&lt;span class="c"&gt;# Quota remaining (same anon bucket as lookups)&lt;/span&gt;
curl https://ip99.com/v1/me
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Sample: residential dial-up / proxy supply (mask last octet in tickets)
&lt;/h3&gt;

&lt;p&gt;Captured ~2026-10-24 13:08 UTC. Full address works in the curl above; display masks the last octet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ip"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"27.22.66.x"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"computed_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-10-24T13:08:34Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"evidence_state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"active"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"geo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"country"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CN"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"region"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Hubei"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"city"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Xiangyang"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"district"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Nanzhang"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"admin_code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"420624"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"tz"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Asia/Shanghai"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"network"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"asn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4134&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"usage_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DYN"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"risk"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;26&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"level"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"low"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"signals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"dialup_pool"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hijacked_proxy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"proxy"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read left to right: still &lt;strong&gt;DYN&lt;/strong&gt; home broadband; signals say dial-up pool + proxy presence, and &lt;code&gt;hijacked_proxy&lt;/code&gt; warns the line owner may be a victim; &lt;code&gt;evidence_state: active&lt;/code&gt; with a &lt;strong&gt;low&lt;/strong&gt; 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.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sample: hosting control
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ip"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"8.8.8.8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"evidence_state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"none"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"network"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"asn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;15169&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"usage_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"IDC"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"risk"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"level"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"none"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"signals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"hosting"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;hosting&lt;/code&gt; alone is not a registration reject reason. Pair network type with &lt;strong&gt;risk&lt;/strong&gt; signals and freshness.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wire it into signup (minimal shape)
&lt;/h2&gt;

&lt;p&gt;Pseudo-flow at &lt;code&gt;POST /register&lt;/code&gt; after you have the client IP (trusted proxy headers only if you terminate TLS yourself):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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 &amp;gt;= 70: challenge_or_delay(); optional short IP throttle
  elif score &amp;gt;= 30: captcha_or_otp(); raise_new_account_rate_limit()
  else: log_for_review()
else:
  # none / stale — unknown or expired evidence
  apply_baseline_limits_only()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;CORS is open on &lt;code&gt;/v1/ip&lt;/code&gt;, so a browser can call it, but &lt;strong&gt;do the gate on your server&lt;/strong&gt; — clients lie about their IP. Import the machine-readable contract from &lt;code&gt;https://ip99.com/openapi.json&lt;/code&gt; into Postman or your codegen if you prefer typed clients. For agents, the same lookup is also available as MCP tool &lt;code&gt;ip_risk_lookup&lt;/code&gt; at &lt;code&gt;https://ip99.com/mcp&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this article is not promising
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;IP risk is not a substitute for phone / device / behavior checks; it is one cheap early signal.&lt;/li&gt;
&lt;li&gt;Score is not attacker probability; &lt;code&gt;0&lt;/code&gt; / &lt;code&gt;evidence_state: none&lt;/code&gt; is not "safe."&lt;/li&gt;
&lt;li&gt;Free &lt;code&gt;/v1/ip&lt;/code&gt; answers &lt;strong&gt;now&lt;/strong&gt;. Historical "what did we know at chargeback time?" needs Pulse &lt;code&gt;?at=&lt;/code&gt; (professional contract) — do not invent that on the free tier.&lt;/li&gt;
&lt;li&gt;Do not advertise Tor detection; IP99's public signal set does not center on that.&lt;/li&gt;
&lt;li&gt;Mask real residential or mobile customer IPs in tickets and blog posts (last octet is enough).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;Paste a signup egress into &lt;a href="https://ip99.com" rel="noopener noreferrer"&gt;ip99.com&lt;/a&gt; or hit the free curl above. If you are building registration or promo abuse controls, start with &lt;code&gt;evidence_state&lt;/code&gt; + typed signals, and keep hard IP bans rare — especially on shared residential and mobile egress.&lt;/p&gt;

</description>
      <category>security</category>
      <category>api</category>
      <category>devops</category>
      <category>webdev</category>
    </item>
    <item>
      <title>This Week's Proxy Exits: 11.3M New Addresses, Still Almost All Off Datacenter Space</title>
      <dc:creator>IP99</dc:creator>
      <pubDate>Fri, 23 Oct 2026 13:09:19 +0000</pubDate>
      <link>https://dev.to/ip99/this-weeks-proxy-exits-113m-new-addresses-still-almost-all-off-datacenter-space-3gph</link>
      <guid>https://dev.to/ip99/this-weeks-proxy-exits-113m-new-addresses-still-almost-all-off-datacenter-space-3gph</guid>
      <description>&lt;p&gt;Datacenter blocklists are still the default proxy defence in a lot of fraud stacks. The addresses showing up in proxy supply this week mostly were not in datacenters.&lt;/p&gt;

&lt;p&gt;Between &lt;strong&gt;16–22 October 2026 (UTC)&lt;/strong&gt;, the &lt;a href="https://ip99.com?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=ip99" rel="noopener noreferrer"&gt;IP99&lt;/a&gt; risk feed captured &lt;strong&gt;11,330,797&lt;/strong&gt; proxy exit addresses it had not seen in the previous 7 days (&lt;strong&gt;-5%&lt;/strong&gt; vs the prior week). Source page: &lt;a href="https://ip99.com/en/proxy-exits?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=ip99" rel="noopener noreferrer"&gt;ip99.com/en/proxy-exits&lt;/a&gt; (generated 23 Oct 2026 UTC).&lt;/p&gt;

&lt;h2&gt;
  
  
  What those addresses sit on
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Usage type&lt;/th&gt;
&lt;th&gt;Addresses&lt;/th&gt;
&lt;th&gt;Share&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Residential broadband&lt;/td&gt;
&lt;td&gt;8,929,906&lt;/td&gt;
&lt;td&gt;78.8%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mobile network&lt;/td&gt;
&lt;td&gt;1,544,616&lt;/td&gt;
&lt;td&gt;13.6%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gateway / NAT egress&lt;/td&gt;
&lt;td&gt;550,109&lt;/td&gt;
&lt;td&gt;4.9%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Datacenter&lt;/td&gt;
&lt;td&gt;230,551&lt;/td&gt;
&lt;td&gt;2.0%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Education network&lt;/td&gt;
&lt;td&gt;45,780&lt;/td&gt;
&lt;td&gt;0.4%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unclassified&lt;/td&gt;
&lt;td&gt;29,835&lt;/td&gt;
&lt;td&gt;0.3%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;About &lt;strong&gt;64%&lt;/strong&gt; were rated high-risk at the moment of capture. The rest still carried a proxy-class signal at a lower risk level.&lt;/p&gt;

&lt;p&gt;IP99 counts an address as "captured" when it enters the feed with a proxy-class signal (&lt;code&gt;proxy&lt;/code&gt;, &lt;code&gt;VPN&lt;/code&gt;, dial-up pool, cloud phone, or hijacked shared proxy) and was &lt;strong&gt;not&lt;/strong&gt; in the live 7-day risk mirror. Addresses that go quiet and return can be counted again. The page describes what &lt;strong&gt;56&lt;/strong&gt; upstream collection sources delivered — not the total number of proxies in the world. No address lists are published.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the 2% matters
&lt;/h2&gt;

&lt;p&gt;If your defence is mainly an ASN or hosting blocklist, you are mostly looking at that &lt;strong&gt;2.0%&lt;/strong&gt; datacenter slice. Residential and mobile space is where home-broadband proxies and dial-up pools live. An address that keeps turning up in proxy supply is a proxy exit whatever its ISP label says.&lt;/p&gt;

&lt;p&gt;That is the same reason a residential allowlist fails the other way: usage type answers "what kind of network is this," not "is this address currently sold as an exit." Rotating residential proxies and dial-up pools are designed to sit in DYN / MOB space. Detecting them needs supply-side observation with an age attached, not a permanent network-type label.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which networks show up most
&lt;/h2&gt;

&lt;p&gt;Top ASNs in this week's capture window (by distinct address count):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;ASN&lt;/th&gt;
&lt;th&gt;Addresses&lt;/th&gt;
&lt;th&gt;Share&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;AS4134&lt;/td&gt;
&lt;td&gt;950,083&lt;/td&gt;
&lt;td&gt;8.4%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AS4837&lt;/td&gt;
&lt;td&gt;726,688&lt;/td&gt;
&lt;td&gt;6.4%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AS9808&lt;/td&gt;
&lt;td&gt;358,188&lt;/td&gt;
&lt;td&gt;3.2%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AS36947&lt;/td&gt;
&lt;td&gt;281,699&lt;/td&gt;
&lt;td&gt;2.5%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AS36903&lt;/td&gt;
&lt;td&gt;281,170&lt;/td&gt;
&lt;td&gt;2.5%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Large access networks appearing here are a clue about where proxy supply is being sold from — not a claim that every customer on those networks is a proxy. Ban a whole ASN and you take out legitimate home and mobile users. Prefer per-address evidence at decision time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Day-to-day shape
&lt;/h2&gt;

&lt;p&gt;Captured addresses (new to the prior 7-day mirror that day), with how many were high-risk at capture:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Day (UTC)&lt;/th&gt;
&lt;th&gt;Captured&lt;/th&gt;
&lt;th&gt;High-risk at capture&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2026-10-16&lt;/td&gt;
&lt;td&gt;1,667,871&lt;/td&gt;
&lt;td&gt;1,059,319&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-10-17&lt;/td&gt;
&lt;td&gt;1,671,080&lt;/td&gt;
&lt;td&gt;1,036,063&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-10-18&lt;/td&gt;
&lt;td&gt;1,480,428&lt;/td&gt;
&lt;td&gt;887,422&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-10-19&lt;/td&gt;
&lt;td&gt;1,638,525&lt;/td&gt;
&lt;td&gt;1,098,049&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-10-20&lt;/td&gt;
&lt;td&gt;1,496,080&lt;/td&gt;
&lt;td&gt;1,062,230&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-10-21&lt;/td&gt;
&lt;td&gt;1,679,859&lt;/td&gt;
&lt;td&gt;1,061,464&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-10-22&lt;/td&gt;
&lt;td&gt;1,696,954&lt;/td&gt;
&lt;td&gt;1,060,504&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Volume eased a little week-over-week (&lt;strong&gt;-5%&lt;/strong&gt;), but the mix held: residential near &lt;strong&gt;79%&lt;/strong&gt;, datacenter still about &lt;strong&gt;2%&lt;/strong&gt;. Daily capture stayed in a band roughly &lt;strong&gt;1.5–1.7M&lt;/strong&gt; addresses — no single spike day that would make a static list look "caught up."&lt;/p&gt;

&lt;h2&gt;
  
  
  How to use this without over-blocking
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Branch on evidence, not network type.&lt;/strong&gt; Residential is where your customers live; do not hard-block on DYN / MOB alone.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step up, don't stop.&lt;/strong&gt; A fresh proxy-class signal on signup, a coupon claim, or a login from a new device justifies a second factor or a delay — not an immediate ban.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read the age.&lt;/strong&gt; &lt;code&gt;evidence_state: "stale"&lt;/code&gt; means the address was in supply before and is not now; treat it as a weak prior. Re-query at decision time rather than caching a verdict for days.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep the timestamp.&lt;/strong&gt; Store &lt;code&gt;computed_at&lt;/code&gt; with the decision so a disputed block can be traced back to when the observation was made.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Check one yourself
&lt;/h2&gt;

&lt;p&gt;Free, no signup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://ip99.com/v1/ip/8.8.8.8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look at &lt;code&gt;evidence_state&lt;/code&gt; (&lt;code&gt;active&lt;/code&gt; / &lt;code&gt;stale&lt;/code&gt; / &lt;code&gt;none&lt;/code&gt;), &lt;code&gt;risk.score&lt;/code&gt;, and the proxy-class signals. A score of 0 is not "safe" — it means IP99 holds no current verifiable evidence against that address. The score is the freshness and strength of that evidence, not a probability that the address "is an attacker."&lt;/p&gt;

&lt;p&gt;The same lookup is available to agents as an MCP tool at &lt;code&gt;https://ip99.com/mcp&lt;/code&gt; (&lt;code&gt;ip_risk_lookup&lt;/code&gt;). Field reference and routing notes live on the &lt;a href="https://ip99.com/api?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=ip99" rel="noopener noreferrer"&gt;API docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;IP99 is built by ThreatHunter. Weekly capture totals refresh daily on &lt;a href="https://ip99.com/en/proxy-exits?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=ip99" rel="noopener noreferrer"&gt;ip99.com/en/proxy-exits&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>security</category>
      <category>devops</category>
      <category>api</category>
      <category>fraudprevention</category>
    </item>
    <item>
      <title>Dial-Up Pools Hide in Residential Space: Detect Them Without Hard-Banning Customers</title>
      <dc:creator>IP99</dc:creator>
      <pubDate>Thu, 22 Oct 2026 13:19:56 +0000</pubDate>
      <link>https://dev.to/ip99/dial-up-pools-hide-in-residential-space-detect-them-without-hard-banning-customers-51mk</link>
      <guid>https://dev.to/ip99/dial-up-pools-hide-in-residential-space-detect-them-without-hard-banning-customers-51mk</guid>
      <description>&lt;p&gt;A datacenter blocklist will not catch a dial-up pool. The address is ordinary broadband: &lt;code&gt;usage_type&lt;/code&gt; says &lt;code&gt;DYN&lt;/code&gt;, the ASN is a consumer ISP, and geo looks like a real city. The only thing that changes is the &lt;em&gt;tenant&lt;/em&gt; — a line that redials on demand, picks up a fresh address, and burns through hundreds of residential IPs a day before those same addresses go back to real subscribers.&lt;/p&gt;

&lt;p&gt;That is the core idea: &lt;strong&gt;network type describes the pipe; dial-up pool describes how the pipe is being used right now.&lt;/strong&gt; IP99 by ThreatHunter reports the latter as &lt;code&gt;dialup_pool&lt;/code&gt; in &lt;code&gt;risk.signals&lt;/code&gt;, with &lt;code&gt;evidence_state&lt;/code&gt; and &lt;code&gt;computed_at&lt;/code&gt; so you know whether the observation is still worth acting on.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a dial-up pool is
&lt;/h2&gt;

&lt;p&gt;A dial-up pool (Chinese ops slang: 秒拨 / &lt;em&gt;miaobo&lt;/em&gt;, "per-second redial") is a set of ADSL or fibre lines that reconnect on demand. Each reconnect gets a new public address from the carrier — sometimes across many cities. One line can exhaust hundreds of legitimate residential addresses in a day. Later the same day, a normal household may inherit one of those addresses.&lt;/p&gt;

&lt;p&gt;Related plumbing in the same market:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Rotating residential proxies&lt;/strong&gt; — SDK on a phone or router; the address belongs to a real subscriber and changes hands every few minutes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hijacked exits&lt;/strong&gt; — a device owned by a victim briefly sold as an exit (&lt;code&gt;hijacked_proxy&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;VPN / cloud phone / cloud function exits&lt;/strong&gt; — different signals (&lt;code&gt;vpn&lt;/code&gt;, &lt;code&gt;cloud_phone&lt;/code&gt;, &lt;code&gt;cloud_service&lt;/code&gt;); different disposition.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these need to sit in datacenter space. That is why "block IDC ASN" and "allow DYN" both fail in opposite directions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why usage type cannot see them
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;network.usage_type&lt;/code&gt; answers "what kind of network is this?" For dial-up pools the honest answer is residential broadband (&lt;code&gt;DYN&lt;/code&gt;). A residential allowlist has zero precision against this product; a datacenter denylist has zero recall. The mark is not on the address — it is in &lt;strong&gt;observed presence in proxy supply&lt;/strong&gt;, and that observation ages out in minutes to hours.&lt;/p&gt;

&lt;p&gt;So you need three things together:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A signal that the address is (or was) in that supply — e.g. &lt;code&gt;dialup_pool&lt;/code&gt; and often &lt;code&gt;proxy&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;How fresh that observation is — &lt;code&gt;evidence_state&lt;/code&gt; + &lt;code&gt;computed_at&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A score that compresses freshness and strength of evidence — &lt;strong&gt;not&lt;/strong&gt; "probability this IP is an attacker."&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Live lookups (2026-10-22)
&lt;/h2&gt;

&lt;p&gt;No key. Anonymous free quota is &lt;strong&gt;500/day&lt;/strong&gt; per client IP (60/min). Signed-in free is 5,000/day; overseas self-serve Pro starts at $29/mo for volume (mainland China: free + enterprise contract only).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Active dial-up sample (public demo IP from IP99 docs; residential samples churn)&lt;/span&gt;
curl https://ip99.com/v1/ip/27.22.66.200

&lt;span class="c"&gt;# Homepage example that used to carry dialup_pool — today proves expiry&lt;/span&gt;
curl https://ip99.com/v1/ip/1.197.213.232

&lt;span class="c"&gt;# Datacenter control&lt;/span&gt;
curl https://ip99.com/v1/ip/8.8.8.8

&lt;span class="c"&gt;# Your own egress&lt;/span&gt;
curl https://ip99.com/v1/ip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  1) Active dial-up (mask last octet in tickets)
&lt;/h3&gt;

&lt;p&gt;Captured ~2026-10-22 13:15 UTC. Displayed body masks the last octet; the curl above uses the full public demo address.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ip"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"27.22.66.x"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"computed_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-10-22T13:15:10Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"evidence_state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"active"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"geo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"country"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CN"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"region"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Hubei"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"city"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Xiangyang"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"district"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Nanzhang"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"admin_code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"420624"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"tz"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Asia/Shanghai"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"network"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"asn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4134&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"usage_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DYN"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"risk"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;78&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"level"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"high"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"signals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"dialup_pool"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hijacked_proxy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"proxy"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read it left to right: still &lt;strong&gt;DYN&lt;/strong&gt; residential space; signals say dial-up pool + proxy presence, and &lt;code&gt;hijacked_proxy&lt;/code&gt; warns the line owner may be a victim; &lt;code&gt;evidence_state: active&lt;/code&gt; and a mid/high score mean the evidence is fresh and strong enough to step up — not that you have proven a human attacker.&lt;/p&gt;

&lt;h3&gt;
  
  
  2) Same product story, opposite clock: expiry
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;1.197.213.232&lt;/code&gt; is a public homepage sample that older docs showed around score 62 with &lt;code&gt;dialup_pool&lt;/code&gt; + &lt;code&gt;proxy&lt;/code&gt;. Re-fetched the same day as the active sample:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ip"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.197.213.232"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"computed_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-10-22T13:14:56Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"evidence_state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"none"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"network"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"asn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4134&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"usage_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DYN"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"risk"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"level"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"none"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Still &lt;code&gt;DYN&lt;/code&gt;. Score 0 with &lt;code&gt;evidence_state: none&lt;/code&gt; means &lt;strong&gt;no current verifiable evidence&lt;/strong&gt; — a blind spot, not a permanent "clean" badge. Yesterday's dial-up exit must not permanently brand tomorrow's subscriber. IP99's slogan for this: every answer is a &lt;strong&gt;judgement that expires&lt;/strong&gt;, not a permanent label.&lt;/p&gt;

&lt;h3&gt;
  
  
  3) Datacenter control
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ip"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"8.8.8.8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"evidence_state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"none"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"network"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"asn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;15169&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"usage_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"IDC"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"risk"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"level"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"none"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"signals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"hosting"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;hosting&lt;/code&gt; (and &lt;code&gt;mobile&lt;/code&gt;) are &lt;strong&gt;network attributes&lt;/strong&gt;, not risk for scoring. Do not ban on them alone. &lt;code&gt;crawler&lt;/code&gt; is benign and does not participate in scoring. IP99 has &lt;strong&gt;no Tor signal&lt;/strong&gt; — do not invent one.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to act on a &lt;code&gt;dialup_pool&lt;/code&gt; hit
&lt;/h2&gt;

&lt;p&gt;Official disposition guidance is generic, not a contract:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;evidence_state&lt;/code&gt; first.&lt;/strong&gt; &lt;code&gt;active&lt;/code&gt; → the observation is current. &lt;code&gt;stale&lt;/code&gt; → seen before, aged out to score 0; weak prior only. &lt;code&gt;none&lt;/code&gt; → never seen here; do not treat as allowlist proof.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step up, don't stop.&lt;/strong&gt; Fresh &lt;code&gt;proxy&lt;/code&gt; / &lt;code&gt;dialup_pool&lt;/code&gt; on signup, coupon claim, or new-device login justifies CAPTCHA, second factor, delay, or rate limit — not a permanent IP or account ban on residential customers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read &lt;code&gt;hijacked_proxy&lt;/code&gt; carefully.&lt;/strong&gt; The handset or line owner is often a victim. Prefer challenge over account termination.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Store &lt;code&gt;computed_at&lt;/code&gt; with the decision.&lt;/strong&gt; When someone disputes a block, you need the observation time, not a naked "bad IP" tag.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Re-query at decision time.&lt;/strong&gt; Dial-up and rotating residential evidence decays in minutes to hours. Caching a verdict across sessions recreates the false-positive gap the product is designed to close.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Score bands (same docs): 0 → do not stamp "clean"; 1–29 → log; 30–69 → verify rather than ban; 70–100 → decide by business cost, still preferring time-bounded controls on &lt;code&gt;DYN&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  DYN vs signals (the distinction that matters)
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Answers&lt;/th&gt;
&lt;th&gt;Dial-up pool case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;network.usage_type&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Kind of network&lt;/td&gt;
&lt;td&gt;Usually &lt;code&gt;DYN&lt;/code&gt; — looks like a customer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;risk.signals&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;What was observed in supply&lt;/td&gt;
&lt;td&gt;May include &lt;code&gt;dialup_pool&lt;/code&gt;, &lt;code&gt;proxy&lt;/code&gt;, …&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;evidence_state&lt;/code&gt; + &lt;code&gt;computed_at&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Whether that observation still holds&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;active&lt;/code&gt; now; may be &lt;code&gt;none&lt;/code&gt; hours later&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;risk.score&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Freshness + strength of evidence&lt;/td&gt;
&lt;td&gt;High when evidence is fresh; not attacker probability&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If your pipeline only branches on &lt;code&gt;usage_type == IDC&lt;/code&gt;, you will miss this class entirely. If it permanently denylists every &lt;code&gt;DYN&lt;/code&gt; that ever scored high, you will ban the next household that inherits the address.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://ip99.com/v1/ip/27.22.66.200
curl https://ip99.com/v1/ip/1.197.213.232
curl https://ip99.com/v1/ip/8.8.8.8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start at &lt;a href="https://ip99.com/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=ip99&amp;amp;utm_content=dialup-pools" rel="noopener noreferrer"&gt;ip99.com&lt;/a&gt; or the English API reference at &lt;a href="https://ip99.com/api" rel="noopener noreferrer"&gt;ip99.com/api&lt;/a&gt;. Before the next hard ban on a "residential" IP, ask: &lt;strong&gt;are you blocking a pool that redials — or the subscriber who gets that address an hour later?&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>networking</category>
      <category>api</category>
      <category>devops</category>
    </item>
    <item>
      <title>One Ban Is One Building: CGNAT, Shared Egress, and Ban-Scope Decisions</title>
      <dc:creator>IP99</dc:creator>
      <pubDate>Wed, 21 Oct 2026 13:19:33 +0000</pubDate>
      <link>https://dev.to/ip99/one-ban-is-one-building-cgnat-shared-egress-and-ban-scope-decisions-2mmf</link>
      <guid>https://dev.to/ip99/one-ban-is-one-building-cgnat-shared-egress-and-ban-scope-decisions-2mmf</guid>
      <description>&lt;p&gt;Hard-banning a single public IP feels precise. On many networks it is not. Carrier-grade NAT (CGNAT), mobile egress, and enterprise gateway exits often put dozens to tens of thousands of people behind one address. When you block that address, you are not blocking "the attacker" — you are blocking everyone who happens to share the same egress today.&lt;/p&gt;

&lt;p&gt;That is the core idea: &lt;strong&gt;an address is not a person&lt;/strong&gt;. IP99 by ThreatHunter documents this as &lt;em&gt;share tier&lt;/em&gt; on its Pulse professional API, and the same docs tell you how to approximate ban-scope decisions from free &lt;code&gt;/v1/ip&lt;/code&gt; fields when you do not have Pulse yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  What CGNAT and shared egress look like in practice
&lt;/h2&gt;

&lt;p&gt;On a classic home broadband line, one household may share a public IP via a home router NAT. That is already multi-user, but the blast radius of a ban is usually one apartment.&lt;/p&gt;

&lt;p&gt;CGNAT and carrier pools go further. Mobile operators (&lt;code&gt;usage_type: MOB&lt;/code&gt;) and many enterprise / campus gateways (&lt;code&gt;usage_type: GTW&lt;/code&gt;) put large numbers of subscribers or employees behind a small set of public addresses. One abusive session on that egress, plus a permanent IP denylist, can look like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;an entire office floor losing access after lunch,&lt;/li&gt;
&lt;li&gt;a dorm building locked out during exam week,&lt;/li&gt;
&lt;li&gt;or a mobile user who "keeps getting banned" every time the carrier rotates them onto a polluted pool.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The failure mode is not "your blocklist is wrong about risk." It is that &lt;strong&gt;the unit of enforcement (one IP) is larger than the unit of identity (one person or one device)&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Share tier T0–T3 (Pulse professional context)
&lt;/h2&gt;

&lt;p&gt;IP99's Pulse professional endpoint returns &lt;code&gt;share_tier&lt;/code&gt; so you can size the blast radius before you hard-ban. From the official API docs:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tier&lt;/th&gt;
&lt;th&gt;Rough shared users&lt;/th&gt;
&lt;th&gt;Typical network&lt;/th&gt;
&lt;th&gt;Suggested handling&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;T0&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~1–10&lt;/td&gt;
&lt;td&gt;Dedicated / datacenter egress&lt;/td&gt;
&lt;td&gt;Hard ban is low collateral&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;T1&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~1–20&lt;/td&gt;
&lt;td&gt;Home broadband NAT&lt;/td&gt;
&lt;td&gt;Ban possible; may hit one household&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;T2&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~50–2,000&lt;/td&gt;
&lt;td&gt;Enterprise line, campus egress&lt;/td&gt;
&lt;td&gt;Be careful — one ban can be a building&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;T3&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~1,000–100,000+&lt;/td&gt;
&lt;td&gt;Mobile cellular CGNAT&lt;/td&gt;
&lt;td&gt;Do &lt;strong&gt;not&lt;/strong&gt; hard-ban the IP; prefer challenge, rate-limit, or down-rank&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Share tier also feeds scoring: the more people share an address, the harder old evidence decays, so a T3 score tends to be more conservative than a T0 score on comparable evidence.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Honest boundary:&lt;/strong&gt; free &lt;code&gt;GET /v1/ip&lt;/code&gt; does &lt;strong&gt;not&lt;/strong&gt; return &lt;code&gt;share_tier&lt;/code&gt;. That field is Pulse professional only (&lt;code&gt;GET /pulse/v1/ip/{ip}&lt;/code&gt;). Do not write pipelines that expect &lt;code&gt;share_tier&lt;/code&gt; on the anonymous free call.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the free API &lt;em&gt;does&lt;/em&gt; give you today
&lt;/h2&gt;

&lt;p&gt;You can still make safer ban-scope decisions with free fields. No key required:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://ip99.com/v1/ip/8.8.8.8
curl https://ip99.com/v1/ip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Live shapes re-fetched &lt;strong&gt;2026-10-21&lt;/strong&gt; (Asia/Shanghai):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ip"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"8.8.8.8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"computed_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-10-21T13:15:59Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"evidence_state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"none"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"geo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"city"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Mountain View"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"country"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"US"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"region"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"California"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"network"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"asn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;15169&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"usage_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"IDC"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"risk"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"level"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"none"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"signals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"hosting"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ip"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"140.248.50.7"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"computed_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-10-21T13:16:00Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"evidence_state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"none"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"geo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"city"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Ashburn"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"country"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"US"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"region"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Virginia"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"network"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"asn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;54113&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"usage_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"IDC"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"risk"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"level"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"none"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"signals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"hosting"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(The second call is this publisher's box egress — datacenter/&lt;code&gt;IDC&lt;/code&gt;, so the full address is shown. Mask the last octet for residential or mobile exits in your own posts and tickets.)&lt;/p&gt;

&lt;p&gt;Fields that matter for shared-egress triage:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;network.usage_type&lt;/code&gt;&lt;/strong&gt;: &lt;code&gt;MOB&lt;/code&gt; (mobile), &lt;code&gt;GTW&lt;/code&gt; (enterprise / gateway), &lt;code&gt;DYN&lt;/code&gt; (residential broadband), &lt;code&gt;IDC&lt;/code&gt; (datacenter), and others. Treat &lt;code&gt;MOB&lt;/code&gt; / &lt;code&gt;GTW&lt;/code&gt; as &lt;em&gt;likely shared&lt;/em&gt; even without &lt;code&gt;share_tier&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;risk.signals&lt;/code&gt;&lt;/strong&gt;: proxy-class hits such as &lt;code&gt;proxy&lt;/code&gt;, &lt;code&gt;vpn&lt;/code&gt;, &lt;code&gt;dialup_pool&lt;/code&gt;, &lt;code&gt;hijacked_proxy&lt;/code&gt;, plus network attributes like &lt;code&gt;hosting&lt;/code&gt; / &lt;code&gt;mobile&lt;/code&gt;. Do &lt;strong&gt;not&lt;/strong&gt; ban on &lt;code&gt;hosting&lt;/code&gt; or &lt;code&gt;mobile&lt;/code&gt; alone — &lt;code&gt;8.8.8.8&lt;/code&gt; carries &lt;code&gt;hosting&lt;/code&gt; with score &lt;code&gt;0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;evidence_state&lt;/code&gt;&lt;/strong&gt;: &lt;code&gt;active&lt;/code&gt; (fresh evidence scoring), &lt;code&gt;stale&lt;/code&gt; (seen before, aged out to 0), &lt;code&gt;none&lt;/code&gt; (never seen — a blind spot, not "clean").&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;risk.score&lt;/code&gt;&lt;/strong&gt;: 0–100 measuring &lt;strong&gt;freshness and strength of verifiable evidence&lt;/strong&gt;, not "probability this is an attacker."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Anonymous free quota is &lt;strong&gt;500 lookups/day&lt;/strong&gt; per public IP (&lt;code&gt;X-Daily-Limit&lt;/code&gt; / &lt;code&gt;X-Daily-Remaining&lt;/code&gt;); signed-in free is &lt;strong&gt;5,000/day&lt;/strong&gt;. Docs: &lt;a href="https://ip99.com/api" rel="noopener noreferrer"&gt;ip99.com/api&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  A practical free-tier playbook
&lt;/h2&gt;

&lt;p&gt;When Pulse is not available, approximate ban scope from usage type + evidence state:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Read &lt;code&gt;evidence_state&lt;/code&gt; first.&lt;/strong&gt; Score 0 with &lt;code&gt;none&lt;/code&gt; means "no current evidence," not "safe to permanently allowlist." Score 0 with &lt;code&gt;stale&lt;/code&gt; means you saw something earlier — treat it as a weak historical hint.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Classify the network.&lt;/strong&gt; If &lt;code&gt;usage_type&lt;/code&gt; is &lt;code&gt;MOB&lt;/code&gt; or &lt;code&gt;GTW&lt;/code&gt;, assume high share (T2/T3-like). Prefer CAPTCHA, step-up auth, rate limits, or feature down-rank over a permanent IP ban.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;On &lt;code&gt;DYN&lt;/code&gt;, still be careful.&lt;/strong&gt; Home NAT is often T1-ish: a hard ban can lock out a whole household, including the victim of a &lt;code&gt;hijacked_proxy&lt;/code&gt; (the docs note the handset owner is often a victim — do not auto-ban accounts on that signal alone).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;On &lt;code&gt;IDC&lt;/code&gt; with active proxy-class signals&lt;/strong&gt;, hard bans are lower collateral &lt;em&gt;when&lt;/em&gt; evidence is fresh — but still prefer time-bounded blocks. Evidence expires; permanent denylists do not.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Never treat &lt;code&gt;hosting&lt;/code&gt; / &lt;code&gt;mobile&lt;/code&gt; as proxy verdicts.&lt;/strong&gt; They describe network placement, not abuse.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Official disposition guidance (generic, not a contract): scores 30–69 → verify rather than ban; 70–100 → decide by business cost &lt;strong&gt;and&lt;/strong&gt; share context; on mobile CGNAT-class egress, skip the IP hard-ban entirely.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters more than another blocklist
&lt;/h2&gt;

&lt;p&gt;Abuse tooling often sells "IP reputation" as a permanent label. Shared egress breaks that model twice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Identity mismatch&lt;/strong&gt; — one address, many people.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time mismatch&lt;/strong&gt; — the abusive session leaves; the address stays in your denylist for days or weeks while real users inherit it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;IP99's product stance is that every answer is a &lt;strong&gt;judgement that expires&lt;/strong&gt;, not a permanent label. Free &lt;code&gt;/v1/ip&lt;/code&gt; already exposes the fields you need to act on that idea: usage type for likely share, evidence state for trustworthiness, signals for &lt;em&gt;what&lt;/em&gt; was observed, and a score that tracks how fresh that evidence is.&lt;/p&gt;

&lt;p&gt;When you later need explicit &lt;code&gt;share_tier&lt;/code&gt; T0–T3 and per-evidence freshness rows, that is what Pulse professional adds — same score, more context for ban-scope decisions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Any public IP&lt;/span&gt;
curl https://ip99.com/v1/ip/8.8.8.8

&lt;span class="c"&gt;# Your own egress&lt;/span&gt;
curl https://ip99.com/v1/ip

&lt;span class="c"&gt;# Quota / plan headers&lt;/span&gt;
curl &lt;span class="nt"&gt;-sI&lt;/span&gt; https://ip99.com/v1/ip/8.8.8.8 | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-iE&lt;/span&gt; &lt;span class="s1"&gt;'x-daily|x-plan'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start at &lt;a href="https://ip99.com/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=ip99&amp;amp;utm_content=cgnat-shared-egress" rel="noopener noreferrer"&gt;ip99.com&lt;/a&gt; or the English API reference at &lt;a href="https://ip99.com/api" rel="noopener noreferrer"&gt;ip99.com/api&lt;/a&gt;. Before the next hard IP ban, ask one question: &lt;strong&gt;are you blocking a person — or a building?&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>networking</category>
      <category>api</category>
      <category>devops</category>
    </item>
    <item>
      <title>From One Phish Domain to the Rest of the Cluster: Passive DNS on IP99</title>
      <dc:creator>IP99</dc:creator>
      <pubDate>Tue, 20 Oct 2026 13:14:59 +0000</pubDate>
      <link>https://dev.to/ip99/from-one-phish-domain-to-the-rest-of-the-cluster-passive-dns-on-ip99-fem</link>
      <guid>https://dev.to/ip99/from-one-phish-domain-to-the-rest-of-the-cluster-passive-dns-on-ip99-fem</guid>
      <description>&lt;p&gt;Ordinary DNS answers one question: where does this name point &lt;strong&gt;right now&lt;/strong&gt;? Fraud infrastructure rarely stays put. Domains rotate, IPs get swapped, registrars change. What does not change is the history of relationships that already happened.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ip99.com/domain" rel="noopener noreferrer"&gt;Passive DNS&lt;/a&gt; (pDNS) stores those observed answers over time: which name resolved to which address, and for how long. On &lt;a href="https://ip99.com" rel="noopener noreferrer"&gt;IP99&lt;/a&gt; (by ThreatHunter) you get a free trial of that history — forward lookup, reverse lookup, and a light malware-list check — without inventing a key for the first queries.&lt;/p&gt;

&lt;p&gt;This post walks a practical pivot: &lt;strong&gt;domain → historical IPs → other names on those IPs&lt;/strong&gt;, then how to read the timestamps without fooling yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why pivoting beats a single lookup
&lt;/h2&gt;

&lt;p&gt;A phishing page that was taken down yesterday still leaves footprints. The server it used three months ago often still hosts other names from the same crew. IP99's own product copy puts it bluntly: a retired phish domain's old resolver address frequently carries dozens more domains from the same operators.&lt;/p&gt;

&lt;p&gt;That is infrastructure pivoting. Certificate Transparency (CT) can show you a lookalike was signed early. pDNS shows you where it landed and who else shared the machine. Either alone is half a picture; together they close a loop. We covered CT in an earlier post — here the focus is the DNS side.&lt;/p&gt;

&lt;h2&gt;
  
  
  Forward lookup: history for one name
&lt;/h2&gt;

&lt;p&gt;Free trial, no key, ten queries per client IP per day (signing in does not raise this particular quota):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://ip99.com/v1/domain/dns/example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A live call on &lt;strong&gt;20 Oct 2026&lt;/strong&gt; (same shape as the 15 Oct check) returned &lt;code&gt;corpus_last_seen: "2026-08-15"&lt;/code&gt;, twenty-one records (A, AAAA, MX, NS, TXT), and a &lt;code&gt;profile&lt;/code&gt; that enriched landing IPs with country, city, ASN, and ASN org. Shape (trimmed):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"domain"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"corpus_last_seen"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-08-15"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"has_more"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"rrtype"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"A"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"104.20.23.154"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"first_seen"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-08-15"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"last_seen"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-08-15"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"profile"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"malware"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"listed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"last_listed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"days_listed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"record_types"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"A"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"AAAA"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"MX"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"NS"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"TXT"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Six record types are covered, not only A. Mail and nameserver churn show up in the same response. Empty &lt;code&gt;data: []&lt;/code&gt; is a real answer (no observed rows), not an error. Bad parameters return HTTP 400 with a clear code — automate on status, not on emptiness.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do not misread &lt;code&gt;last_seen&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This is the field people get wrong most often. IP99 imports resolution corpora in periodic batches. Seeing a &lt;code&gt;last_seen&lt;/code&gt; a month ago usually means &lt;strong&gt;that snapshot's cutoff&lt;/strong&gt;, not that the name went dark. Read &lt;code&gt;last_seen&lt;/code&gt; against the top-level &lt;code&gt;corpus_last_seen&lt;/code&gt;. Do not hard-code a date in your pipeline.&lt;/p&gt;

&lt;p&gt;A separate path — newly registered domains for a calendar day — updates daily and has different time semantics. Do not apply the same interpretation to both.&lt;/p&gt;

&lt;p&gt;Times are UTC. Aggregate by day only after you account for that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reverse lookup: who else shared the address
&lt;/h2&gt;

&lt;p&gt;Once you have a landing IP, flip the question:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://ip99.com/v1/domain/reverse/104.20.23.154
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same day's call returned &lt;strong&gt;432&lt;/strong&gt; co-hosted names on that Cloudflare anycast address (&lt;code&gt;has_more: false&lt;/code&gt; for that page). That volume is useful and noisy at once. Shared CDN and hosting IPs can reverse to hundreds or thousands of unrelated tenants. Treat reverse hits as &lt;strong&gt;candidates&lt;/strong&gt;, then filter with CT fingerprints, malware-list days, registrar timing, or content similarity — not as automatic guilt-by-IP.&lt;/p&gt;

&lt;p&gt;Limits to know: up to 1000 rows per response, paginate with &lt;code&gt;?offset=N&lt;/code&gt; and stop when &lt;code&gt;has_more&lt;/code&gt; is false. Full-table reverse can take seconds (the service waits up to about 25s). Pass a bare address only — no &lt;code&gt;:443&lt;/code&gt;, no &lt;code&gt;/32&lt;/code&gt;. Private addresses are valid query values when they appear in real DNS history; they are not rejected as "invalid IP".&lt;/p&gt;

&lt;p&gt;IP99 does &lt;strong&gt;not&lt;/strong&gt; do active resolution, subdomain brute force, or one-shot bulk export of an IP's entire map through this trial surface. What you get is observed history, page by page.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enrich the egress, not just the name
&lt;/h2&gt;

&lt;p&gt;After you shortlist an IP that looks like dedicated fraud hosting (or before you block a signup from that space), check the address itself with the free no-key risk API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://ip99.com/v1/ip/8.8.8.8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example shape from the same day:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ip"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"8.8.8.8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"evidence_state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"none"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"risk"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"level"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"none"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"signals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"hosting"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"network"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"asn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;15169&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"usage_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"IDC"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"geo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"country"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"US"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"city"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Mountain View"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;score&lt;/code&gt; is freshness and strength of verifiable risk evidence, &lt;strong&gt;not&lt;/strong&gt; the probability that someone is an attacker. &lt;code&gt;evidence_state: none&lt;/code&gt; with score 0 means no current evidence in the feed — a coverage gap, not a clean bill of health. &lt;code&gt;hosting&lt;/code&gt; here is a network property (datacenter / CDN-class space), not a proxy verdict; do not block solely on &lt;code&gt;hosting&lt;/code&gt; or &lt;code&gt;mobile&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Anonymous quota for &lt;code&gt;/v1/ip&lt;/code&gt; is separate from the pDNS trial (500/day per client IP; signed-in accounts get more). Same honesty rule across products: absence of a hit is not proof of safety.&lt;/p&gt;

&lt;h2&gt;
  
  
  A short workflow you can script
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Start from a reported phish or lookalike domain.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /v1/domain/dns/{domain}&lt;/code&gt; — collect historical A/AAAA (and note NS/MX if they jumped).&lt;/li&gt;
&lt;li&gt;For each non-CDN-looking landing IP, &lt;code&gt;GET /v1/domain/reverse/{ip}&lt;/code&gt; — build a candidate cluster.&lt;/li&gt;
&lt;li&gt;Cross-check interesting names with CT (&lt;code&gt;/v1/domain/certs/...&lt;/code&gt;) when you need issuance timing.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /v1/ip/{ip}&lt;/code&gt; on egress you might gate — branch on &lt;code&gt;evidence_state&lt;/code&gt;, then score, then signals.&lt;/li&gt;
&lt;li&gt;Prefer step-up friction over hard bans when the address sits on shared residential or mobile space.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Optional helpers on the same free trial quota: &lt;code&gt;GET /v1/domain/malware/{domain}&lt;/code&gt; for a millisecond list check, and &lt;code&gt;GET /v1/domain/registered/{YYYY-MM-DD}&lt;/code&gt; for the first 1000 newly registered names that day (no pagination on that endpoint).&lt;/p&gt;

&lt;h2&gt;
  
  
  Coverage honesty
&lt;/h2&gt;

&lt;p&gt;pDNS is observation, not authority. Sensor placement matters. Niche, brand-new, or region-local names may simply never appear. IP99 says that gap out loud: treat "we have no rows" differently from "we have rows and no malware hit." No single feed — this one included — sees the whole proxy or phishing market.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;Open the interactive page at &lt;a href="https://ip99.com/domain" rel="noopener noreferrer"&gt;ip99.com/domain&lt;/a&gt;, or start with the curls above. For risk and city-level geo (China down to district) on any public IP, use &lt;a href="https://ip99.com" rel="noopener noreferrer"&gt;ip99.com&lt;/a&gt; or &lt;code&gt;curl https://ip99.com/v1/ip/{ip}&lt;/code&gt;. Docs and OpenAPI live under &lt;code&gt;/api&lt;/code&gt; and &lt;code&gt;/openapi.json&lt;/code&gt; if you want to generate a client instead of pasting shell.&lt;/p&gt;

&lt;p&gt;Every answer on the risk side is a judgement that expires. On the DNS side, every row is a relationship that was observed — with a corpus clock you must read next to &lt;code&gt;last_seen&lt;/code&gt;. Use both clocks, and you spend less time chasing ghosts.&lt;/p&gt;

</description>
      <category>api</category>
      <category>security</category>
      <category>dns</category>
      <category>osint</category>
    </item>
    <item>
      <title>This Week's Proxy Exits: 10.8M New Addresses, Still Almost All Off Datacenter Space</title>
      <dc:creator>IP99</dc:creator>
      <pubDate>Tue, 20 Oct 2026 00:00:38 +0000</pubDate>
      <link>https://dev.to/ip99/this-weeks-proxy-exits-108m-new-addresses-still-almost-all-off-datacenter-space-3ob5</link>
      <guid>https://dev.to/ip99/this-weeks-proxy-exits-108m-new-addresses-still-almost-all-off-datacenter-space-3ob5</guid>
      <description>&lt;p&gt;Datacenter blocklists are still the default proxy defence in a lot of fraud stacks. The addresses showing up in proxy supply this week mostly were not in datacenters.&lt;/p&gt;

&lt;p&gt;Between &lt;strong&gt;12–18 October 2026 (UTC)&lt;/strong&gt;, the &lt;a href="https://ip99.com?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=ip99" rel="noopener noreferrer"&gt;IP99&lt;/a&gt; risk feed captured &lt;strong&gt;10,829,683&lt;/strong&gt; proxy exit addresses it had not seen in the previous 7 days (&lt;strong&gt;-7%&lt;/strong&gt; vs the prior week). Source page: &lt;a href="https://ip99.com/en/proxy-exits?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=ip99" rel="noopener noreferrer"&gt;ip99.com/en/proxy-exits&lt;/a&gt; (capture window still 12–18 Oct 2026 UTC as of publish).&lt;/p&gt;

&lt;h2&gt;
  
  
  What those addresses sit on
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Usage type&lt;/th&gt;
&lt;th&gt;Addresses&lt;/th&gt;
&lt;th&gt;Share&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Residential broadband&lt;/td&gt;
&lt;td&gt;8,645,883&lt;/td&gt;
&lt;td&gt;79.8%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mobile network&lt;/td&gt;
&lt;td&gt;1,436,705&lt;/td&gt;
&lt;td&gt;13.3%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gateway / NAT egress&lt;/td&gt;
&lt;td&gt;466,938&lt;/td&gt;
&lt;td&gt;4.3%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Datacenter&lt;/td&gt;
&lt;td&gt;220,055&lt;/td&gt;
&lt;td&gt;2.0%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Education network&lt;/td&gt;
&lt;td&gt;36,064&lt;/td&gt;
&lt;td&gt;0.3%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unclassified&lt;/td&gt;
&lt;td&gt;24,038&lt;/td&gt;
&lt;td&gt;0.2%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;About &lt;strong&gt;63%&lt;/strong&gt; were rated high-risk at the moment of capture. The rest still carried a proxy-class signal at a lower risk level.&lt;/p&gt;

&lt;p&gt;IP99 counts an address as "captured" when it enters the feed with a proxy-class signal (&lt;code&gt;proxy&lt;/code&gt;, &lt;code&gt;VPN&lt;/code&gt;, dial-up pool, cloud phone, or hijacked shared proxy) and was &lt;strong&gt;not&lt;/strong&gt; in the live 7-day risk mirror. Addresses that go quiet and return can be counted again. The page describes what &lt;strong&gt;53&lt;/strong&gt; upstream collection sources delivered - not the total number of proxies in the world.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the 2% matters
&lt;/h2&gt;

&lt;p&gt;If your defence is mainly an ASN or hosting blocklist, you are mostly looking at that &lt;strong&gt;2.0%&lt;/strong&gt; datacenter slice. Residential and mobile space is where home-broadband proxies and dial-up pools live. An address that keeps turning up in proxy supply is a proxy exit whatever its ISP label says.&lt;/p&gt;

&lt;p&gt;Top ASNs in this week's capture window (by address count) include large access networks such as AS4134 and AS4837. That is a clue about where supply is being sold from, not a claim that every customer on those networks is a proxy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Day-to-day shape
&lt;/h2&gt;

&lt;p&gt;Captured addresses (new to the prior 7-day mirror that day), with how many were high-risk at capture:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Day (UTC)&lt;/th&gt;
&lt;th&gt;Captured&lt;/th&gt;
&lt;th&gt;High-risk at capture&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2026-10-12&lt;/td&gt;
&lt;td&gt;1,522,820&lt;/td&gt;
&lt;td&gt;961,739&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-10-13&lt;/td&gt;
&lt;td&gt;1,501,634&lt;/td&gt;
&lt;td&gt;953,757&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-10-14&lt;/td&gt;
&lt;td&gt;1,489,460&lt;/td&gt;
&lt;td&gt;957,899&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-10-15&lt;/td&gt;
&lt;td&gt;1,496,390&lt;/td&gt;
&lt;td&gt;965,299&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-10-16&lt;/td&gt;
&lt;td&gt;1,667,871&lt;/td&gt;
&lt;td&gt;1,059,319&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-10-17&lt;/td&gt;
&lt;td&gt;1,671,080&lt;/td&gt;
&lt;td&gt;1,036,063&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-10-18&lt;/td&gt;
&lt;td&gt;1,480,428&lt;/td&gt;
&lt;td&gt;887,422&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Volume cooled off after last week's spike (&lt;strong&gt;-7%&lt;/strong&gt; WoW), but the mix barely moved: residential still near &lt;strong&gt;80%&lt;/strong&gt;, datacenter still about &lt;strong&gt;2%&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check one yourself
&lt;/h2&gt;

&lt;p&gt;Free, no signup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://ip99.com/v1/ip/8.8.8.8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look at &lt;code&gt;evidence_state&lt;/code&gt; (&lt;code&gt;active&lt;/code&gt; / &lt;code&gt;stale&lt;/code&gt; / &lt;code&gt;none&lt;/code&gt;), &lt;code&gt;risk.score&lt;/code&gt;, and the proxy-class signals. A score of 0 is not "safe" - it means IP99 holds no current verifiable evidence against that address.&lt;/p&gt;

&lt;p&gt;IP99 is built by ThreatHunter. Weekly capture totals refresh daily on &lt;a href="https://ip99.com/en/proxy-exits?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=ip99" rel="noopener noreferrer"&gt;ip99.com/en/proxy-exits&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>security</category>
      <category>devops</category>
      <category>api</category>
      <category>fraudprevention</category>
    </item>
  </channel>
</rss>
