Automation in cold email means the list, the checks and the loading are scripted. The sending stays in a sender built for it. This guide covers the half you can automate with an API: a sized, verified list that arrives in your sequencer ready to run.
Most sequences fail before step one. The list was too wide, or full of addresses that bounce. Fix that first and the copy matters more.
TL;DR
- Size the list with a free count, search a small test batch, then export the full list as a CSV with email status per row.
- It takes about 45 minutes. Sizing costs 0 records. A 25-row test search costs 25 records, inside the Free plan's 1,000.
- LeadOcean is the data layer. It has no sequencer and sends no email, so the last step loads the CSV into your sender.
Prerequisites
- A LeadOcean account. Start free: no card, 1,000 records for life, 5 requests a second.
- An API key from app.leadocean.io, exported as
$LEADOCEAN_API_KEY. - A sender that imports a CSV and handles mailboxes, warm-up and unsubscribes. See warm-up tools.
curlandjq. Python 3 for the gate script.
Step 1: Size the list before you spend a record
Count first. A count costs 0 records, so run it as often as you like. Through the MCP server it is leadocean_count_leads. Over REST it is a search with count=true as a query parameter and limit 1.
curl -s "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": ["GB"],
"industry": ["Software Development"],
"emailStatus": ["verified"],
"limit": 1
}' | jq '.meta'meta.total is capped at 100,000 and meta.countOnly is true. Filter values are exact strings. If a count comes back empty, look the value up with GET /v1/enums or leadocean_list_enum_values.
Step 2: Narrow until the list is one you could hand-pick
A sequence needs one reader in mind. Add a filter, recount, and stop when you can describe every person in a sentence. Here is the same list with and without a headcount band.
{"employeeRange": ["51-200"]}Add that key to the body above and compare meta.total.
Aim for hundreds to low thousands per sequence. You will read replies from all of them, and a small list keeps the first-line and offer specific.
Step 3: Pull a 25-row test batch
Search returns a thin record: name, title, company and a status flag. Each person returned is one record. Page with cursor.
curl -s 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": ["GB"],
"industry": ["Software Development"],
"emailStatus": ["verified"],
"limit": 25
}' > batch.json
jq '.data | length, .[0].contact_data.email_status' batch.jsonRead the 25 rows by eye. If titles or companies look wrong, tighten the filters now. Fixing the list costs nothing at this stage. Fixing it after 1,000 sends costs reputation.
Step 4: Export the full list with status per row
Bulk CSV export is not on Free. Solo includes it, and Agency allows 50k rows a file. One request takes up to 50,000 rows and a day tops out at 500,000. Every row costs one record.
curl -s -X POST https://api.leadocean.io/v1/exports \
-H "x-api-key: $LEADOCEAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "uk-saas-vp-sales",
"filters": {
"jobLevel": ["VP"],
"jobFunction": ["Sales & Business Development"],
"country": ["GB"],
"industry": ["Software Development"],
"emailStatus": ["verified"]
},
"limit": 190,
"columns": ["profile_first_name", "profile_last_name", "current_company_name", "current_job_title", "email_1_address", "email_1_status"]
}'The job answers with an id. Poll GET /v1/exports/{id} until it is done, then download the file. The app at app.leadocean.io also has an Exports page with a filter builder, column picker and a price preview, if you prefer to click. On Free, skip the export and use batch.json from Step 3.
Step 5: Gate the CSV on status
Never trust a filter alone. Check the status column and drop the rest. There is no refund for bounced emails, so this step protects you.
import csv
rows = list(csv.DictReader(open("export.csv")))
keep = [r for r in rows if r.get("email_1_status") == "verified"]
dupes = len(keep) - len({r["email_1_address"].lower() for r in keep})
with open("sequence_input.csv", "w", newline="") as f:
w = csv.DictWriter(f, fieldnames=rows[0].keys())
w.writeheader()
w.writerows(keep)
print(len(rows), "in", len(keep), "kept", dupes, "duplicate addresses")Also remove anyone on your own suppression list, and anyone who replied or unsubscribed in a past campaign. Your sender usually does this on import, but check.
Step 6: Write the sequence as a spec
Keep the sequence in a file you can version, then rebuild it in your sender. Four touches over about two weeks is a sound shape for a first test. Change one variable at a time.
sequence: uk-saas-vp-sales-v1
audience: sequence_input.csv
send_window: Tue-Thu, 08:00-11:00 recipient local time
stop_on: reply, unsubscribe, bounce
steps:
- day: 0
goal: one question about a specific problem
length: under 90 words
- day: 3
goal: add one proof point, same thread
- day: 8
goal: different angle, new subject line
- day: 14
goal: short close, offer to stop writingStart each message with the reader's problem, not your product. End with one question. Keep step one plain text with no links, and measure the effect on your own domain. For more on structure, see cold email campaign examples.
Step 7: Load, send small and read the bounces
Import sequence_input.csv into your sender. Start at a low daily volume per mailbox while it warms, and send to 10 percent of the list first.
Check two numbers after the first day. Bounce rate tells you about the data and your domain. Reply rate tells you about the offer. If bounces are high, look at email_1_status for the bounced rows and tighten the gate in Step 5. If replies are low, change the offer, not the list size.
Cost at scale: a 1,000-row export is 1,000 records. Solo at $97 a month covers one person. Agency at $497 a month is for teams, with 5 keys. Paid plans have a fair-use pace, not credits, so there is no overage invoice. See pricing.
What you get
The free count from Step 1, run on 21 October 2026 through leadocean_count_leads. It cost 0 records.
{
"total": 190,
"totalIsExact": true,
"distinctCompanies": 150,
"distinctCompaniesIsExact": true,
"credits": 0
}Read it like this. 190 VPs of sales and business development in the UK, at Software Development employers, hold a verified address. They work at 150 companies. Adding employeeRange 51-200 narrows it to 26 people at 25 companies. This count is filtered to verified status only.
A gated CSV row has this shape. Column ids from GET /v1/exports/columns, placeholder values:
profile_first_name,profile_last_name,current_company_name,current_job_title,email_1_address,email_1_status
Jane,Doe,Acme,VP Sales,jane.doe@acme.com,verifiedTroubleshooting
| Error | Cause | Fix |
|---|---|---|
| 401 | Missing or wrong x-api-key header | Re-export $LEADOCEAN_API_KEY from app.leadocean.io |
| 429 | Above the plan's rate limit (5 a second on Free, 200 on Agency) | Honor Retry-After and slow the loop |
| 402 | Records spent (Free is 1,000 for life) | Check GET /v1/usage, then move to Solo at $97 a month |
| Empty results | A filter value is not an exact enum string | Call GET /v1/enums or leadocean_list_enum_values and copy the value |
| Export refused | Over 50,000 rows a request or 500,000 a day, or on Free | Split by country or seniority, or use a paid plan |
Search stops with meta.depthCapped | One search pages to 10,000 rows | Narrow the filters and run several searches |
FAQ
Can LeadOcean send the sequence?
No. LeadOcean is data only. It has no sequencer, dialer or CRM. Export a CSV and load it into the sender you use.
How many records does a test cost?
A count is 0 records. A 25-row search is 25. A 190-row export is 190 on Solo or above. See pricing.
Why gate on status if I already filtered?
A filter selects at search time. The gate is a second check on the file you actually load, and it catches mistakes in your own export columns.
Should I use Claude to personalize each step?
It helps for first lines when each record holds a real fact. The build is in Claude Code cold email automation.
How big should one sequence list be?
Small enough that you can read every reply. Hundreds to low thousands is a practical range for a first test.
Build the list your next sequence runs on
Free to start. No credit card. 1,000 records to spend whenever you like.
Get your free API key →