Verify cold lists right before each send, active CRM segments monthly, newsletters quarterly, and any purchased or event list before the first send. Third-party estimates put monthly email decay between about 2% and 3.6%. A status you read in spring says little about a send in autumn. This guide gives the cadence, then the calls that check a segment against LeadOcean data.
TL;DR
- Cadence follows risk: cold lists at send time, active CRM monthly, newsletters quarterly, purchased or event lists once before first use.
- The whole setup takes about 20 minutes. Sizing a segment is free. Re-checking addresses spends 1 record per address found in LeadOcean, and a miss (404) costs nothing.
- Checking more often than the data refreshes is wasted work. LeadOcean refreshes monthly, so a weekly re-pull of the same address returns the same answer.
Prerequisites
- A LeadOcean API key from app.leadocean.io, exported as
$LEADOCEAN_API_KEY. The free plan has 1,000 records, one-off, no card, and both the search and enrich scopes. curlandjq.- A CSV or CRM export of the addresses you plan to mail, one address per line.
Step 1: Set the cadence by list type
Start with a rule per list, not one schedule for everything. Set the gap so that a check always costs less than the bounces it prevents.
| List | Re-verify | Why |
|---|---|---|
| Cold outreach | Right before each send (within a few days) | Every bounce hurts your sending domain. |
| Active CRM segment | Monthly, for the slice you mail in the next 30 days | Job changes break work addresses continuously. |
| Newsletter or nurture | Quarterly | Opted-in subscribers decay, but a bounce costs less. |
| Event or webinar sign-ups | Before the first send, every time | Typos and throwaway addresses cluster here. |
| Purchased or scraped list | Before the first send, no exceptions | Quality varies by source. |
A status is a reading at one moment. It answers whether the mailbox accepted mail on that day, and it says nothing about next month, when the person may change jobs or the company may move its mail. That is why the schedule matters more than the verifier you pick.
The decay figures behind this are third-party. LeadMagic's September 2026 guide cites about 3.6% a month for email addresses (ZoomInfo) and 2.1% a month (Apollo), and we have not re-measured either (source, checked September 2026). Treat them as a range and measure your own bounce rate by segment.
Two adjustments. If a list was last checked more than six months ago, run a full pass before you resume the schedule. If your volume is high, check closer to the send, not the whole database.
Volume tightens the gap further. The more you send, the more of your list sits exposed on any given day.
| Monthly sends | Gap between checks on the active slice |
|---|---|
| Under 1,000 | Quarterly |
| 1,000 to 10,000 | Monthly |
| 10,000 to 50,000 | Every one to two weeks |
| Over 50,000 | Check each address shortly before its send slot, and audit the full list monthly |
These bands follow the volume guidance in the same LeadMagic guide (September 2026). They are recommendations, not measured thresholds. Start here, then tighten the gap on any segment whose bounce rate climbs between checks. A rising bounce rate on one source is the clearest signal that its gap is too long.
Step 2: Size the segment for free
Count before you spend anything. A count tells you how big the next check is and whether the segment is worth mailing at all. A search with count=true and limit=1 is a free call, and meta.total is capped at 100,000.
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": ["CA"],
"emailStatus": ["verified", "catch_all"]
}' | jq '.meta | {total, totalIsExact}'count=true goes in the query string, not the JSON body, which rejects unknown fields. The emailStatus values are exact strings, so check them with GET /v1/enums/email_status if you are unsure. The same count runs from the MCP server with the free leadocean_count_leads tool.
Step 3: Re-check each address before you send
Enrich by email and read the status. POST /v1/people/enrich takes exactly one of linkedin_url, email, phone or person_id, and reveal_email returns the address block.
while read -r addr; do
curl -s -X POST "https://api.leadocean.io/v1/people/enrich" \
-H "x-api-key: $LEADOCEAN_API_KEY" \
-H "Content-Type: application/json" \
-d "{\"email\": \"$addr\", \"reveal_email\": true}" \
| jq -c --arg a "$addr" '{
input: $a,
status: (.data.contact_data.email_status // "not_in_data")
}'
sleep 0.05
done < addresses.txt > recheck.jsonlA person we hold nothing for is a 404 and costs nothing, so the not_in_data rows are free. Every address found costs 1 record. The sleep 0.05 keeps the loop near 20 requests a second, under Solo's 50 and Agency's 200. On Free, raise it to sleep 0.2 to stay under 5 a second.
A worked example: a 200-address test of your CRM slice spends at most 200 records, and fewer if some are not in the data. On the free plan that leaves 800 of your 1,000 one-off records. A monthly job over 10,000 addresses on Solo ($97 a month) runs at a fair-use pace, with no per-record price.
LeadOcean is a data source with its own verification, not a verifier for arbitrary addresses. For addresses that are not in the data, run a dedicated verifier before you send.
Step 4: Suppress by status, not by age alone
Turn the status into a send decision. Safe to send: verified and catch_all_valid. Cannot be confirmed: catch_all. Do not send: invalid, spam_trap, abuse, disposable. Use with care: risky, role, derived. untested means nobody has checked the address yet.
jq -r 'select(.status == "verified" or .status == "catch_all_valid") | .input' \
recheck.jsonl > send_ok.txt
jq -r 'select(.status | IN("invalid","spam_trap","abuse","disposable")) | .input' \
recheck.jsonl > suppress.txt
jq -r 'select(.status | IN("verified","catch_all_valid","invalid","spam_trap","abuse","disposable") | not) | .input' \
recheck.jsonl > review.txtRead the date as well. Each email carries verified_batch_date, the date of the verification run, not a timestamp for that one address. A null means LeadOcean did not run the check itself. LeadOcean does not refund or credit back bounced emails, so read email_status before sending.
Step 5: Schedule it
Schedule the job from Step 3 on the cadence from Step 1. The first line below runs on the first of the month for the CRM slice, the second on send mornings for the cold list.
0 6 1 * * /opt/jobs/recheck.sh /data/crm_next_30_days.txt
0 6 * * 1,3 /opt/jobs/recheck.sh /data/cold_today.txtBecause the dataset refreshes monthly, a weekly job on the same addresses adds cost without new information. Keep the check close to the send, and keep the gap between jobs at least a month for anything that is not about to go out.
What you get
The free count from Step 2, run on 2026-10-21 for VPs in Sales & Business Development in Canada:
filters: jobLevel VP, jobFunction Sales & Business Development, country CA
emailStatus [verified] -> total 1,854 (totalIsExact true)
emailStatus [verified, catch_all] -> total 5,574 (totalIsExact true)
credits: 0Those are counts by what we filtered, not a promise of deliverability. The catch_all filter also matches catch_all_valid. A search count of verified is an upper bound: confirm per address on enrichment, and trust the enriched answer.
The per-address answer from Step 3 has this shape (response shape from the API reference, placeholder values).
{
"success": true,
"data": {
"contact_data": {
"has_email": true,
"email_status": "verified",
"email_type": "work",
"contact_emails": [
{
"email": "jane.doe@acme.com",
"type": "work",
"status": "verified",
"verified_batch_date": "2026-09-20"
}
]
}
}
}Troubleshooting
| Error | Cause | Fix |
|---|---|---|
| 401 | Missing or wrong x-api-key header | Check $LEADOCEAN_API_KEY is exported and sent as x-api-key. |
| 429 on search or enrich | Faster than your plan's rate per account: 5 a second on Free, 50 on Solo, 200 on Agency | Add a short sleep or cap parallel workers. |
| 402 | Fewer records left than the call needs | Check GET /v1/account and GET /v1/usage, or move to a paid plan. |
| 404 on enrich | LeadOcean holds nothing for that person | Free. Send it through a dedicated verifier instead. |
| Empty results or zero total | A misspelled enum value such as Vp or ca | Use exact strings from GET /v1/enums/{name}: VP, CA, verified. |
| 429 on export | Daily or period export ceiling reached | Exports take 50,000 rows a request and 500,000 a day. Split the job. |
FAQ
How often should you verify a cold email list?
Right before each send, within a few days. Cold sends are the riskiest for your sending domain, and a list decays by a few percent a month, so an old status is a guess.
Is verifying monthly enough?
For an active CRM segment, yes, if you check the slice you will mail in the next 30 days. For cold sends, no: check close to the send. For newsletters, quarterly is usually fine.
Does checking more often than monthly help?
Not against LeadOcean data. The dataset refreshes monthly and each record carries its own fetched_at date, so a weekly re-pull of the same address returns the same answer. Spend the effort on checking right before the send.
Do I need to verify before every campaign?
For cold outreach, yes, on the slice you are about to send. For a warm list you mail often, the monthly check on the next 30 days of recipients is enough. Add a check after any import from a new source.
What does a re-check cost?
Sizing is free. Enriching an address costs 1 record if we hold it and nothing if we do not. On the free plan that comes out of your 1,000 one-off records. Solo is $97 a month and Agency is $497 a month, both at a fair-use pace instead of credits, see pricing.
What should I do with catch-all results?
A catch_all status means the domain accepts everything, so the mailbox cannot be confirmed. catch_all_valid means we confirmed the mailbox exists. Send to catch_all_valid and treat plain catch_all as a smaller, separate batch.
Related
- Hub: verification and deliverability
- Verify a cold email list in bulk
- How to verify email addresses
- What is email list churn?
Check the list before you send it
Free to start. No credit card. 1,000 records to spend whenever you like.
Get your free API key →