TL;DR
- A catch-all domain accepts mail for any name, so no probe can prove one mailbox exists. Treat those rows as their own bucket, not as verified and not as invalid.
- Sizing the split takes about 20 minutes and spends 0 LeadOcean records: counts are free. A 100-row test batch spends 100.
- Send verified rows first, catch-all rows in small batches, and read
emailStatuson every row before it reaches a sequencer.
Prerequisites
- A LeadOcean API key in
$LEADOCEAN_API_KEY. The Free plan (1,000 records for life, no card) is enough for this guide. Pricing. curlandjq.- A sequencer or CSV workflow where you can tag rows by a status column.
Step 1: Know what the server is telling you
A catch-all server answers 250 to RCPT TO for every name, real or made up. That is why a verifier cannot confirm the mailbox (RFC 5321, September 2026).
RCPT TO:<jane.doe@acme.com> 250 OK (maybe real)
RCPT TO:<zzq81x@acme.com> 250 OK (definitely fake)Same answer for both, so the result is "cannot confirm". Domains behind a secure email gateway can behave this way, because the gateway accepts first and filters later. See what is a catch-all email for the definition.
Step 2: Learn the status values
LeadOcean stores the verdict in emailStatus. There are 13 values. Four matter here.
curl -s "https://api.leadocean.io/v1/enums/email_status" \
-H "x-api-key: $LEADOCEAN_API_KEY" | jq '.'| Value | Read it as |
|---|---|
verified | Mailbox confirmed. Send. |
catch_all_valid | Catch-all domain, but the mailbox was confirmed through the provider's identity check (Microsoft 365 managed tenants and Google Workspace only). Send. |
catch_all | Catch-all domain, cannot confirm. Small batches. |
risky, unknown | Do not send by default. |
By default a count treats verified, catch_all_valid and catch_all as mailable. Narrow it yourself when you want the buckets apart. As a filter, catch_all also matches catch_all_valid, so filter on catch_all_valid alone for the confirmed set (API reference, October 2026).
Step 3: Size each bucket for free
Counting spends no records. Run one count per bucket with the same audience filters. The MCP tool leadocean_count_leads does this from Claude.
leadocean_count_leads
jobLevel: ["VP"]
jobFunction: ["Sales & Business Development"]
country: ["CA"]
emailStatus: ["verified"]
facets: falseChange only emailStatus for the other two runs. Over REST, send count=true as a query parameter with limit 1; meta.total is capped at 100,000.
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":["Sales & Business Development"],
"country":["CA"],"emailStatus":["catch_all_valid","catch_all"],"limit":1}' \
| jq '.meta'Step 4: Pull the buckets as separate lists
Run two real searches, one per bucket, so the status never gets blended. The first file holds the send-first bucket, the second the catch-all test batch. Filters are arrays, limit runs 1 to 100, and you page with cursor.
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":["CA"],"emailStatus":["verified"],"limit":100}' \
> verified.json
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":["CA"],"emailStatus":["catch_all"],"limit":100}' \
> catchall.jsonFor a full list, use POST /v1/exports (up to 50,000 rows a request, 500,000 a day; bulk CSV is not on Free). Request the email_1_status column so the status travels with each row.
Step 5: Check a domain yourself
You can also test a single domain by hand. Send a probe to a name that cannot exist, such as a random string at the domain, using your own mail server. If the server accepts it, the domain is catch-all. Do this sparingly and only on domains you plan to mail, because repeated probes can get your IP flagged. The status field in the data saves you this work for most rows.
RCPT TO:<q7x2k9wz@acme.com> 250 OK -> catch-all
RCPT TO:<q7x2k9wz@acme.com> 550 -> normal domain, bad name rejectedStep 6: Send in a safe order
Verified first, catch-all second, in batches you can measure.
Day 1 bucket A, normal pace
Day 3 bucket B, 100 rows, separate sender or sequence tag
Day 6 read bounces on B
under your own bounce limit -> send the next 500
over it -> stop, keep B for another channelPick the bounce limit before you start. We publish no universal number, because it depends on your domain's history. LeadOcean gives no refund or credit-back for bounced emails, so read emailStatus before sending. See can I send this email: valid, risky and catch-all and the accept-all verification guide.
Bounces from the catch-all bucket should not damage the reputation you built on verified rows. Use a different mailbox or sequence for bucket B so the numbers stay readable. If a bounce spike hits, you pause one sequence, not your whole outbound.
Keep copy for bucket B plain too: no attachments, one link at most, a short first line. Gateways filter on content after they accept the message, so a heavy email is more likely to be dropped silently. Silent drops show up as low replies, not bounces, so watch both.
Step 7: Tag rows before they reach the sequencer
Add a column so the sequencer can split by bucket.
jq -r '.data[] | [.profile_data.profile_full_name, .contact_data.email_status] | @csv' catchall.jsonSearch rows carry the status flag, never the address. Enrich a row with reveal_email to get the address itself. Anything not verified, catch_all_valid or catch_all stays out of the send list.
What you get
Counts from the free leadocean_count_leads tool, run 2026-10-21. Audience: VP, Sales & Business Development, Canada. Each count filters by emailStatus only, so it is a count by status, not a mailable total.
| emailStatus filter | People | Distinct companies |
|---|---|---|
verified | 1,854 | 1,392 |
catch_all_valid, catch_all | 3,720 | 2,567 |
risky, unknown | 1,460 | 1,147 |
On this audience the catch-all bucket is larger than the verified one. Drop it and you lose most of the list.
Troubleshooting
| Error | Cause | Fix |
|---|---|---|
| 401 | Missing or bad x-api-key | Check $LEADOCEAN_API_KEY is exported and has no stray spaces. |
| 429 | Above the plan's rate limit (5 a second on Free, 200 on Agency) | Honor Retry-After, slow the loop or move up a plan. |
| 402 | Records spent (Free is 1,000 for life) | Counts still work. Upgrade to pull more. |
| Empty results | Enum value is not an exact string | List values with leadocean_list_enum_values or GET /v1/enums. |
meta.total stops at 100,000 | Count cap | Add filters until the total is under the cap. |
| Export refused | Over 50,000 rows a request or 500,000 a day | Split by country or title and run several exports. |
FAQ
Should I send to catch-all addresses at all?
Yes, in a separate bucket and small batches. Skipping them can cut most of a list: in the counts above, the catch-all bucket is about twice the verified one. Sending them blind risks your domain.
Does a verifier "resolve" a catch-all?
Not by SMTP alone, because the server gives the same answer to every name. Any resolution comes from other signals, so read the status label and test in batches.
Is catch_all_valid safe like verified?
Close. LeadOcean confirmed the mailbox exists through the provider's identity check, so it is safe to send and ranks just below verified. A plain catch_all was never confirmed.
How many records does this guide spend?
Counts spend 0. The two 100-row test searches spend about 200 records at most.
Where can I read more?
Start with what is a catch-all email. For the send rules, see can I send this email. More guides are in the blog. Plans are on pricing.
Size your catch-all slice before you send
Free to start. No credit card. 1,000 records to spend whenever you like.
Get your free API key →