v0.14.0
OpenAPI 3.1.1

Content Filter API

Introduction

The Content Filter API is a high-performance mail content filtering service that checks emails for spam, phishing, malware and other unwanted mail. It provides endpoints for checking emails, training the Bayes classifier, and querying historical mail check requests.

Authentication

All API endpoints require authentication via an API key passed in the Authorization header as a Bearer token:

Authorization: Bearer <API_KEY>
Server:https://content-filter.spamhaus.com

Production External

No authentication selected
Client Libraries

Mail (Collapsed)

​

Check Mail

​

Check an email for spam, phishing, malware and other unwanted mail. The email content must be provided as a base64-encoded string in the body field. The response gives the action to take (accept or reject), the class of the email and a confidence.

Testing the Filters

You can verify your integration end-to-end using well-known, deterministic test signals that flow through the real filtering pipeline:

  • Anti-malware: attach the EICAR test file (eicar.com) to the email. The malware scanner detects it and returns a reject verdict.
  • Anti-spam: include a URL such as https://www.dbltest.com in the email body. The Spamhaus DBL test domain triggers a reject verdict from the spam filter.

Body·

required
application/json

The email to check, including SMTP metadata and the base64-encoded email body.

Request body for checking an email against spam filters.

  • The raw email content, base64-encoded. The email may be up to 50MiB (52,428,800 bytes) once decoded; a larger one is rejected with a 400. The whole request body is limited to 75MiB (78,643,200 bytes), which holds a 50MiB email in base64 plus the other request fields.

  • The client IP address of the mail sender.

  • The HELO/EHLO string from the SMTP transaction.

  • An array of recipient email addresses (minimum 1).

  • The per-user Bayes classifier learns which words and other tokens are typical of the mail you label as spam or ham through POST /mail/learn, and its verdict feeds into the POST /mail/check decision (shown as analysis.ml.user on GET /mail/requests/{request_id}). bias_db_id selects which of your Bayes token databases it uses. Tokens written by POST /mail/learn are stored in the database identified by bias_db_id, and POST /mail/check reads tokens from the same database when scoring.

    Use it to keep separate Bayes databases for your own end users - for example, pass each end user's UUID so their training never influences anyone else's results. You can also switch to a new value to start from scratch when a database's predictions become unreliable, for example when its training data is suspected to be corrupted or poisoned; the old database is left untouched.

    1-64 characters of A-Z, a-z, 0-9, ., : or -. The value is case-sensitive and used exactly as sent, so ABC and abc select different databases - always send a given identifier in the same case.

    Each database needs 200 learned samples before it contributes to scoring, so splitting your training across many databases delays that point for each of them.

    Optional. Defaults to 0 when omitted. Do not send together with the deprecated bias_db_version; supplying both is rejected with a 400. bias_db_version: 3 and bias_db_id: "3" select the same database, so you can switch fields without losing existing training data.

  • Deprecated - use bias_db_id, which accepts string identifiers such as UUIDs.

    Integer form of bias_db_id: bias_db_version: 3 selects the same Bayes token database as bias_db_id: "3". Still accepted, but must be a JSON integer - a string value is rejected with a 400 that points to bias_db_id. Do not send together with bias_db_id.

    Optional. Defaults to 0 when omitted.

  • The direction of email flow (inbound or outbound) is used to ensure only relevant rules are applied. If omitted, inbound is assumed.

    values
    • inbound

      Mail arriving at the protected system; inbound rules are applied.

    • outbound

      Mail leaving the protected system; outbound rules are applied.

  • The sender email address.

  • The reverse DNS hostname of the sending IP.

Responses

  • application/json
  • application/json
  • application/json
  • application/json
  • application/json
  • application/json
  • application/json
  • application/json
Request Example for post/api/v1/mail/check
curl https://content-filter.spamhaus.com/api/v1/mail/check \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN' \
  --data '{
  "client_ip": "192.168.1.100",
  "mail_from": "sender@example.com",
  "helo": "mail.example.com",
  "rdns": "mail.example.com",
  "rcpts": [
    "recipient@example.com"
  ],
  "bias_db_id": "0b8f3c1e-7d2a-4f6b-9a51-3e2c8d4f7a90",
  "flow": "inbound",
  "body": "RnJvbTogc2VuZGVyQGV4YW1wbGUuY29tDQpUbzogcmVjaXBpZW50QGV4YW1wbGUuY29tDQo="
}'
Email classified as ham (accepted).
{
  "action": "accept",
  "class": "ham",
  "request_id": "3c159060-00e4-4320-a434-adddf323562b",
  "diagnostic": "U0hfRElBR19FWEFNUEXF...",
  "confidence": 0.93,
  "scan_duration_ms": 26
}

Learn Mail

​

Submit a labelled email sample to train the per-user Bayes classifier. The email content must be provided as a base64-encoded string in the body field. The class field indicates whether the sample is spam or ham.

Both learned and duplicate responses are successful outcomes (HTTP 200):

  • learned: The sample was incorporated into the Bayes classifier.
  • duplicate: The sample was already known and the classifier was unchanged.

Body·

required
application/json

The labelled email sample to learn.

Request body for submitting a labelled email sample to train the per-user Bayes classifier.

Supply exactly one of body or diagnostic - never both, never neither:

  • body carries a full raw email to train on. Use this when uploading an email sample manually.
  • diagnostic carries the opaque diagnostic value returned by a previous POST /mail/check call. The server reconstructs a skeleton email from the embedded tokens and feeds that to the trainer, so the original raw email does not need to be retained.

A request that supplies both fields, or neither, is rejected with a 400 (exactly one of 'body' or 'diagnostic' must be supplied).

  • The classification label for the email sample.

    values
    • spam

      Train the sample as spam (unwanted mail).

    • ham

      Train the sample as ham (legitimate mail).

  • The per-user Bayes classifier learns which words and other tokens are typical of the mail you label as spam or ham through POST /mail/learn, and its verdict feeds into the POST /mail/check decision (shown as analysis.ml.user on GET /mail/requests/{request_id}). bias_db_id selects which of your Bayes token databases it uses. Tokens written by POST /mail/learn are stored in the database identified by bias_db_id, and POST /mail/check reads tokens from the same database when scoring.

    Use it to keep separate Bayes databases for your own end users - for example, pass each end user's UUID so their training never influences anyone else's results. You can also switch to a new value to start from scratch when a database's predictions become unreliable, for example when its training data is suspected to be corrupted or poisoned; the old database is left untouched.

    1-64 characters of A-Z, a-z, 0-9, ., : or -. The value is case-sensitive and used exactly as sent, so ABC and abc select different databases - always send a given identifier in the same case.

    Each database needs 200 learned samples before it contributes to scoring, so splitting your training across many databases delays that point for each of them.

    Optional. Defaults to 0 when omitted. Do not send together with the deprecated bias_db_version; supplying both is rejected with a 400. bias_db_version: 3 and bias_db_id: "3" select the same database, so you can switch fields without losing existing training data.

  • Deprecated - use bias_db_id, which accepts string identifiers such as UUIDs.

    Integer form of bias_db_id: bias_db_version: 3 selects the same Bayes token database as bias_db_id: "3". Still accepted, but must be a JSON integer - a string value is rejected with a 400 that points to bias_db_id. Do not send together with bias_db_id.

    Optional. Defaults to 0 when omitted.

  • The raw email content, base64-encoded. The email may be up to 50MiB (52,428,800 bytes) once decoded; a larger one is rejected with a 400. The whole request body is limited to 75MiB (78,643,200 bytes). Mutually exclusive with diagnostic.

  • The opaque diagnostic value returned by a previous POST /mail/check. The server reconstructs a skeleton email from the embedded tokens and trains the Bayes classifier on that. Mutually exclusive with body.

Responses

  • application/json
  • application/json
  • application/json
  • application/json
  • application/json
  • application/json
  • application/json
Request Example for post/api/v1/mail/learn
curl https://content-filter.spamhaus.com/api/v1/mail/learn \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN' \
  --data '{
  "class": "spam",
  "bias_db_id": "0b8f3c1e-7d2a-4f6b-9a51-3e2c8d4f7a90",
  "body": "RnJvbTogc2VuZGVyQGV4YW1wbGUuY29tDQpUbzogcmVjaXBpZW50QGV4YW1wbGUuY29tDQo=",
  "diagnostic": "U0hfRElBR19FWEFNUEXF..."
}'
Sample was incorporated into the classifier.
{
  "action": "learned",
  "request_id": "51e9caa0-27b2-407f-acb2-9d453d96eab2"
}

Get Request

​

Returns the full details of a single mail check request, including an analysis of what the filter observed about the message.

Analysis

The analysis object explains the verdict in terms of documented codes rather than internal rule names, so it can be displayed or acted upon without any knowledge of the filtering engine. It has three parts.

Findings

analysis.findings lists what the filter observed about the connection and about the message's authentication, one entry per observation. Each entry carries:

  • code - a stable identifier of the observation; every code is documented on the Finding schema.
  • category - connection (the sending network and IP, its sending history, the HELO and reverse-DNS checks) or authentication (SPF, DKIM, DMARC and ARC).
  • severity - how to read it: good, info, warning or bad.

Findings are returned in a fixed display order. Where several checks describe the same fact on a scale (the network's reputation, the IP's sending history) only the applicable tier is reported, and a correct forward-confirmed reverse DNS is reported as the single helo_fcrdns_match finding rather than its component checks. New codes may be added over time - treat an unknown code as informational.

Listings

analysis.listings has one entry for each element of the message that was found on a Spamhaus dataset:

  • element - which part of the message was listed (the sending IP, a URL in the body, the sender domain, an attachment, ...).
  • dataset - the Spamhaus dataset it was listed on (ZEN, DBL, HBL, ...).
  • tag - an optional refinement: the reason category for DBL and DBLAL listings, the verdict for HBL attachment listings.
  • value - the listed value itself, which can be looked up at https://check.spamhaus.org.
  • origin - where the value was derived from when it is not directly visible in the message, for example the URL a listed hash was computed over.

tag and origin are omitted when absent. element and dataset are omitted for a listing this API does not yet know how to describe; the value is still reported.

Machine learning

analysis.ml reports the machine-learning verdicts as confidences in the range 0..1, rounded to two decimal places: global for the shared Bayes classifier, user for your per-user Bayes classifier trained through POST /api/v1/mail/learn, and fuzzy for matches against fingerprints of known spam or known legitimate mail. Each carries a spam and/or a ham side; a side is omitted when the classifier did not lean that way, and an empty object means it produced no verdict.

Score

analysis.score is the filter's overall score for the message, normalised into the range 0..15 - higher means more spam-like. It summarises the findings, listings and machine-learning verdicts into one figure and is omitted when the filter recorded no score for the request.

The verdict itself is carried by action and class, its confidence by confidence, and the training payload for POST /api/v1/mail/learn by diagnostic.

Path Parameters

  • The unique identifier of the mail check request (UUID v4).

Responses

  • application/json
  • application/json
  • application/json
  • application/json
  • application/json
  • application/json
  • application/json
Request Example for get/api/v1/mail/requests/{request_id}
curl https://content-filter.spamhaus.com/api/v1/mail/requests/51e9caa0-27b2-407f-acb2-9d453d96eab2 \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN'
Accepted email, classified clean.
{
  "request_id": "51e9caa0-27b2-407f-acb2-9d453d96eab2",
  "event_time": "2026-03-06T18:00:00Z",
  "action": "accept",
  "class": "clean",
  "confidence": 0.74,
  "client_ip": "192.168.1.100",
  "mail_from": "sender@example.com",
  "helo": "mail.example.com",
  "rdns": "mail.example.com",
  "rcpts": [
    "recipient@example.com"
  ],
  "message_id": "20261013.abc123@example.com",
  "analysis": {
    "findings": [
      {
        "code": "network_reputation_good",
        "category": "connection",
        "severity": "good"
      },
      {
        "code": "sending_history_very_good",
        "category": "connection",
        "severity": "good"
      },
      {
        "code": "helo_fcrdns_match",
        "category": "connection",
        "severity": "good"
      },
      {
        "code": "spf_pass",
        "category": "authentication",
        "severity": "good"
      },
      {
        "code": "dkim_pass",
        "category": "authentication",
        "severity": "good"
      },
      {
        "code": "dmarc_pass",
        "category": "authentication",
        "severity": "good"
      }
    ],
    "listings": [],
    "ml": {
      "global": {
        "ham": 0.98
      },
      "user": {},
      "fuzzy": {}
    },
    "score": 1.5
  },
  "diagnostic": "U0hfRElBR19FWEFNUEXF..."
}

Search Requests

​

Search historical mail check requests. Returns request-level summaries (no per-rule details). Results are ordered by event_time descending.

Time filtering: Use either relative for a sliding time window (e.g. 1h, 24h) or start/end for an absolute range. These are mutually exclusive. If neither is provided, defaults to the last 24 hours.

Pagination: Use cursor-based pagination. The response includes a next_cursor field when more results exist; pass its value as the cursor query parameter to fetch the next page. Keep all other query parameters (time range and filters) identical across page requests - the cursor encodes only a position in the result order, not your query. Changing the time range or filters while paging yields undefined page boundaries; start a new search instead.

Query Parameters

  • Time window relative to now. Supports Go duration strings plus d (days) and w (weeks) suffixes (e.g. 30s, 5m, 1h, 24h, 2d, 1w). Maximum 30 days. Mutually exclusive with start/end. If neither relative nor start/end is provided, defaults to the last 24 hours.

  • Start of absolute time window (RFC3339). Must be paired with end. Mutually exclusive with relative.

  • End of absolute time window (RFC3339). Must be paired with start. Mutually exclusive with relative.

  • Filter results by email classification.

    values
    • ham

      Legitimate mail with no more specific class.

    • spam

      Unsolicited bulk mail.

    • bounce

      Bounce messages (NDRs, DSNs).

    • clean

      Good, legitimate mail - includes person-to-person.

    • commercial

      Mass commercial mail (newsletters etc).

    • community:mailinglist

      Mailing list (aka Listserv) discussions.

    • community:social

      Updates from social networks.

    • malware

      Malspam - links to or attachments with malicious content.

  • Filter results by action taken.

    values
    • accept

      The email passed the spam and content checks and should be delivered.

    • reject

      The email was flagged as spam or malicious and should not be delivered.

  • Only return requests whose verdict confidence (the confidence field, 0..1) is at least this value. Confidence is in the verdict itself, whichever class it is: a confident ham and a confident spam both pass a high minimum. Requests with no confidence (confidence: null) never match a confidence filter.

  • Only return requests whose verdict confidence (the confidence field, 0..1) is at most this value - for example 0.6 to find borderline verdicts worth reviewing. Requests with no confidence (confidence: null) never match a confidence filter.

  • Filter by client IP address of the mail sender.

  • Filter by sender email address (MAIL FROM).

  • Filter by the HELO/EHLO string presented by the sending MTA.

  • Filter by the reverse DNS hostname of the sending IP.

  • Filter by a single recipient email address. Matches requests that include this address in their recipients list.

  • Filter by the email's Message-ID header, without the surrounding angle brackets. Matches exactly (case-sensitive). Only requests checked after Message-ID capture was introduced carry one, and the same message delivered to several recipients can match several requests.

  • Filter by the direction of the email, as given in the flow field of the check request.

    values
    • inbound

      Mail arriving at the protected system.

    • outbound

      Mail leaving the protected system.

  • Maximum number of results to return.

  • Opaque pagination cursor returned as next_cursor in the previous response. Omit for the first page.

Responses

  • application/json
  • application/json
  • application/json
  • application/json
  • application/json
  • application/json
  • application/json
Request Example for get/api/v1/mail/requests
curl 'https://content-filter.spamhaus.com/api/v1/mail/requests?relative=1h&start=2026-03-06T17%3A00%3A00Z&end=2026-03-06T18%3A00%3A00Z&class=ham&action=accept&min_confidence=0.9&max_confidence=0.6&client_ip=192.168.1.100&mail_from=sender%40example.com&helo=mail.example.com&rdns=mail.example.com&rcpt=recipient%40example.com&message_id=20261013.abc123%40example.com&flow=inbound&limit=100&cursor=MjAyNi0wMy0wNlQxODowMDowMFp8NTFlOWNhYTAtMjdiMi00MDdmLWFjYjItOWQ0NTNkOTZlYWIy' \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN'
One page of results with a further page available.
{
  "meta": {
    "next_cursor": "MjAyNi0wMy0wNlQxODowMDowMFp8NTFlOWNhYTAtMjdiMi00MDdmLWFjYjItOWQ0NTNkOTZlYWIy"
  },
  "results": [
    {
      "request_id": "6c93ff8e-063f-4c39-b22f-b341de23a7c0",
      "event_time": "2026-03-06T18:00:01Z",
      "action": "reject",
      "class": "spam",
      "confidence": 0.97,
      "client_ip": "192.168.1.100",
      "mail_from": "sender@example.com",
      "helo": "mail.example.com",
      "rdns": "mail.example.com",
      "rcpts": [
        "recipient@example.com"
      ]
    },
    {
      "request_id": "51e9caa0-27b2-407f-acb2-9d453d96eab2",
      "event_time": "2026-03-06T18:00:00Z",
      "action": "accept",
      "class": "clean",
      "confidence": 0.82,
      "client_ip": "192.168.1.100",
      "mail_from": "sender@example.com",
      "helo": "mail.example.com",
      "rdns": "mail.example.com",
      "rcpts": [
        "recipient@example.com"
      ]
    }
  ]
}

Models