You automate outbound by automating the data feed, not the pitch. Pick one audience, size it, pull it on a schedule, and push only mailable rows to your sender. This guide does that with four requests and one cron line.
TL;DR
- You end with a script that refreshes a filtered prospect CSV every week and drops anyone without a mailable address. It takes about 45 minutes to set up.
- The test pull spends 25 LeadOcean records on the Free plan. The full example list (about 1,300 people) needs Solo, because bulk CSV export is not on Free.
- Sizing the list is free. Counts, filter lists and exports that find nothing cost no records.
Prerequisites
- A LeadOcean API key in
$LEADOCEAN_API_KEY. Free works for steps 1 and 2: start free, no card. curlandjq, plus any machine that can run cron. n8n works too, see the n8n recipe.- A sender that takes a CSV or an API push. LeadOcean is the data source. It has no sequencer, dialer or CRM.
- Solo ($97 a month) for bulk CSV export in step 3. Agency ($497 a month) if a team shares keys.
Step 1: Size the audience before you spend anything
Count first. A count tells you whether the filters are too wide or too thin, and it spends no records.
The audience for this guide is VP-level sales leaders in Canada at companies with 51 to 500 employees. In an MCP client, the leadocean_count_leads tool takes these filters.
{
"jobLevel": ["VP"],
"jobFunction": ["Sales & Business Development"],
"country": ["CA"],
"employeeRange": ["51-200", "201-500"],
"facets": false
}Over REST, send the same filters to the search endpoint with count=true as a query parameter and "limit": 1 in the body.
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"],"employeeRange":["51-200","201-500"],"limit":1}' \
| jq '.meta | {total, distinctCompanies}'A narrow list beats a wide one. If the count is over a few thousand, add a filter such as employeeRange or seniority before you pull anything. If it is under a hundred, loosen one filter. Enum values are exact strings. Resolve them with GET /v1/enums/job_level before you guess.
Step 2: Preview the first 25 rows
Pull a small page to check that the people look right. The search body is flat JSON, filters are arrays, limit runs 1 to 100, and you page with meta.nextCursor.
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":["CA"],"employeeRange":["51-200","201-500"],
"emailStatus":["verified"],"limit":25}' \
| jq '{returned: (.data | length), next: .meta.nextCursor}'This spends 25 records. Search returns a thin projection with no contact points, so you read titles and companies here and fetch emails in step 3. One search pages to 10,000 rows. If meta.depthCapped is true, narrow the filters instead of paging harder.
Step 3: Export the full list as a CSV
Use an export for volume. One request takes up to 50,000 rows, and the account ceiling is 500,000 rows a day.
curl -s -X POST https://api.leadocean.io/v1/exports \
-H "x-api-key: $LEADOCEAN_API_KEY" -H "Content-Type: application/json" \
-d '{
"name": "vp-sales-ca-weekly",
"filters": {
"jobLevel": ["VP"],
"jobFunction": ["Sales & Business Development"],
"country": ["CA"],
"employeeRange": ["51-200", "201-500"],
"emailStatus": ["verified"],
"emailType": ["work"]
},
"limit": 2000,
"columns": ["person_id","profile_first_name","profile_last_name",
"current_job_title","current_company_name","current_company_domain",
"email_N_address","email_N_status"]
}'The API answers 202 with a job id. Poll GET /v1/exports/{id} until data.status is done, then download from GET /v1/exports/{id}/download. Column ids come from GET /v1/exports/columns.
The limit reserves records, so keep it near the count from step 1. You can also build the same export in the app at app.leadocean.io, on the Exports page, which shows the record price before you start.
Step 4: Schedule it
A script plus cron is the whole automation. This one creates the export, waits, downloads, and keeps only rows with a mailable status.
set -euo pipefail
API=https://api.leadocean.io
H=(-H "x-api-key: $LEADOCEAN_API_KEY" -H "Content-Type: application/json")
ID=$(curl -s -X POST "$API/v1/exports" "${H[@]}" -d @export.json | jq -r '.data.id')
while :; do
S=$(curl -s "${H[@]}" "$API/v1/exports/$ID" | jq -r '.data.status')
[ "$S" = "done" ] && break
case "$S" in failed|cancelled) echo "export $S" >&2; exit 1;; esac
sleep 10
done
curl -s "${H[@]}" -o raw.csv "$API/v1/exports/$ID/download"
mlr --csv filter '$email_1_status =~ "^(verified|catch_all_valid|catch_all)$"' raw.csv > mailable.csvSave the body from step 3 as export.json. The mlr call keeps verified and catch-all rows only. It is Miller, a CSV tool, and csvkit does the same. Run it weekly:
0 6 * * 1 cd /opt/outbound && ./refresh.sh && ./push-to-sender.shDedupe on person_id and skip anyone already in your sender, so each run adds only new people to the sequence. Bounced addresses are not refunded, so read the status column before every send.
Two habits keep the loop safe. First, change one filter at a time and recount, so you know which filter moved the number. Second, store the export name and the count next to each run. When a week's list suddenly doubles, you can see which filter change caused it.
Step 5: Hand off to your sender
Your sender does the sending. Point push-to-sender.sh at its CSV import or API, and map email_1_address to email, current_company_domain to company domain, and current_job_title to title.
Keep the human parts human. Automation without review sends the same mistake to every row. Write the first line yourself, review replies yourself, and cap daily volume per mailbox. The data feed is the part worth automating. For the copy side, see how to personalize outbound sales emails.
If you prefer a visual runner, n8n can wait and poll for you. Its Wait node resumes a workflow after a set interval (source: n8n docs, checked September 2026). The full node layout is on the n8n recipe page.
What you get
The step 1 count, run on 2026-10-21 with the MCP tool leadocean_count_leads:
{
"total": 1300,
"totalIsExact": true,
"totalAll": 4945,
"distinctCompanies": 1030,
"credits": 0
}Read it this way. By default the tool counts only people whose best email is verified, catch_all_valid or catch_all. So 1,300 is the mailable audience, and 4,945 is everyone matching the same filters with no email condition. They sit at 1,030 companies.
Next, the step 2 response shape from the API reference, with placeholder values.
{
"success": true,
"data": [
{ "profile_data": { "profile_full_name": "Jane Doe" },
"contact_data": { "has_email": true, "email_status": "verified", "email_type": "work" },
"meta": { "source_ids": { "person_id": "1234567" } } }
],
"meta": { "count": 25, "limit": 25, "nextCursor": "eyJ...placeholder" }
}After the export, mailable.csv has one row per person with the columns you requested.
Troubleshooting
| Error | Cause | Fix |
|---|---|---|
| 401 | Missing or wrong x-api-key header. | Check $LEADOCEAN_API_KEY is exported and sent as the x-api-key header. |
| 429 | Above the plan's rate limit: 5 a second on Free, 200 on Agency. | Read Retry-After, sleep that long, and retry. |
| 402 | Out of records. Free has 1,000 for life. | Lower limit, or move to a paid plan, which runs on a fair-use pace. |
| Zero rows | A filter value is not an exact enum string, such as VP Sales for jobLevel. | Resolve it with GET /v1/enums/job_level. Use VP. |
400 export_limit_rows | limit is over 50,000. | Split the pull into several exports. |
429 export_limit_daily | Past 500,000 export rows in a day. | Wait for resetsAt in the response. |
FAQ
What parts of outbound should you automate?
Automate list building, enrichment and refresh. These are the same every week. Leave message writing and reply handling to people.
How many records does a weekly refresh cost?
One record is one person you receive. An export bills every row it writes, so a weekly re-pull of the same audience pays for the same people again. Run it less often or tighten the filters to keep it lower.
Can I run this on the Free plan?
Steps 1 and 2 run on Free, inside the 1,000 one-off records. Bulk CSV export is not on Free, so use paged search with meta.nextCursor instead, or move to Solo.
How often should the list refresh?
The dataset is refreshed monthly, and each record carries its own fetched_at date. So a monthly run matches the data cycle. Run weekly only if your sender needs a steady top-up.
Does LeadOcean send the emails?
No. LeadOcean is the data source: search, enrich and export. You bring the sender. See outbound sales automation for how teams wire the pieces together, and the B2B outbound guide for team structure and cadence.
Start free and size your first outbound list in minutes
Free to start. No credit card. 1,000 records to spend whenever you like.
Get your free API key →