Home › Guides

Blacklist Monitoring: Scripts, an API and Webhook Alerts

Published 2026-11-03 / Updated 2026-11-03 / By Yuki Nakazawa

Blacklist monitoring means querying several DNS blocklists (DNSBLs) for your sending IPs and domains on a schedule, and raising an alert only when a result changes. Catching a listing when it happens, rather than when customers report bounced mail, gives you time to find the cause and request removal before the damage spreads.

There are two practical ways to do it: run a small dig and cron script yourself, or hand the checks to a service through an API and webhooks. Either way, two details decide whether the monitor tells the truth: queries sent through public resolvers are refused by lists such as Spamhaus and produce misleading answers, and error codes must never be counted as listings.

The list operators' rules quoted here are based on their official pages as of November 2026. Policies change, so confirm them before you rely on a setup. For background on how DNSBLs work, see what a DNSBL (RBL) is.

Check for free whether your domain or IP address is on any of about 345 blacklists.

Decide what to watch and which lists to query

Start by writing down the objects you actually need to watch.

The query format is defined in RFC 5782. For IPv4 you reverse the octets and append the list's zone, so 192.0.2.99 on a list becomes 99.2.0.192 followed by the zone name. For IPv6 the address is written as 32 hex nibbles in reverse order. A domain is simply prefixed to the zone. A listed entry returns an A record, conventionally in 127.0.0.0/8; an unlisted one returns NXDOMAIN. The RFC also notes that lists should publish a TXT record explaining the reason for a listing.

Watch several lists rather than one, because receivers do not all use the same ones. What each return code means varies by operator; our guide to DNSBL return codes covers the common ones.

Why public resolvers give wrong answers

A common mistake in home-grown monitoring is sending queries through a public resolver such as Google Public DNS (8.8.8.8) or Cloudflare (1.1.1.1). Spamhaus's official DNSBL usage FAQ explains what happens:

The FAQ's top recommendation is to run your own DNS resolver. For users who must stay on a public resolver, Spamhaus points to its Data Query Service (DQS); check its terms for what is free. The FAQ suggests dig txt whoami.fastly.net to see which resolver you are actually using.

RFC 5782 adds a built-in sanity check: an IPv4 list must contain an entry for 127.0.0.2 and must not contain 127.0.0.1, and clients should periodically check these test entries to confirm the list is working. In Spamhaus's example, 2.0.0.127.zen.spamhaus.org returns 127.0.0.10, 127.0.0.4 and 127.0.0.2, while 1.0.0.127.zen.spamhaus.org returns NXDOMAIN. A monitor should test this first and discard the run if it fails.

Option 1: a dig and cron script

Here is a minimal script that checks one IPv4 address against Spamhaus ZEN and SpamCop (bl.spamcop.net, which SpamCop documents as answering 127.0.0.2 for a listed IP). Replace the address and zones with your own.

#!/bin/sh
# Query your own recursive resolver (e.g. unbound on the same host), never a public one
RESOLVER=127.0.0.1
IP=192.0.2.10
ZONES="zen.spamhaus.org bl.spamcop.net"
REV=$(echo "$IP" | awk -F. '{print $4"."$3"."$2"."$1}')

q() {
  out=$(dig +time=3 +tries=1 @"$RESOLVER" "$1" A) || { echo ERR; return; }
  case "$out" in
    *"status: NOERROR"*) a=$(printf '%s\n' "$out" | awk '$4=="A"{print $5}'); echo "${a:-NX}" ;;
    *"status: NXDOMAIN"*) echo NX ;;
    *) echo ERR ;;
  esac
}

for Z in $ZONES; do
  # If the 127.0.0.2 test entry does not resolve to a listing code, do not trust this run
  case "$(q "2.0.0.127.$Z")" in
    127.255.255.*|NX|ERR) echo "$Z ERROR test-entry"; continue ;;
    127.*) ;;
    *) echo "$Z ERROR test-entry"; continue ;;
  esac
  A=$(q "$REV.$Z")
  case "$A" in
    NX)            echo "$Z CLEAN" ;;
    ERR)           echo "$Z ERROR no-answer" ;;
    127.255.255.*) echo "$Z ERROR $A" ;;
    127.*)         echo "$Z LISTED $A" ;;
    *)             echo "$Z ERROR unexpected $A" ;;
  esac
done

What the script is doing:

Then let cron compare each run with the previous one and send mail only when something changed:

# Example: run hourly, send mail only when the output differs from the last run
0 * * * * /usr/local/bin/dnsbl-watch.sh > /var/tmp/dnsbl.now 2>&1; cmp -s /var/tmp/dnsbl.now /var/tmp/dnsbl.prev || mail -s "DNSBL status changed" you@example.com < /var/tmp/dnsbl.now; mv /var/tmp/dnsbl.now /var/tmp/dnsbl.prev

The hourly interval is only an example. Spamhaus offers free access for low-volume, non-commercial use, and clients that exceed its query volume threshold need a paid option; check each list's terms as your number of monitored IPs grows. The FAQ also advises trying commands a few times to confirm the results stay the same.

Option 2: the DNSBL Checker batch API and webhooks

If you would rather not maintain a resolver and per-list parsing rules, you can let a service run the checks. Below is the v1 API of this site, limited to what the server implementation actually does. The reference lives at /en/api-docs and keys are issued on the members page at /account. Sign-up needs only an email address, and you log in with a link sent by email.

Authentication and limits

Batch checks

Send a targets array of domain names or IP addresses to POST /api/v1/batch:

curl -X POST https://dnsblchecker.com/api/v1/batch \
  -H "X-API-Key: $DNSBL_API_KEY" -H "Content-Type: application/json" \
  -d '{"targets":["example.com"]}'

If any target still needs checking, the response is 202 with a batch_id. Poll GET /api/v1/batch/{batch_id} every few seconds until status is done (abridged example; the numbers and list name are illustrative, not a real result):

{
  "batch_id": 1, "status": "done", "created": "2026-11-03 09:10:41", "count": 1, "pending": 0,
  "results": [
    {
      "target": "example.com", "type": "domain",
      "status": "done", "verdict": "listed",
      "checked_at": "2026-11-03 09:12",
      "lists_checked": 80, "listed_count": 1, "error_count": 4,
      "listed_on": [
        { "list": "ZapBL RHSBL", "zone": "rhsbl.zapbl.net", "response": "127.0.0.2" }
      ]
    }
  ]
}

Webhook monitoring

Register a target and a callback_url with POST /api/v1/monitors. Each registered target is then checked once a day without using the cache (the first check runs within about a day of registering), and a JSON POST is sent to your URL only on the first check (monitor.initial) and when the verdict flips between listed and clean (monitor.status_changed).

curl -X POST https://dnsblchecker.com/api/v1/monitors \
  -H "X-API-Key: $DNSBL_API_KEY" -H "Content-Type: application/json" \
  -d '{"target":"example.com","callback_url":"https://your-app.example/hooks/dnsbl"}'
import hmac, hashlib
expected = "sha256=" + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
ok = hmac.compare_digest(expected, request.headers.get("X-DNSBL-Signature", ""))

Designing alerts: frequency, multiple lists and false alarms

A monitor is only useful if people still read its alerts after a few months. One that fires constantly soon gets ignored.

Frequency

Several lists

False positives and transient answers

What to do when a listing is detected

  1. Confirm it. Check the operator's own lookup page and note the list name, the time and any reason shown.
  2. Fix the cause first. Look for compromised accounts, abused web forms, infected machines, poor list hygiene and mismatched reverse DNS (PTR) or HELO names. Requesting removal while the cause remains usually leads to a new listing.
  3. Follow that list's removal process. The decision to delist belongs to each list operator, and filing a request is no promise that the listing will be lifted. If you do not control the IP, as with shared hosting, contact your hosting provider or ISP.
  4. Let the monitor confirm the result. Receivers also run their own filters, so keep an eye on delivery after the verdict returns to clean.

For a one-off look at an IP or domain, use the blacklist checker. If tracing the cause or handling the requests is more than you can take on, you can ask us for help. For the full troubleshooting path when mail is bouncing, see email blocked by blacklists.

If you would rather not handle this yourself

We introduce providers who take on delisting requests, consulting and advice (from JPY 50,000). Delisting is decided by each list operator and cannot be promised.

Get help

This site may receive a referral fee from listed providers. About

FAQ

I checked through 8.8.8.8 and got no listing. Am I clean?

Not necessarily. Spamhaus's FAQ says queries through free open resolvers such as Google Public DNS will in most cases get a not-listed (NXDOMAIN) reply, so a listed IP can look clean. Query from your own resolver, or use the operator's lookup page.

I got 127.255.255.254. Does that mean I am listed?

No. It is an error code returned for queries via a public or open resolver, and Spamhaus states it does not mean the object is listed. Log it as an error and do not send a listing alert.

Can I just call the batch API every day as my monitor?

A batch may return a stored result up to 30 days old, so it is not always today's state. Check checked_at, or use webhook monitoring (POST /api/v1/monitors), which runs a fresh check every day.

Should I request removal as soon as an alert fires?

Fix the cause first. A request made while the problem remains usually ends in a new listing. The decision always rests with the list operator.

Official pages used

The facts in this article were checked against the official pages below at the time of publication. Policies and behavior change without notice, so check the official pages before relying on anything important.

Related guides

DNSBL Return Codes Explained: 127.0.0.2, 127.255.255.x and NXDOMAIN

How to read DNSBL return codes: what 127.0.0.2 means under RFC 5782, codes for Spamhaus, SpamCop and UCEPROTECT, error codes, and NXDOMAIN vs SERVFAIL.

Spamhaus Removal: How to Get Your IP or Domain Delisted

Listed on Spamhaus? Find out which list you are on (ZEN, PBL, XBL, CSS, SBL, DBL), what the return codes mean, and the right delisting path for each.

Email Blacklisted? How to Read the Bounce and Get Off a Blacklist

Email blocked by a blacklist? Read the bounce code, check the causes in the right order, see which listings matter, and request removal the right way.

All guides →