My Router Was a Buffet for Bots. So I Built It a Bouncer.
Every exposed IP on the internet gets probed, poked and brute-forced on a schedule. Mine was no different, until I got annoyed enough to build something about it, and decided to give it away.
TL;DR Built a free public IP reputation API because scanners were eating my routers alive. One threshold check now blocks over 80% of that traffic. It costs nothing to use, it's staying that way.
Docs and both endpoints, right now, no scrolling required: https://netbait.org/docs
Here's a fun exercise. Tail your access logs for thirty seconds on any server with a public IP. Now count how many of those lines are a person doing something you'd recognize as human, versus a script checking whether you're still running a WordPress plugin from 2019, or trying admin:admin for the four-hundredth time this week.
I did that exercise on my own routers and did not love the results. The internet does not wait for an invitation. Every open port is a buffet, and there is an entire, tireless economy of scanners and botnets that shows up automatically the moment your IP is reachable, whether you built anything interesting yet or not.
01. Enough Was Enough
I went looking for a way to fight back that didn't involve babysitting fail2ban rules forever, and ran into the same wall a lot of people hit: the tools that actually know an IP's history are either community blocklists that only cover known-bad ranges (most attackers rotate through fresh ones constantly), or proper reputation APIs that gate the useful fields, proxy detection, abuse contacts, ASN classification, behind a paid plan. Free tiers exist, but the quotas are built for occasionally checking a suspicious IP by hand, not for scoring real, ongoing traffic.
I didn't want to check IPs occasionally. I wanted to check all of them, automatically, forever, for free. So I built that instead.
02. What a Score Actually Confesses
Netbait boils down to two endpoints. The one you'll use the most just answers one question: how sketchy is this address, on a scale from 0 to 1?
curl https://api.netbait.org/v1/score/45.83.64.1
# 0.3922
Plain number back, no JSON, nothing to parse. That's enough to make an allow/throttle/block decision on its own. But the number alone doesn't tell you why, and that's where it gets genuinely interesting. Here's the detail endpoint for that exact same address, unedited:
curl https://api.netbait.org/v1/detail/45.83.64.1
{
"ip": "45.83.64.1",
"abuse_score": 0.3922,
"non_residential_score": 0,
"non_residential_forced": false,
"known": true,
"categories": ["attack"],
"sources": ["maltrail_scanners"],
"first_seen": "2026-07-08",
"last_seen": "2026-07-18",
"rir": "RIPE",
"is_bogon": false,
"is_datacenter": false,
"is_proxy": false,
"is_vpn": false,
"is_abuser": true,
"company": {
"name": "ALPHASTRIKE-RESEARCH",
"abuser_score": "0.3922 (Elevated)",
"domain": "",
"type": "isp",
"network": "45.83.64.0 - 45.83.67.255",
"netname": "INTERNET-RESEARCH-NET"
},
"abuse": {
"name": "Abuse-C Role",
"address": "Albert-Einstein-Straรe 14, 12489, Berlin, GERMANY",
"email": "abuse@alphastrike.io",
"phone": ""
},
"asn": {
"asn": 208843,
"abuser_score": "0.0000 (Low)",
"route": "45.83.64.0/22",
"descr": "ALPHASTRIKE-RESEARCH, DE",
"country": "de",
"active": true,
"org": "ALPHASTRIKE-RESEARCH",
"domain": "",
"abuse": "abuse@alphastrike.io",
"type": "isp",
"created": "2019-06-03",
"updated": "2022-05-03",
"rir": "RIPE"
},
"location": {
"is_eu_member": true,
"calling_code": "49",
"currency_code": "EUR",
"continent": "EU",
"country": "Germany",
"country_code": "DE",
"state": "",
"city": "",
"latitude": null,
"longitude": null,
"zip": "",
"timezone": "",
"local_time": "",
"local_time_unix": 1784381028,
"is_dst": false,
"utcoffset": "",
"accuracy": "LOW"
},
"facts": {
"known": true,
"categories": ["attack"],
"sources": ["maltrail_scanners"],
"first_seen": "2026-07-08",
"last_seen": "2026-07-18"
},
"elapsed_ms": 129.46
}
That's the full, unedited response, empty strings and all. Worth actually sitting with it instead of skimming past it. Notice is_datacenter, is_proxy and is_vpn are all false. This isn't a rented VPS hiding behind a hosting provider. categories says attack, sources says one single feed, maltrail_scanners, and the company is literally named ALPHASTRIKE-RESEARCH on a netname called INTERNET-RESEARCH-NET, with a real abuse contact and a street address in Berlin. That's not a criminal hiding, that's an internet-wide research scanner, the same category of operation as Shodan or Censys, sweeping ranges and touching ports nobody invited it to touch. One source, one category, ASN itself clean at 0.0000. That's why the score lands at 0.39, elevated but nowhere near hostile.
Now put that next to a real 0.9 and the difference explains itself. Same endpoint, a different address:
curl https://api.netbait.org/v1/detail/185.220.101.5
{
"ip": "185.220.101.5",
"abuse_score": 0.949,
"non_residential_score": 1,
"non_residential_forced": true,
"known": true,
"categories": ["composite", "abuse", "anonymizer", "aggregate", "attack"],
"sources": [
"firehol_level4",
"firehol_abusers",
"abuseipdb",
"stopforumspam",
"firehol_anonymous",
"stamparm_ipsum",
"jamesbrine"
],
"first_seen": "2026-07-08",
"last_seen": "2026-07-18",
"rir": "RIPE",
"is_bogon": false,
"is_datacenter": true,
"is_proxy": true,
"is_vpn": true,
"is_abuser": true,
"company": {
"name": "TORSERVERS-NET",
"abuser_score": "0.9490 (High)",
"domain": "",
"type": "hosting",
"network": "185.220.100.0 - 185.220.103.255",
"netname": "ARTIKEL10"
},
"abuse": {
"name": "Artikel10 e.V. Abuse Handling",
"address": "Artikel10 e.V., Rueckertstrasse 41, 22089 Hamburg, Germany",
"email": "abuse@artikel10.org",
"phone": ""
},
"asn": {
"asn": 60729,
"abuser_score": "1.0000 (High)",
"route": "185.220.100.0/22",
"descr": "TORSERVERS-NET, DE",
"country": "de",
"active": true,
"org": "TORSERVERS-NET",
"domain": "",
"abuse": "abuse@artikel10.org",
"type": "hosting",
"created": "2021-08-19",
"updated": "2025-06-18",
"rir": "RIPE"
},
"location": {
"is_eu_member": true,
"calling_code": "49",
"currency_code": "EUR",
"continent": "EU",
"country": "Germany",
"country_code": "DE",
"state": "",
"city": "",
"latitude": null,
"longitude": null,
"zip": "",
"timezone": "",
"local_time": "",
"local_time_unix": 1784381069,
"is_dst": false,
"utcoffset": "",
"accuracy": "LOW"
},
"facts": {
"known": true,
"categories": ["composite", "abuse", "anonymizer", "aggregate", "attack"],
"sources": ["firehol_level4", "firehol_abusers", "abuseipdb", "stopforumspam", "firehol_anonymous", "stamparm_ipsum", "jamesbrine"],
"first_seen": "2026-07-08",
"last_seen": "2026-07-18"
},
"elapsed_ms": 317.8
}
Everything about this one is the opposite of the research scanner. Where 45.83.64.1 had one category and one source, this address is flagged under five categories at once and corroborated by seven independent feeds, from AbuseIPDB to StopForumSpam to Firehol's own lists. is_datacenter, is_proxy and is_vpn are all true, and the ASN itself carries an abuser score of 1.0000, meaning the surrounding network isn't hosting one bad address, the whole range has a reputation.
Worth being precise about what this address actually is: a Tor exit node run by Artikel10 e.V., a real nonprofit operating part of the Tor network out of Germany, tied to the Torservers.net project. Nobody at Artikel10 is personally attacking anyone. But Tor exit nodes get used for genuinely bad traffic constantly, credential stuffing, comment spam, vulnerability scanning, brute force, all of it, precisely because they hide where the request actually came from. That's not a guess or guilt by association here: seven independent detection feeds, AbuseIPDB, StopForumSpam, Firehol's own lists and others, each flagged this exact address on their own, without coordinating with each other. When a score on a Tor exit lands this high, it's not because "it's Tor, so maybe." It's because that specific address has actually been caught doing it, repeatedly, by multiple unrelated sources at once. That's the actual value of sources and categories: one blocklist hit could be a false positive, but seven unrelated feeds agreeing, plus a maxed-out ASN score, is evidence, not suspicion.
Side by side, the two responses tell two completely different stories that a bare number would flatten into "moderate" and "high": one is a research operation scanning too broadly, the other is anonymizing infrastructure that legitimate and malicious traffic both hide behind. Same schema, same endpoint, very different reason to be careful.
03. Free Isn't the Bait
I already run this infrastructure for my own projects, so extra public lookups cost me very little on the margin. What I get back is more traffic hitting the API from more angles, which keeps the underlying picture sharper for everyone querying it, myself included.
Here's the part people usually assume wrong: the tokens are free too. There's no paid tier hiding behind them. If the public 10 req/s limit isn't enough for what you're building, email me, tell me what you're working on, and we sort out a number that works. I'm genuinely flexible about it, it's a five-minute conversation about your project, not a sales funnel. Nothing here gets pulled behind a paywall later just because it worked, which, yes, I'm aware is a slightly funny thing to promise on a site called Netbait.
04. Four Months Later, My Logs Are Boring Again
This isn't a weekend project I'm hoping works out. It's been running against real traffic on my own infrastructure for more than four months.
At minimum, 80% of my traffic gets blocked before it reaches anything. Some days it's more. The scanners and brute-force attempts that used to fill my logs on schedule, every single day, mostly stopped showing up at all. Not because they got smarter and left me alone, but because the ones that keep hitting the same ranges over and over get scored, cached, and dropped before they ever reach anything worth logging. I disappeared off their radar, and it has stayed that way.
Across every site and API I administer, four months in: zero false positives. Nobody legitimate has ever gotten caught in the net, and nobody running the software elsewhere has reported one either.
That matters more to me than the block rate. A tool that's aggressive but wrong is worse than useless, it just moves the damage from bots onto actual people.
The part that made it practical rather than annoying: never block synchronously on the API call. Let the connection through, score it in the background, and keep a short local cache, a few hours for clean IPs, up to a day for the ones you've already blocked, so a repeat visitor never costs you a second lookup. That pattern is also what keeps the free 10 req/s limit realistic for anyone running this against live traffic instead of the occasional manual check.
If any of this sounds like a problem you're already tired of, the docs are right there and the free tier isn't a trial. Go break it, tell me what's missing.
Two endpoints, no signup, no card. Docs: https://netbait.org/docs
Top comments (0)