I tested my IP reputation checker on 80 addresses I knew the answer to
ipcomet's reputation check answers a question I get asked a lot: can I trust this address? It reports whether an IP is a Tor exit node, what kind of network owns it, and whether it appears on five public blocklists. I wr
ipcomet's reputation check answers a question I get asked a lot: can I trust this address? It reports whether an IP is a Tor exit node, what kind of network owns it, and whether it appears on five public blocklists. I wrote it, which makes me the wrong person to judge it. So I found two groups of addresses where the right answer is public and ran the checker against both.
The test sets
- 40 Tor exit nodes, picked at random from the Tor Project's official exit list, which had 1,226 IPv4 addresses when I downloaded it.
- 40 addresses inside Amazon EC2, picked from the ranges AWS publishes in
ip-ranges.json. The file I used (created 23 October 2026) lists 1,887 EC2 prefixes. For each address I chose a random prefix, then a random host inside it.
Both samples used the random seed 2026. Each address got one call to https://ipcomet.com/api/reputation, 1.2 seconds apart to stay under the 60 a minute limit. All 80 lookups answered.
That gives me ground truth for two things. A Tor exit should be flagged as Tor and an EC2 address should not. And an EC2 address should be labelled as hosting.
What came back
| Tor exits (40) | EC2 addresses (40) | |
|---|---|---|
| Flagged as Tor | 40 | 0 |
| Network kind "hosting" | 13 | 38 |
| Network kind "isp" | 27 | 2 |
| On at least one blocklist | 39 | 0 |
The Tor flag was exact
All 40 exits were flagged and none of the 40 EC2 addresses were. That is the least interesting result, and I'm glad it is. The flag is a lookup in the official exit list, and I had just downloaded a fresh copy. It's also the only field in the response that doesn't rely on a guess.
"Hosting" caught 38 of 40 cloud addresses
Two EC2 addresses came back as "isp". The checker returned A100 ROW Inc as the owner of one and Langfang university Langfang Development Area for the other, and neither name looks like a hosting company. The network kind comes from who owns the address (plus hints in its reverse DNS name), so when the registered owner doesn't look like a hosting provider, the label slips. I didn't dig into why AWS-published prefixes are registered to those names.
Most Tor exits were labelled as ordinary ISPs
This is the result I didn't expect. 27 of the 40 Tor exits came back with the kind "isp", and the label text for that kind reads "Likely a residential or mobile ISP". The 27 spread across 15 different organisations, so one operator isn't skewing it. The tor field was right every time. The sentence next to it was misleading for two out of three of these addresses.
If you read only the network kind, you would have waved most of the exits through.
The blocklists agreed less than they look
39 of the 40 Tor exits appeared on at least one of the five blocklists, and none of the EC2 addresses did. But one list, s5h.net, accounts for 39 of those hits by itself. DroneBL listed 15 of the exits, SpamCop 9 and UCEPROTECT Level 1 only 2. So "listed on 3 of 5" can mean one list that tracks Tor exits plus two more, and in this sample s5h.net behaved like a Tor list.
How I'd use the result
- Trust the Tor flag.
- Treat "hosting" as a strong hint that can miss. It caught 38 of 40 here.
- Read "isp" as "no hosting signs found", and nothing more about whoever is using the address.
- Count blocklist hits per list, not in total.
As the maintainer, the part I take from this is the wording of the "isp" label, which promises more than the data supports.
What this test can't show
- It has no home connections in it. There's no public list of residential addresses, so I can't measure how often a real home gets labelled as hosting, or how many VPNs and proxies get through. The about page already says residential proxies can't be detected from public data.
- It's two samples of 40 from one moment, and only one cloud provider.
- The Tor list changes through the day, so the same seed can pick different exits tomorrow.
Run it yourself
The script I used, tidied up. It needs only the standard library. Set your own contact in the User-Agent, as the last post explains.
import collections, ipaddress, json, random, time, urllib.request
UA = {"User-Agent": "reputation-check/1.0 ([email protected])"}
N = 40
rng = random.Random(2026) # same seed, same sample, if the lists haven't changed
def get(url):
for attempt in range(3):
try:
return urllib.request.urlopen(urllib.request.Request(url, headers=UA), timeout=25).read()
except OSError:
time.sleep(2 * (attempt + 1))
raise RuntimeError("giving up on " + url)
tor = [l.strip() for l in get("https://check.torproject.org/torbulkexitlist").decode().splitlines()
if l.strip() and ":" not in l]
ranges = json.loads(get("https://ip-ranges.amazonaws.com/ip-ranges.json"))
ec2 = [p["ip_prefix"] for p in ranges["prefixes"] if p["service"] == "EC2"]
def random_host(prefix):
net = ipaddress.ip_network(prefix)
return str(net.network_address + rng.randrange(1, max(2, net.num_addresses - 1)))
groups = {"tor exit": rng.sample(tor, N), "aws ec2": [random_host(p) for p in rng.sample(ec2, N)]}
for name, ips in groups.items():
seen = []
for ip in ips:
seen.append(json.loads(get("https://ipcomet.com/api/reputation?q=" + ip)))
time.sleep(1.2) # the endpoint allows 60 a minute
kinds = collections.Counter(r["network"]["kind"] for r in seen)
print(f"{name}: tor flag {sum(r['tor'] for r in seen)}/{N}, "
f"on a blocklist {sum(r['flagged'] > 0 for r in seen)}/{N}, network kinds {dict(kinds)}")
It takes about two minutes. The full response for each address is documented in the API reference.
I maintain ipcomet, a free IP checker. AI-assisted: I used Claude to help draft this post and to run the test. The lookups were made from my own machine, and every request to the checker went to my own API.
Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.