Integrations
Tested filter configuration for the blocklist. Every directory ships a test suite that runs against the real filter in Docker — not a mock. Querying is free, and a delist request needs no account. If you are designing policy around feeds, start with our pages on domain reputation, IPv6 intelligence, and ASN reputation.
The DNSBL path needs nothing installed and works with whatever already does RBL lookups.
smtpd_recipient_restrictions =
permit_mynetworks,
permit_sasl_authenticated,
reject_unauth_destination,
reject_rbl_client dnsbl.vspam.orgPrefer canonical phishing domains and exact IPv4 or IPv6 hosts for enforcement. These are the cleanest publication surfaces for SMTP and firewall policy.
Treat IPv6 prefix watch and ASN reputation as triage and policy-tuning signals unless you have stronger direct evidence for a host or domain.
Feed thresholds, delist handling, and false-positive trends are public. Check status, statistics, and delist policy before broad rollout.
Worried about false positives? Anyone can file a delist request without an account. Every request is reviewed by a human within 48 hours and tracked publicly by ID. Approval removes the DNSBL publication and marks the report a false positive in our scoring history.
Delist policy →Recommended: vspam-agent
Lightweight Go daemon with local caching, fail-open design, and automatic threat reporting. Supports Postfix natively and any MTA via HTTP check API (Exim, OpenSMTPD, Sendmail, etc.).
Firewall & Network Devices
Plain-text blocklists for Palo Alto, FortiGate, pfSense, Juniper SRX, iptables, MikroTik, and more.
Postfix defaults to 451 4.3.5 Server configuration problem on a 100-second timeout. If you use the agent’s policy service without changing that, the day the agent is restarting, upgrading or wedged, Postfix defers every inbound message — 100 seconds at a time — until someone notices.
smtpd_policy_service_default_action = DUNNO smtpd_policy_service_timeout = 10s
Every URL in the message body, a symbol per category, and the suspicious-but-not-listed verdict a DNS return code cannot express.
Three lookup surfaces and seven scored metas, kept current by a signed update channel.
The agent across a fleet, with a Postfix failure mode that lets mail through.
Compose override plus an rspamd drop-in, for mailcow deployments.
Set it up
about 2 minutesOne line in your resolver. No account, nothing to install, and it checks the connecting IP and the sender.
The DNSBL path is three lines in main.cf. The agent adds policy delegation, a local cache, and reporting.
smtpd_recipient_restrictions =
...
reject_rbl_client dnsbl.vspam.orgdig +short 19.72.234.185.dnsbl.vspam.org
# 127.0.0.2 means listed (IP listings always answer .2); no answer means not listedBlocking something you should not be? Delist requests are reviewed by a human within 48 hours, need no account, and are tracked publicly by ID. Check what we hold on a host first with lookup.
More Than One Server
The builder above configures one host. For a fleet there is an Ansible role that installs the agent from the signed package repository, writes its config, starts it, waits until it actually answers, and — if you ask it to — points Postfix at it with a failure mode that lets mail through rather than deferring it.
git clone https://github.com/vspam-org/integrations
cd integrations/ansible
cp inventory.example.ini inventory.ini # edit
ansible-playbook -i inventory.ini playbook.ymlIdempotent, so it is safe in a nightly run. Debian, Ubuntu, RHEL, Rocky and Alma. Role and variables.
DNSBL return codes
dnsbl.vspam.orgMatching anywhere in 127.0.0.0/8 treats every listing alike, which is the safe default. Category subzones are filtered views of that same list, so a subzone only returns anything for indicators observed in that category — several are defined but sparsely populated today, and an empty answer is not a fault. Phishing is by far the largest category.
Adding a subzone check gains you nothing over the main zone unless you intend to score that category differently.
The codes and subzones below apply to domains. IP listings carry no category: they are published in dnsbl.vspam.org only and always answer 127.0.0.2.
dig +short 19.72.234.185.dnsbl.vspam.org # 127.0.0.2 → listed (IP listings always answer .2) # no answer → not listed
curl "api.vspam.org/api/v1/rbl/check?ip=2001:db8::25"Docker, plus ansible-playbook for the Ansible suite. Nothing else. They run against real filters because untested rules do not belong in anyone’s mail path.
./rspamd/test/run-tests.sh ./spamassassin/test/run-tests.sh --channel ./ansible/test/run-tests.sh
Security Tools
Auto-report banned IPs to vspam.org when Fail2Ban triggers an action.
# /etc/fail2ban/action.d/vspam.conf
[Definition]
actionban = curl -s -X POST https://api.vspam.org/api/v1/reports \
-H "X-API-Key: <YOUR_KEY>" \
-H "Content-Type: application/json" \
-d '{"ioc_type":"ip","ioc_value":"<ip>","category":"spam","evidence":"Fail2Ban: <name> jail"}'Pull operator-focused feeds such as exact IPv6 hosts, phishing domains, or IPv6 prefix watchlists and apply them directly to your firewall ruleset.
# Download and apply IPv6 exact-host blocklist
curl -s -H "X-API-Key: <KEY>" https://api.vspam.org/api/v1/feeds/ipv6-exact-host-malicious/csv \
| tail -n +2 | cut -d',' -f2 | grep ':' \
| while read ip; do iptables -A INPUT -s "$ip" -j DROP; doneThreat Feeds
Pull operator-focused feeds for phishing domains, exact IPv6 hosts, IPv6 prefix watch, and ASN abuse density in structured formats for OpenCTI, MISP, SIEM, or custom pipelines. CSV, JSON, and plain-text downloads are public. STIX and MISP are Pro, and TAXII is Enterprise.
GET /api/v1/feeds/stixGET /api/v1/feeds/mispGET /api/v1/feeds/domain-high-confidence-phish/csvGET /api/v1/feeds/ipv6-exact-host-malicious/jsonGET /api/v1/feeds/ipv6-prefix-watch/txtGET /api/v1/feeds/asn-high-abuse-density/jsonGET /taxii2/Use block feeds for direct enforcement, watch feeds for escalation and guardrails, and context feeds for correlation and prioritization. The methodology and publication thresholds are visible in the feed catalog.
API Quick Start
Common curl examples — replace <KEY> with your API key from the account page.
# Check an IP
curl https://api.vspam.org/api/v1/rbl/check?ip=192.0.2.1
# Check a phishing domain
curl "https://api.vspam.org/api/v1/public/operator-lookup?type=domain&value=login-paypa1.com"
# Check ASN reputation context
curl "https://api.vspam.org/api/v1/public/operator-lookup?type=asn&value=AS13335"
# Search reports
curl https://api.vspam.org/api/v1/public/reports?q=example.com
# Submit a phishing report (URLs normalize to canonical domains at ingest)
curl -X POST https://api.vspam.org/api/v1/reports \
-H "X-API-Key: <KEY>" \
-d '{"ioc_type":"url","ioc_value":"https://phish.example.com","category":"phishing"}'SDK Examples
Query the vspam.org API from your application. Replace <KEY> with your API key.
import requests
API = "https://api.vspam.org/api/v1"
KEY = "<KEY>"
# Check a phishing domain
domain_lookup = requests.get(
f"{API}/public/operator-lookup",
params={"type": "domain", "value": "login-paypa1.com"},
)
print(domain_lookup.json())
# Check an exact IPv6 host
ipv6_lookup = requests.get(
f"{API}/rbl/check",
params={"ip": "2001:db8::42"},
)
print(ipv6_lookup.json())
# Submit a report
requests.post(f"{API}/reports",
headers={"X-API-Key": KEY},
json={"ioc_type": "url",
"ioc_value": "https://phish.example.com",
"category": "phishing"})package main
import (
"fmt"
"io"
"net/http"
)
func main() {
domainResp, _ := http.Get(
"https://api.vspam.org/api/v1/public/operator-lookup?type=domain&value=login-paypa1.com")
defer domainResp.Body.Close()
domainBody, _ := io.ReadAll(domainResp.Body)
fmt.Println(string(domainBody))
ipResp, _ := http.Get(
"https://api.vspam.org/api/v1/rbl/check?ip=2001:db8::42")
defer ipResp.Body.Close()
ipBody, _ := io.ReadAll(ipResp.Body)
fmt.Println(string(ipBody))
}const API = "https://api.vspam.org/api/v1";
const KEY = "<KEY>";
// Check a canonical domain
const domainRes = await fetch(
`${API}/public/operator-lookup?type=domain&value=login-paypa1.com`);
console.log(await domainRes.json());
// Check an exact IPv6 host
const ipv6Res = await fetch(
`${API}/rbl/check?ip=2001:db8::42`);
console.log(await ipv6Res.json());
// Submit a report
await fetch(`${API}/reports`, {
method: "POST",
headers: {
"X-API-Key": KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
ioc_type: "domain",
ioc_value: "phish.example.com",
category: "phishing",
}),
});$api = "https://api.vspam.org/api/v1"
# Check a phishing domain
Invoke-RestMethod "$api/public/operator-lookup?type=domain&value=login-paypa1.com"
# Check an exact IPv4 or IPv6 host
Invoke-RestMethod "$api/rbl/check?ip=192.0.2.1"
# Submit a report
$headers = @{ "X-API-Key" = "<KEY>" }
$body = @{
ioc_type = "ip"
ioc_value = "185.234.72.19"
category = "spam"
} | ConvertTo-Json
Invoke-RestMethod "$api/reports" `
-Method Post -Headers $headers `
-Body $body -ContentType "application/json"Say So On Your Site
A public SVG endpoint, no key needed, cached for an hour. Reporters get a second badge carrying their confirmed count — the snippet is on your account.
[](https://vspam.org/)If we get one wrong
Reviewed within 48hNo account, no fee, reviewed by a human. False positives are the failure mode that matters and they are only visible to the person affected — so telling us is the contribution.
Licence
Filtering your own mail costs nothing, however much of it there is — commercially or not, no registration. Redistributing the data (mirroring zones, repackaging, reselling, embedding it in a product) needs a licence.
Need help? Check the API documentation or FAQ. For SIEM and threat intel platform guides, see SIEM Integrations and Threat Intel Platforms.