Pre-Black Friday: 20% off, for as long as you stayEARLYBFon Solo and AgencyEnds Nov 28Start free

How-to guide

How to Set Up an SPF Record (Publish, Test and Troubleshoot for Outbound Email)

Publish one TXT record, count its DNS lookups, prove a send passes, fix permerror, then check the list you send to.

Get your free API key →Free to start. No credit card. 1,000 records to spend whenever you like.

SPF is one TXT record that lists the servers allowed to send mail as your domain.

Most failures come from three mistakes: two records, a missing sender, or more than 10 DNS lookups. This guide publishes the record, tests it, and fixes each of those. For the definition, see what SPF is.

TL;DR

  • SPF takes about 30 minutes to set up and test, plus DNS propagation.
  • You inventory every sender, publish one v=spf1 TXT record, count its lookups, and prove a real message passes.
  • It spends 0 LeadOcean records. The list check at the end uses a free count.

Prerequisites

  • Edit access to the DNS zone of your sending domain.
  • A shell with dig.
  • A mailbox you own at another provider, to read the result headers.
  • A LeadOcean API key in $LEADOCEAN_API_KEY for Step 6. The free plan covers it.
  • A list of every system that sends mail as your domain: mailboxes, sequencer, help desk, product.

acme.com stands for your domain. Replace it everywhere.

Step 1: Check what is already published

A domain may have only one SPF record. RFC 7208 says that if more than one v=spf1 record exists, the result is permerror (RFC 7208, September 2026). Look before you add anything.

bash
dig +short TXT acme.com | grep spf1

If a record comes back, you will edit it, not add a second one. This is a live query for the example.com test domain, run on 21 October 2026:

bash
$ dig +noall +answer TXT example.com @8.8.8.8
example.com.  300  IN  TXT  "v=spf1 -all"

v=spf1 -all is the "this domain sends no mail" record. Other TXT records on the same name are fine, such as site verification tokens. Only v=spf1 ones count.

Step 2: List every sender and its include

Each system that sends as your domain needs one mechanism in the record. Your provider's help page gives the string.

text
Sender            Mechanism
Google Workspace  include:_spf.google.com
Sequencer         include:<value from its setup page>
Your own server   ip4:203.0.113.25

Google publishes v=spf1 include:_spf.google.com ~all for Workspace, and adds include:amazonses.com as an example for a second sender (Google, October 2026). Use ip4: for a fixed address. The address above is a documentation range.

Do not add a sender you do not use. Every include: costs lookups, and old ones are the usual reason a record breaks.

Step 3: Write the record and pick the ending

Join the mechanisms into one string that starts with v=spf1 and ends with an all qualifier.

text
v=spf1 include:_spf.google.com ip4:203.0.113.25 ~all

The ending decides what receivers do with mail from unlisted servers:

  • -all is fail. The strictest choice.
  • ~all is softfail. Mail is accepted but marked. Google's own example uses it.
  • ?all is neutral and +all allows everyone. Never use +all.

Start with ~all. Move to -all once reports and test sends show every sender passes.

Step 4: Publish and verify it resolves

Create one TXT record at the root of the domain (host @) with the string from Step 3. Then query it from outside your network:

bash
dig +short TXT acme.com @8.8.8.8 | grep spf1

You should see your string back in quotes. Two common slips: pasting the value with smart quotes, and creating the record at www.acme.com instead of the root. SPF is checked on the domain in the envelope sender, so it must sit on that exact name.

Step 5: Count lookups and test a real send

SPF allows at most 10 DNS lookups. include, a, mx, ptr, exists and redirect each count. ip4, ip6 and all do not. Go over and the result is permerror (RFC 7208, September 2026). The RFC also limits lookups that return nothing to two.

Count by hand. Each include costs one lookup, plus whatever its own record contains:

bash
dig +short TXT _spf.google.com @8.8.8.8

On 21 October 2026 this returned a flat list with no further includes:

text
"v=spf1 ip4:74.125.0.0/16 ip4:209.85.128.0/17 ip6:2001:4860:4864::/56 ip6:2404:6800:4864::/56 ip6:2607:f8b0:4864::/56 ip6:2800:3f0:4864::/56 ip6:2a00:1450:4864::/56 ip6:2c0f:fb50:4864::/56 ~all"

So that include costs one lookup. Repeat the query for every include in your record and add the numbers. Providers can change these records, so recheck after a vendor change.

Now send a real message from your domain to a mailbox you own and open the raw headers. Look for the result line:

text
Authentication-Results: mx.example.net;
 spf=pass smtp.mailfrom=acme.com

spf=pass with your domain next to smtp.mailfrom is what you want. softfail means the sending server is not in the record. permerror means the record is broken.

Step 6: Check the list before you send

SPF proves the server may send. It does not make a bad list safe. Bounces hurt a domain that passes every check.

LeadOcean stores an email_status on each address. Sizing is free. Send count=true as a query parameter with limit 1 and read meta.total:

bash
curl -s -X POST "https://api.leadocean.io/v1/people/search?count=true" \
  -H "x-api-key: $LEADOCEAN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "jobLevel": ["VP"],
    "jobFunction": ["Information Technology"],
    "country": ["US"],
    "limit": 1
  }'

The MCP tool leadocean_count_leads does the same from Claude or any MCP client. For the DKIM half of authentication, read Gmail DKIM. Sending at volume also changes your cost per contact: see what 1M records cost at 20 data providers.

What you get

Two outputs. The first is the SPF lookup from Step 1, run on 21 October 2026:

text
example.com.  300  IN  TXT  "v=spf1 -all"

The second is a free count from leadocean_count_leads. It ran on 21 October 2026 and spent 0 records. The filters were VP, Information Technology and country US.

FilterTotal
Default mailable (verified, catch_all_valid or catch_all)8,675
No email filter17,699
Distinct companies in the mailable count3,502

The first row is the audience worth sending to. The gap to the second row is addresses you would be guessing on. LeadOcean does not refund bounced emails, so read email_status before a send.

The REST sizing call returns the response shape from the API reference: success, an empty data array, and a meta object with countOnly: true and total. meta.total is capped at 100,000.

Troubleshooting

ErrorCauseFix
spf=permerrorTwo v=spf1 records on the nameMerge them into one record
spf=permerrorMore than 10 lookupsRemove unused includes, or replace an include with ip4: ranges
spf=softfailThe sending server is not listedAdd that sender's include, then retest
spf=noneNo record at the envelope-sender domainPublish at the exact name in smtp.mailfrom
401 from the LeadOcean APIMissing or wrong x-api-key headerCheck $LEADOCEAN_API_KEY
429 from the LeadOcean APIAbove your plan's rate (5 a second on Free, 200 on Agency)Slow the client and wait the Retry-After seconds
402 from the LeadOcean APIFree plan: the 1,000 one-off records are spent (counts cost nothing)Check GET /v1/account, or move to Solo at $97 or Agency at $497 a month

FAQ

Can I have two SPF records?

No. Two v=spf1 records give permerror. Merge every sender into one record.

Should I end with ~all or -all?

Start with ~all. Switch to -all once every sender passes in test sends. -all is stricter but punishes a forgotten sender.

Does SPF alone get cold email into the inbox?

No. SPF checks the envelope address, not the From address the reader sees. Add DKIM and DMARC, and keep the list clean.

Does LeadOcean send email or publish SPF records?

No. LeadOcean is a people and company data API with a hosted MCP server. It gives you the list and the email_status on each address. Your mailbox provider or sequencer sends.

Publish SPF, then fill the list with addresses you can trust

Free to start. No credit card. 1,000 records to spend whenever you like.

Get your free API key →

See also LeadOcean pricing. More guides are in the blog.