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

How-to guide

How to Apply Email Deliverability Best Practices (for B2B Outbound)

Six practices, each with a command you can run today. Authentication, list status, sizing, pacing and a spam-rate tripwire.

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

Most B2B deliverability problems come from three places: missing DNS authentication, a list full of unchecked addresses, and sending too much too fast. All three are testable from a terminal.

This page is the short version. For the full gate in code, see building for email deliverability. For sending-side fixes, see how to improve email deliverability. More guides are on the blog.

TL;DR

  • Six practices, in order: authenticate the domain, size the list for free, filter on email_status, re-check at enrichment, ramp volume, watch the spam rate.
  • It takes about 20 minutes and spends 0 LeadOcean records. A 25-record spot check is optional.
  • Google's guideline is a spam rate below 0.10% and never 0.30% or higher (Google, October 2026).

Prerequisites

  • A sending domain you control, and dig on your machine.
  • A LeadOcean API key from app.leadocean.io, exported as $LEADOCEAN_API_KEY. The free plan needs no card.
  • curl and jq.
  • Google Postmaster Tools access for your sending domain.

Step 1: Check SPF, DKIM and DMARC

Authenticate every sending domain before you send anything. Google requires SPF or DKIM from all senders, and SPF, DKIM and DMARC from anyone sending 5,000 or more messages a day to personal Gmail accounts (Google, October 2026).

bash
dig +short TXT yourdomain.com                      # SPF
dig +short TXT selector1._domainkey.yourdomain.com # DKIM (your selector)
dig +short TXT _dmarc.yourdomain.com               # DMARC

Here is a real run against example.com, on 2026-10-21:

text
$ dig +short TXT example.com
"v=spf1 -all"
$ dig +short TXT _dmarc.example.com
"v=DMARC1;p=reject;sp=reject;adkim=s;aspf=s"

That domain publishes -all because it sends no mail. Yours should list every service that sends for you. Google asks for a DKIM key of 1024 bits or longer, and recommends 2048.

Use a separate sending domain for cold outreach, not your main domain. That is our advice, not a Google requirement.

Step 2: Size the list before you pull it

Count first. A count costs no records, so you can try ten filter sets and pick the one worth buying.

bash
curl -s -X POST "https://api.leadocean.io/v1/people/search?count=true&limit=1" \
  -H "x-api-key: $LEADOCEAN_API_KEY" \
  -H "content-type: application/json" \
  -d '{"jobLevel":["VP"],"jobFunction":["Sales & Business Development"],"country":["US"],"emailType":["work"],"emailStatus":["verified"]}' \
  | jq '.meta'

count=true goes in the query string, not the JSON body. meta.total is capped at 100,000, so a very wide list reads as a floor. Enum values are exact strings. Check them with GET /v1/enums or the free leadocean_list_enum_values MCP tool.

Step 3: Send only to statuses you trust

email_status has 13 values. Send to verified and catch_all_valid. Hold catch_all, risky, unknown and untested for a smaller test wave. Drop invalid, disposable, spam_trap, abuse and none.

Compare the strict and loose counts on the same list. The gap is what you keep out of your sequencer.

text
Filter: jobLevel VP, jobFunction Sales & Business Development, country US, emailType work
emailStatus verified                                               26,988
emailStatus verified, catch_all, risky, unknown, untested         235,170

The two rows come from leadocean_count_leads on 2026-10-21. The loose count is an upper bound on addresses you might try, not a mailable count. Nearly 90% of it is something other than verified, which is why the filter matters. The catch_all filter value also matches catch_all_valid.

LeadOcean does not refund or credit bounces. Read email_status before you send.

Step 4: Re-check addresses at enrichment

A search row tells you a status. The enrich answer is the one to trust right before sending. Enrich by person_id with reveal_email on, and read the status that comes back.

bash
curl -s -X POST "https://api.leadocean.io/v1/people/enrich" \
  -H "x-api-key: $LEADOCEAN_API_KEY" \
  -H "content-type: application/json" \
  -d '{"person_id":"PERSON_ID_FROM_SEARCH","reveal_email":true}' \
  | jq '.data.contact_data'

A person with nothing on file returns 404 and costs nothing. A hit costs one record. Run this on the 25 rows from the pull below if you want a spot check before a wave. If the enriched status is worse than the search status, drop the address.

Do this for every wave, not once. People change jobs, and a mailbox that was fine last quarter can bounce today. The dataset refreshes monthly, and each record carries its own fetched_at date.

Step 5: Ramp volume and cap per domain

Do not double your volume overnight, and do not point a new domain at a full list on day one. Google warns that sudden spikes without a sending history can bring rate limiting or reputation drops. It says to ramp the changed traffic separately after a significant change to your sending infrastructure or message format (Google, October 2026).

These caps are our defaults, not rules from Google or LeadOcean. Change them to fit your domain.

yaml
week_1: { per_mailbox_per_day: 20 }
week_2: { per_mailbox_per_day: 40 }
week_3: { per_mailbox_per_day: 60 }
contacts_per_company_domain_per_wave: 3
statuses_allowed: [verified, catch_all_valid]

To pull a first wave, search with the same filters, a small limit and a cursor. Each person returned costs one record.

bash
curl -s -X POST "https://api.leadocean.io/v1/people/search" \
  -H "x-api-key: $LEADOCEAN_API_KEY" \
  -H "content-type: application/json" \
  -d '{"jobLevel":["VP"],"jobFunction":["Sales & Business Development"],"country":["US"],"emailType":["work"],"emailStatus":["verified"],"limit":25}' \
  | jq '{returned: (.data | length), next: .meta.nextCursor}'

Step 6: Set a spam-rate tripwire

Watch the spam rate in Postmaster Tools every week. Pause a campaign when it crosses 0.10%, because Google says to stay below that line and never reach 0.30%.

For bulk marketing mail, add one-click unsubscribe. Google requires both headers for senders above 5,000 messages a day:

text
List-Unsubscribe-Post: List-Unsubscribe=One-Click
List-Unsubscribe: <https://acme.com/unsubscribe/example>

Google does not track open rates, so do not use them as a health signal. Use bounces and spam rate.

What you get

Response shape from the API reference, for a sizing call. Values are placeholders except the counts from Step 3.

json
{
  "success": true,
  "data": [],
  "meta": {
    "total": 26988,
    "countOnly": true,
    "distinctCompanies": 16114
  }
}

After six steps you have a signed domain, a list sized for free, a status filter, a ramp plan and a number to watch. The counts in Step 3 are from 2026-10-21.

Troubleshooting

ErrorCauseFix
401Missing or wrong x-api-keyCheck $LEADOCEAN_API_KEY and the header name.
429Above the plan's rate limit (5 a second on Free, 200 on Agency)Back off for the Retry-After value.
402Free plan's 1,000 records are spentMove to a paid plan on pricing.
Empty resultsA filter value is not an exact enum stringLook it up with GET /v1/enums or leadocean_list_enum_values.
meta.depthCapped is trueOne search stops at 10,000 rowsNarrow the filters instead of paging harder.
Export refusedOver 50,000 rows a request or 500,000 a daySplit the export by filter.

FAQ

What spam rate should a B2B sender target?

Below 0.10% in Google Postmaster Tools, and never 0.30% or higher. That is Google's published guidance.

Do I need DMARC if I send under 5,000 emails a day?

Google requires only SPF or DKIM below that volume. Publish DMARC anyway. It costs one DNS record and shows who else sends as you.

Should I email catch-all addresses?

Not in your first waves. Test them in a small batch, watch bounces for a few days, then decide whether the rest of the segment is worth sending. catch_all_valid is safer than plain catch_all.

How often should I re-check a list?

Before every wave. People change jobs and mailboxes close, and the dataset refreshes monthly. Sizing is free, so re-count first.

What belongs on a suppression list?

Every hard bounce, every unsubscribe and every spam complaint. Add them to the suppression list before the next send, and check new lists against it.

Does LeadOcean verify emails?

Each address carries an email_status. A search status is an upper bound, so re-check at enrichment with reveal_email before you send.

Size your next list for free before you send a single email

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

Get your free API key →