TL;DR
- Search by title, job level and job function, then reveal the email only for the people you will write to. A guessed pattern is a guess.
- It takes about 15 minutes and about 15 LeadOcean records: 10 search rows and 5 reveals. The count in Step 1 is free.
- Read
email_statusbefore you send. Send toverified. Treatcatch_allas a risk you choose, and skip the rest.
Prerequisites
- A LeadOcean account. The free plan gives you 1,000 records, one-off, with no card: start free.
- An API key in your shell:
export LEADOCEAN_API_KEY=...(the app lists your keys). curlandjq. Every request goes tohttps://api.leadocean.iowith thex-api-keyheader.
Step 1: Count the mailable Finance Directors
Count first. It is free and tells you whether the filter is worth running. Send count=true as a query parameter, with limit 1 in the body.
Finance Director emails come from four places. These are the company website, a press release or annual report, a pattern guessed from a colleague's address, and a data provider with a verification status. Websites and reports rarely list a director. The third fails quietly. The fourth is what this guide uses.
Titles vary. One company says "Finance Director", the next says "Director of Finance" or "Director, Financial Planning". The title filter is a keyword match, so list each variant you want. jobLevel and jobFunction keep out managers and VPs whose titles share the words.
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 '{
"title": ["Finance Director"],
"jobLevel": ["Director"],
"jobFunction": ["Finance & Accounting"],
"salesRegion": ["NORAM"],
"limit": 1
}' | jq '.meta | {total, totalIsExact, credits}'Change salesRegion to EMEA, APAC or LATAM to size each region. We ran the same filters through the free MCP count_leads tool on 21 October 2026. These are mailable counts: emailStatus verified, catch_all_valid or catch_all, the default.
| Region | Mailable Finance Directors |
|---|---|
| NORAM | 15,778 |
| EMEA | 11,040 |
| APAC | 3,138 |
| LATAM | 999 |
| All regions | 37,233 |
The four regions add to 30,955. The other 6,278 match the filters but sit outside all four regions. Without the email filter the same query matches 128,794 people, so about 29% have an address worth sending to. Add "Director of Finance" to title to widen the list.
Step 2: Pull a first page of Finance Directors
Run the search without count. Each person returned costs one record, so keep limit small while you test. Add emailStatus to keep only addresses worth sending to.
curl -s -X POST "https://api.leadocean.io/v1/people/search" \
-H "x-api-key: $LEADOCEAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": ["Finance Director"],
"jobLevel": ["Director"],
"jobFunction": ["Finance & Accounting"],
"country": ["US"],
"emailStatus": ["verified"],
"limit": 10
}' > finance-directors.json
jq '.meta | {count, credits, nextCursor}' finance-directors.jsonSearch rows are thin. They carry has_email and email_status but no address. Use meta.nextCursor for the next page and pass it back unchanged.
Step 3: Reveal the email for the people you want
Pick the rows worth contacting and enrich each one by person_id. Send reveal_email=true, or the call returns no address. It costs one record per person, and the reveal adds nothing.
PERSON_ID=$(jq -r '.data[0].meta.source_ids.person_id' finance-directors.json)
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\", \"reveal_email\": true}" > reveal.json
jq '.data.contact_data.contact_emails' reveal.jsonThere is no name plus domain lookup. If you start from a company list, search by domain first, then enrich by person_id.
Step 4: Verify before you send
Check the status on every address. This reads the file from Step 3, so it spends nothing. A pattern such as first.last@acme.com or flast@acme.com only tells you how some company might name mailboxes. It does not tell you the mailbox exists.
If you only have a name and a domain, the pattern route is slow. You try jane.doe@, jdoe@ and jane@, send three emails and learn from the bounces. Each bounce costs sender reputation. Search by domain for the Finance Director instead, and read the status on the address you get back.
jq -r '.data.contact_data.contact_emails[]
| select(.type == "work")
| [.email, .status, .verified_batch_date] | @tsv' reveal.jsonUse this rule:
verified: send.catch_all_valid: the mailbox was confirmed on a domain that accepts everything. Send.catch_all: the domain accepts everything, so a bounce is possible. Send a small batch and watch it.risky,unknown,untested,role,invalid: do not send.
verified_batch_date is the date of the verification run. There is no refund for bounced emails, so this step protects your sender reputation.
Step 5: Export the whole list
When the count looks right, export instead of paging. Bulk CSV export is not on Free, so this step needs a paid plan: Solo is $97 a month and Agency is $497 a month. One request takes up to 50,000 rows, with 500,000 a day. Every row costs one record, so set limit to what you will use.
curl -s -X POST "https://api.leadocean.io/v1/exports" \
-H "x-api-key: $LEADOCEAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "finance-directors-noram-verified",
"filters": {
"title": ["Finance Director"],
"jobLevel": ["Director"],
"jobFunction": ["Finance & Accounting"],
"salesRegion": ["NORAM"],
"emailStatus": ["verified"]
},
"limit": 500,
"preset": "full",
"caps": {"emails": 1}
}'Poll GET /v1/exports/{id} until status is done, then download the CSV. Headers are email_1_address and email_1_status. You can build the same export in the app (app.leadocean.io, Exports page), which shows the record price before you start.
What you get
The free count for the NORAM filter, from the MCP count_leads tool on 21 October 2026:
{
"total": 15778,
"totalIsExact": true,
"totalAll": 43601,
"credits": 0,
"defaults": "Counted only addresses worth sending to (emailStatus: verified or catch_all_valid or catch_all)"
}A search row, as a response shape from the API reference. Values are placeholders and profile_data is abbreviated:
{
"success": true,
"data": [
{
"profile_data": {},
"contact_data": {
"has_email": true,
"has_phone": false,
"email_status": "verified",
"email_type": "work"
},
"meta": { "source_ids": { "person_id": "1234567" } }
}
],
"meta": { "count": 1, "credits": 1, "nextCursor": "..." }
}After the reveal, contact_data.contact_emails holds objects with email, type, status and verified_batch_date, for example jane.doe@acme.com, work, verified.
Troubleshooting
| Error | Cause | Fix |
|---|---|---|
| 401 | Missing or wrong x-api-key header | Send the key in x-api-key, not as a bearer token. Check $LEADOCEAN_API_KEY is set. |
| 429 | Above the plan's rate limit: 5 requests a second on Free, 200 on Agency | Honour the Retry-After header and slow the loop. |
| 402 | Out of records. The 1,000 free records are one-off and do not reset | Count first, lower limit, or move to Solo at $97 a month or Agency at $497 a month. |
Empty data | A filter value that is not an exact enum string, such as Directors instead of Director | Check exact values with GET /v1/enums or the free list_enum_values tool. |
export_limit_rows (400) | limit above 50,000 rows in one export | Split into several exports with narrower filters. |
export_limit_daily (429) | More than 500,000 rows exported in a day | Wait for resetsAt in the error, or ask support. |
FAQ
What title should I search for to find a Finance Director?
Search Finance Director, with jobLevel Director and jobFunction Finance & Accounting. The level and function filters keep out finance managers and VPs. Add Director of Finance or Head of Finance if your buyer uses them.
Does LeadOcean guess email patterns?
No. It returns the address it holds, with a status. If it holds nothing for a person, enrich returns a 404 and costs nothing.
How many records does one Finance Director cost?
One record for the search row and one to reveal the email, so two if you do both. An export row costs one record and includes the address. Counts are free.
Can I find a Finance Director's phone number too?
Yes. GET /v2/people/phone returns numbers. Contact lookups on the v2 endpoints cost one record per call, including a call that finds nothing.
Does the record show past roles?
Yes. Since 2026-10-17 person enrich returns career history in resume_data: past roles with title, company name and dates, plus education and certifications when held. Past employers carry no company_id yet. It helps you spot a director who joined last month and has not settled in.
Where do I go for other roles?
See the guides for an IT Director, an HR Director and a Sales Director. More recipes live in the blog hub. Plans are on the pricing page.
Count the Finance Directors you can mail today, free
Free to start. No credit card. 1,000 records to spend whenever you like.
Get your free API key →