An SPF record may trigger at most 10 DNS-querying terms, counted across every record it includes. Go over and receivers return permerror, so SPF can fail even for mail from servers you authorised. Count your domain’s lookups free below, see which include costs the most, then read what counts, how to get back under the limit, and how to check a whole list of domains in one run.
Enter a domain. The counter runs in your browser against public DNS: it reads the SPF record, follows every include and redirect as a receiver would, and counts each DNS-querying term, including any past the limit. You get the total against the limit of 10, the void lookups against the limit of 2, and what each term in your own record costs with everything nested under it.
The SPF record:
| Term | Lookups | Lookup # |
|---|
“Lookups” is the term itself plus every lookup nested under it. “Lookup #” shows where its lookups fall in the receiver’s running total. Receivers stop with permerror at #11, so nothing after that is reached. ip4, ip6 and all cost nothing, and terms after all are never evaluated, so they aren’t listed.
This reads what your DNS publishes right now through Cloudflare’s public resolver, or Google’s when Cloudflare’s fails. A receiver whose DNS cache still holds older records can count differently until those expire. A lookup that fails (server error or timeout) is never treated as fine: the count becomes a minimum and the result reads unknown. The lookup of your own SPF record isn’t one of the counted terms. Include or redirect targets built from macros (%{i} and the like) depend on the sending server, so their own lookup is counted but what’s behind them isn’t. To find every other kind of SPF PermError, use the SPF PermError checker.
RFC 7208, the SPF standard, says in section 4.6.4: “SPF implementations MUST limit the total number of those terms to 10 during SPF evaluation” and “If this limit is exceeded, the implementation MUST return ‘permerror’.”
| Counts toward the 10 | include, a, mx, ptr, exists (mechanisms) and redirect (modifier) |
|---|---|
| Doesn’t count | ip4, ip6, all, and the exp modifier |
| Void lookups | Implementations SHOULD limit “void lookups” (terms whose lookup returns no records or a non-existent domain) to two; exceeding that limit is also a permerror. Find and fix void lookups |
Per mx / ptr | Each MX or PTR record may lead to at most 10 A/AAAA lookups, on top of the overall limit (over that, mx is a permerror; for ptr the extra records are ignored) |
Other SPF PermError causes: more than one SPF record, a syntax error, or an include whose target has no SPF record. Check which one applies to your domain.
Two more rules matter when counting. An include runs a full SPF check against the included domain (section 5.2), so every term in the included record counts too, and so on down the tree. And a redirect= is ignored when the record also has an all mechanism (section 6.1), so it doesn’t count in that case.
Microsoft’s SPF documentation says that if the lookup count “is greater than 10, the message fails SPF with a permanent error (also known as a permerror)”, and that the receiving system may bounce it with an NDR such as “The message required too many lookups.” Google’s troubleshooting page says exceeding the limit causes “your SPF record validation to fail”, and warns that error messages “might include warnings that you don’t have an SPF record (even if you do have one).”
The failure can be intermittent. Receivers evaluate the record left to right and stop at the first match, so, as Microsoft notes, mail from a sender listed early may pass while mail from a sender further along fails. A record that “mostly works” can still be over the limit.
For bulk senders this is more than a warning: Gmail, Yahoo and Outlook.com all require SPF, and Microsoft says it must pass. See what the bulk sender rules require.
Sources: RFC 7208 sections 4.6.4, 5.2 and 6.1; Microsoft Learn, Set up SPF to identify valid email sources for your Microsoft 365 domain; Google Workspace Help, Troubleshoot SPF issues (all checked September 2026).
include, a, mx, ptr, exists and redirect. Skip ip4, ip6 and all.include: and redirect= target, fetch that domain’s SPF record and count its terms the same way.The number of include: terms you can see is not the lookup count. Microsoft’s example: an include that points to a record with three more includes costs four lookups. Real records from our run on 27 September 2026:
| Domain | Includes in its own record | Total lookups |
| posthog.com | 6 | 10 |
|---|---|---|
| github.com | 8 | 10 |
| atlassian.com | 6 | 6 |
| hubspot.com | 0 (one redirect=) | 4 |
posthog.com and github.com both sit exactly at the limit, so adding one more sending service would push either over.
a and an mx that name the same server, each cost a lookup.a, mx or a vendor include with ip4/ip6 where the addresses are stable. Microsoft recommends this only for vendors with “a stable, documented set of sending IP addresses”, and says you then have to watch for IP changes yourself. Google notes each string in an SPF record is limited to 255 characters.marketing.example.com, support mail on support.example.com, and so on. Each subdomain has its own SPF record and, in Microsoft’s words, “its own 10 lookup budget”. This also keeps a bulk sender’s reputation off your main domain.Flattening replaces includes with the IP addresses they currently resolve to. It fixes the count today, but the IPs are a snapshot: when a provider adds a range, your flattened record doesn’t follow and mail from the new range fails SPF. Microsoft says not to flatten its own include (spf.protection.outlook.com) or any service with changing IPs, and if you do flatten, to document what you replaced and review it at least quarterly. Try the options above first.
After any change, allow time for it to spread: Google says 24–48 hours.
Email Security Signals reads each domain’s SPF record from public DNS, follows its includes and redirects, and returns the total lookup count with the rest of its email-authentication checks. It suits auditing client domains, a prospect list or a vendor list, where checking one domain at a time in a web tool doesn’t scale.
Input: paste domains, URLs or email addresses into Domains on the Apify Store page, or send JSON through the API:
{
"domains": ["posthog.com", "https://www.github.com", "jane@atlassian.com"],
"maxConcurrency": 5
}
SPF fields in each result row:
spf.lookupCount | Total DNS-querying terms across the whole include/redirect tree |
|---|---|
spf.lookupCountIsMinimum | true when the tree couldn’t be walked fully (an include or redirect lookup failed, or the tree was more than 10 levels deep or needed more than 40 record fetches), so the count is a lower bound |
spf.valid | true when there is exactly one SPF record, it needs 10 or fewer lookups, and nothing else in it is a permerror (more than 2 void lookups, an mx name with more than 10 MX hosts, an unknown or misspelled mechanism, or an include or redirect target with no SPF record) |
spf.voidLookups | a, mx and exists names in the include/redirect tree that return no records (RFC 7208 allows 2); spf.voidLookupsIsMinimum is true when one of those lookups failed or the 30-name cap was reached, so the count is a lower bound |
spf.record, spf.recordCount | The record itself, and how many SPF records the domain publishes (more than one is also a permerror) |
spf.allQualifier | The effective all: -all, ~all, ?all or +all, following redirect=; null when there is none or it can’t be determined |
sendingVendors, spfIncludes | Services named in the domain’s own includes (SendGrid, Mailchimp, Amazon SES, …), and any includes not in the vendor table, listed raw: a starting point for deciding what to remove |
issues | spf_too_many_lookups (over 10, high severity), spf_near_lookup_limit (9 or 10, low), spf_multiple_records, spf_permerror (high: more than 2 void lookups, an mx name with more than 10 MX hosts, an unknown mechanism, or an include or redirect target with no SPF record or more than one), spf_void_lookups (1 or 2 on an otherwise valid record, low), spf_lookup_count_truncated |
bulkSenderCompliant | false whenever SPF is over 10 lookups or otherwise a permerror |
For a domain over the limit, the issue reads, for example, “SPF needs 14 DNS lookups (limit is 10); receivers return permerror.” Export to CSV and sort by the spf/lookupCount column, or filter issues for spf_too_many_lookups.
Void lookups are checked for a, mx and exists names (up to 30 per domain), and an mx name with more than 10 MX hosts is a permerror. What the check doesn’t cover: ptr is counted but can’t be resolved without a sending IP, the address lookups of each MX host aren’t made, and %{…} macros are counted but not expanded. DNS is read through Cloudflare’s public resolver at run time.
$0.02 per graded domain ($20 per 1,000), with no subscription. Domains that don’t exist, IP addresses, unparseable inputs, DNS lookup failures and duplicates come back as free rows. Set a max charge per run and the run never costs more than that.
~all versus -all change the lookup count?No. all never causes a DNS query. The qualifier only decides what happens to mail from servers the record doesn’t list.
Nested includes. A provider’s include often includes further records of its own, and every one of those terms counts. posthog.com’s six includes add up to ten lookups.
Yes. SPF records are public DNS records that every receiving mail server reads. No email is sent and no login is used.
Or have us grade your list free: send up to 100 domains to hello@siftsmith.com, and we’ll run them through Email Security Signals and email you a CSV file with each domain’s grade, the issues found and whether it meets the bulk-sender rules, or the reason a domain couldn’t be graded.
Related: every SPF PermError cause (find which one applies, with a checker), fixing SPF “too many void lookups” (find every term that returns nothing, with a checker), fixing an SPF record over 255 characters (check how a long record is split into strings), fixing “multiple SPF records” (merge two SPF records into one and see the merged lookup count) and fixing “multiple DMARC records found”.