Security

SPF Record Lookup API: Find SPF TXT Records in JSON

Retrieve SPF policy from DNS TXT records, inspect common mechanisms, and detect missing, duplicate, or dangerously permissive sender rules.

June 8, 202611 min readSecurity · DNS · Email Authentication · SPF

Introduction

SPF mistakes often stay invisible until legitimate mail starts landing in spam or an old vendor remains authorized to send on your domain. A missing policy, duplicate SPF records, or a permissive +all rule can turn a small DNS oversight into a deliverability and spoofing problem.

WhoisJSON does not expose a separate SPF-only endpoint. Use the DNS Lookup API /nslookup endpoint, read the TXT array, and select values beginning with v=spf1. The result is structured JSON that your application can inventory, parse, compare, and monitor.

What Is an SPF Record?

Sender Policy Framework, or SPF, is an email authentication policy published as a DNS TXT record. It describes which hosts or delegated services are allowed to send mail using a domain in the SMTP envelope sender.

Example SPF policyDNS
example.com. 3600 IN TXT
"v=spf1 include:_spf.google.com ip4:192.0.2.0/24 -all"

The record starts with v=spf1, followed by mechanisms and modifiers. Receiving mail systems evaluate those terms during SPF authentication. The DNS response itself gives you the published policy; complete SPF evaluation requires additional recursive DNS resolution and SMTP context.

The protocol behavior is defined in RFC 7208, including record selection, qualifiers, modifiers, and DNS lookup limits.

SPF Lookup API vs SPF Validation API

The distinction matters because a TXT lookup and a complete SPF validator answer different questions.

WorkflowWhat it doesWhoisJSON scope
SPF record lookupRetrieves root-domain TXT records and identifies values beginning with v=spf1.Supported through /nslookup.
First-level parsingSplits the published policy into mechanisms such as include, ip4, ip6, a, mx, redirect, and all.Performed client-side using examples in this guide.
Full SPF validationRecursively resolves includes and redirects, counts DNS-triggering terms, expands macros, and evaluates a sender IP.Not returned by the documented endpoint.
Keep the result precise. Finding a syntactically plausible v=spf1 string is not the same as proving that the policy passes full SPF evaluation.

Query SPF TXT Records in JSON

Authenticate with Authorization: TOKEN=YOUR_API_KEY and query the root domain through /nslookup.

RequestcURL
curl "https://whoisjson.com/api/v1/nslookup?domain=example.com" \
  -H "Authorization: TOKEN=YOUR_API_KEY"

The documented response exposes TXT as an array of strings. SPF is one possible use of TXT, so filter the array instead of treating every TXT value as sender policy.

Response shapeJSON
{
  "TXT": [
    "google-site-verification=abc123",
    "v=spf1 include:_spf.google.com ip4:192.0.2.0/24 -all"
  ],
  "MX": [
    {
      "exchange": "mail.example.com",
      "priority": 10
    }
  ],
  "DMARC": [
    "v=DMARC1; p=reject"
  ]
}

How to Interpret Common SPF Mechanisms

A first-level parser can make the raw policy easier to review without claiming to perform complete SPF evaluation.

TermMeaningReview question
include:provider.exampleDelegates authorization to another domain's SPF policy.Is this provider still approved to send mail?
ip4:192.0.2.0/24Authorizes an IPv4 address or range.Is the range expected and appropriately narrow?
ip6:2001:db8::/32Authorizes an IPv6 address or range.Does the organization actually send from this range?
aUses the domain's address records during SPF evaluation.Should web-hosting addresses also send email?
mxUses the domain's MX hosts during evaluation.Are inbound mail servers also intended senders?
redirect=policy.exampleRedirects evaluation to another domain's policy.Is the delegated policy controlled and current?
-allHard fail for senders not matched earlier.Is the authorized sender inventory complete?
~allSoft fail for unmatched senders.Is this a migration state or the intended long-term policy?
+allExplicitly allows every sender.Usually a critical misconfiguration.

Qualifiers can also appear before mechanisms: + means pass, - means fail, ~ means soft fail, and ? means neutral. The absence of an explicit qualifier is equivalent to +.

Python Example: Find and Parse SPF

This example finds SPF-like TXT values and extracts first-level terms. It deliberately does not resolve include or redirect targets.

spf_lookup.pyPython
import requests

API_KEY = "YOUR_API_KEY"
BASE_URL = "https://whoisjson.com/api/v1"


def parse_spf(record: str) -> dict:
    terms = record.split()
    return {
        "record": record,
        "terms": terms[1:],
        "includes": [
            term.split(":", 1)[1]
            for term in terms
            if term.lstrip("+~-?").startswith("include:")
        ],
        "redirect": next(
            (
                term.split("=", 1)[1]
                for term in terms
                if term.startswith("redirect=")
            ),
            None,
        ),
        "all": next(
            (
                term for term in terms
                if term.lstrip("+~-?") == "all"
            ),
            None,
        ),
    }


def lookup_spf(domain: str) -> dict:
    response = requests.get(
        f"{BASE_URL}/nslookup",
        headers={"Authorization": f"TOKEN={API_KEY}"},
        params={"domain": domain},
        timeout=10,
    )
    response.raise_for_status()
    txt_records = response.json().get("TXT") or []
    spf_records = [
        str(value).strip()
        for value in txt_records
        if str(value).strip().lower().startswith("v=spf1")
    ]

    return {
        "domain": domain,
        "status": (
            "missing" if not spf_records
            else "duplicate" if len(spf_records) > 1
            else "present"
        ),
        "records": [parse_spf(record) for record in spf_records],
    }


print(lookup_spf("example.com"))

Node.js Example: Flag Basic SPF Risks

spf-lookup.jsNode.js
const API_KEY = "YOUR_API_KEY";
const BASE_URL = "https://whoisjson.com/api/v1";

async function lookupSpf(domain) {
  const url = new URL(`${BASE_URL}/nslookup`);
  url.searchParams.set("domain", domain);

  const response = await fetch(url, {
    headers: { Authorization: `TOKEN=${API_KEY}` },
  });
  if (!response.ok) {
    throw new Error(`DNS lookup failed: ${response.status}`);
  }

  const data = await response.json();
  const records = (data.TXT ?? [])
    .map(String)
    .map((value) => value.trim())
    .filter((value) =>
      value.toLowerCase().startsWith("v=spf1")
    );

  return {
    domain,
    status:
      records.length === 0
        ? "missing"
        : records.length > 1
          ? "duplicate"
          : "present",
    records,
    findings: [
      ...(records.some((value) =>
        /(^|\s)\+all(\s|$)/i.test(value)
      )
        ? ["policy allows every sender with +all"]
        : []),
      ...(records.some((value) =>
        /(^|\s)~all(\s|$)/i.test(value)
      )
        ? ["policy ends in soft fail (~all)"]
        : []),
    ],
  };
}

lookupSpf("example.com")
  .then(console.log)
  .catch(console.error);

Common SPF Findings

Missing SPF

No root TXT value begins with v=spf1. Confirm whether the domain sends email before assigning severity.

Multiple SPF records

More than one v=spf1 policy creates ambiguity and should be consolidated into one record.

Stale include

A former mail or marketing provider remains delegated after the service was removed.

Permissive all

+all authorizes every sender. ~all is less strict than -all and may be intentional during rollout.

Context matters. A domain that never sends email can intentionally publish v=spf1 -all to state that no sender is authorized.

SPF vs MX vs DKIM vs DMARC

ControlMain questionWhoisJSON data
MXWhere does the domain receive incoming email?MX exchange and priority through /nslookup.
SPFWhich infrastructure may send using the envelope domain?Raw root-domain TXT records; filter for v=spf1.
DKIMWas the message signed by an authorized selector?No dedicated selector-discovery endpoint.
DMARCWhat should receivers do when aligned authentication fails?DMARC policy through /nslookup.

For inbound routing, use the MX record lookup guide. For policy enforcement and alignment context, use the DMARC lookup API guide.

Bulk SPF Audits for Domain Portfolios

One /nslookup request per domain returns TXT together with MX, DMARC, MTA-STS, TLS-RPT, and other DNS records. That makes a scheduled SPF inventory useful for provider migrations, vendor reviews, customer-domain onboarding, and domain portfolio governance.

  • Store the exact SPF string and timestamp so policy changes are reviewable.
  • Flag missing and duplicate records separately; they are different operational failures.
  • Compare include and redirect targets with an approved provider inventory.
  • Treat +all as urgent and ~all as a review item, not automatic proof of compromise.
  • Use bounded concurrency and retry only transient API failures, not valid empty TXT results.

For production retry patterns, see the rate limits and retries guide.

What This SPF Lookup Cannot Prove

It does not evaluate a sender IP. Full SPF evaluation needs the SMTP identity and source IP.
It does not resolve includes recursively. The examples inspect the published root policy only.
It does not calculate the SPF DNS lookup limit. Counting DNS-triggering mechanisms correctly requires recursive policy resolution.
It does not guarantee delivery. DKIM, DMARC alignment, reputation, content, and recipient policy also affect email delivery.

FAQ

What is an SPF record lookup API?

It retrieves DNS TXT records for a domain and identifies the sender policy value that begins with v=spf1.

Which WhoisJSON endpoint returns SPF?

Use GET /api/v1/nslookup with the domain parameter, then filter the TXT array for values beginning with v=spf1.

Does WhoisJSON fully validate SPF?

No. The documented endpoint returns TXT records. Recursive include resolution, DNS lookup counting, macro expansion, and sender-IP evaluation are outside this lookup workflow.

What is the difference between -all and ~all?

-all is a hard-fail qualifier for unmatched senders, while ~all is a soft-fail qualifier commonly used during gradual deployment.

Can I query SPF, MX, and DMARC together?

Yes. One /nslookup response can include TXT, MX, DMARC, BIMI, MTASTS, TLSRPT, and standard DNS records.

Conclusion

An SPF record lookup API workflow turns a raw DNS sender policy into data your application can inventory and review. Query /nslookup, filter TXT values for v=spf1, and parse the first-level mechanisms that matter to your provider and security baseline.

Keep lookup and validation separate. WhoisJSON returns the published TXT policy; it does not recursively evaluate every include or decide whether a specific sending IP passes SPF. That boundary makes the result predictable and honest.

Check SPF TXT records with WhoisJSON

Query SPF, MX, DMARC, MTA-STS, TLS-RPT, and standard DNS records with one API key.

Check SPF RecordsView Documentation
Sender Policy

Audit SPF Policy in One DNS Response

Retrieve TXT records together with MX, DMARC, MTA-STS, TLS-RPT, and standard DNS data.

SPF TXT lookupFirst-level parsingPython and Node.js1,000 free requests/month