You find a CTO's email by looking it up in a data source first and guessing from a name pattern only when the lookup is empty. A lookup returns an address someone has already tested. A guess is a coin flip you must verify.
CTO addresses come from four places. A data provider holds a work address it collected and checked. A company site, docs page or engineering blog lists a shared inbox, which is rarely the CTO's own. A name pattern gives you a guess. A commit history or open source profile sometimes exposes a personal address, which is a poor fit for business outreach.
Only the first is worth building a process around. The other three are fallbacks, and this guide follows that order.
CTOs are harder than most titles. At a 20-person startup the CTO is a co-founder with an alias and a filtered inbox. At a large company the title may sit three levels below the real technology decision maker. Size the audience first so you know which case you are in.
This guide uses the LeadOcean API. Every step has a request you can run.
TL;DR
- It takes about 15 minutes to size the audience, find one CTO's address and read its status.
- The walkthrough spends about 4 records: up to 3 from the search and 1 from the reveal. Counts are free.
- Send only to
verified,catch_all_validorcatch_all. Anything you guessed gets verified first.
Prerequisites
- A LeadOcean API key from app.leadocean.io. The free plan has 1,000 records, one-off, and no card.
- The key in your shell as
$LEADOCEAN_API_KEY. curlandjq.- A target company domain. The examples use
acme.com.
export LEADOCEAN_API_KEY="paste-your-key-here"Step 1: Size the CTO audience for free
Count how many CTOs you can mail before you spend a record. Send count=true and limit=1 as query parameters. Arrays go comma-separated.
curl -s -G https://api.leadocean.io/v1/people/search \
-H "x-api-key: $LEADOCEAN_API_KEY" \
--data-urlencode "title=CTO,Chief Technology Officer" \
--data-urlencode "jobLevel=C-Team" \
--data-urlencode "salesRegion=EMEA" \
--data-urlencode "emailStatus=verified,catch_all_valid,catch_all" \
--data-urlencode "count=true" \
--data-urlencode "limit=1" | jq '.meta | {total, totalIsExact, countOnly}'salesRegion takes NORAM, LATAM, EMEA or APAC. Swap in country=DE for one market. REST meta.total stops at 100,000, so a big region comes back as a floor with totalIsExact false.
Add employeeRange=11-50 to find startup CTOs, or employeeRange=1001-5000 for mid-size engineering orgs. Each range changes the count, so run it before you decide where to spend records.
Step 2: Find the CTO at your target company
Search by company domain and title. The search returns a thin row with no address in it. The row carries a person_id and flags that say whether we hold an email.
curl -s https://api.leadocean.io/v1/people/search \
-H "x-api-key: $LEADOCEAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"domain": ["acme.com"],
"title": ["CTO", "Chief Technology Officer"],
"jobLevel": ["C-Team"],
"limit": 3
}' | jq '.data[] | {person_id: .meta.source_ids.person_id, contact_data}'Each person returned costs 1 record. Keep limit small. Title values are keywords, so a few rows may be a deputy or an "Associate CTO". Check the title before you pick one.
Small companies often list the technical lead as founder, not CTO. For those, swap the title filter for "seniority": ["founder", "owner"] plus "jobFunction": ["Engineering"]. Sizing with count=true first shows which version returns people.
Step 3: Reveal the address
Send the person_id to enrich with reveal_email set to true. Enrich takes exactly one of linkedin_url, email, phone or person_id. There is no name plus domain lookup, which is why Step 2 comes first.
curl -s https://api.leadocean.io/v1/people/enrich \
-H "x-api-key: $LEADOCEAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"person_id": "PERSON_ID_FROM_STEP_2", "reveal_email": true}' \
| jq '.data.contact_data | {email_status, contact_emails}'This costs 1 record. A person we hold nothing for is a 404 and costs nothing. If you only want a deliverable work address, GET /v2/people/email/work with the same person_id returns one current-employer address or null. It costs 1 record per call, even when it finds nothing.
Step 4: Read the status before you send
The status says how far to trust the address. verified was tested. catch_all_valid means we confirmed the mailbox on a domain that accepts everything. catch_all means the domain accepts everything and the mailbox is unproven.
curl -s https://api.leadocean.io/v1/people/enrich \
-H "x-api-key: $LEADOCEAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"person_id": "PERSON_ID_FROM_STEP_2", "reveal_email": true}' \
| jq -r '.data.contact_data
| select(.email_status | IN("verified","catch_all_valid","catch_all"))
| .contact_emails[0].email'Empty output means the status failed the filter. LeadOcean gives no refund for bounced emails, so this check is your only protection. Treat catch_all as sendable at low volume, not as proof. Our catch-all explainer covers why.
Step 5: Fall back to a name pattern
When the data holds no address, build candidates from the CTO's name and the company domain. Most companies use one of four shapes. Pick the one you can confirm from a known address at the same company.
| Pattern | Example for Jane Doe at Acme |
|---|---|
| first | jane@acme.com |
| first.last | jane.doe@acme.com |
| first initial, last | jdoe@acme.com |
| first initial dot last | j.doe@acme.com |
first="jane"; last="doe"; domain="acme.com"
printf '%s\n' "$first@$domain" "$first.$last@$domain" "${first:0:1}$last@$domain" "${first:0:1}.$last@$domain"Engineering teams are the worst case for guessing. Technical founders often use a short first-name address, a plus alias or a custom domain. A pattern that works for staff may miss them.
Never send to a candidate you have not verified. Run each one through an email verifier you trust first. A domain that accepts all mail cannot confirm any single mailbox, so a guess there stays a guess.
Step 6: Verify and send in small batches
Verify before the first send, then start small. A bad batch to a technical audience gets flagged fast, and a CTO who marks you as spam is gone for good.
Send 20 to 50 messages first and watch the bounce count. If more than a few bounce, stop and re-check the status filter in Step 4. Keep the message short and specific to their stack or team size, not a generic template.
What you get
Step 1 is free. We ran it on 2026-10-21 for each sales region with leadocean_count_leads. The counts are mailable CTOs (verified, catch_all_valid or catch_all, the tool default), not every CTO we hold. The title filter is CTO or Chief Technology Officer with job level C-Team.
| Region | Mailable CTOs | Exact |
|---|---|---|
| NORAM | 71,909 | Yes |
| EMEA | 39,871 | Yes |
| APAC | 16,084 | Yes |
| LATAM | 6,260 | Yes |
Worldwide the same filter counted 171,357, spread across about 115,000 employers. The four regions add up to less, because some records carry a country but no sales region. For one country, country=US counted 67,671 on the same day.
The REST call in Step 1 would show the worldwide figure as a 100,000 floor, because REST meta.total stops there. The MCP tool reported exact totals on the run date.
A revealed person comes back in the shape below. This is the response shape from the API reference, with placeholder values.
{
"success": true,
"data": {
"profile_data": { "...": "name, title, company" },
"contact_data": {
"has_email": true,
"email_status": "verified",
"email_type": "work",
"contact_emails": [
{ "email": "jane.doe@acme.com", "type": "work", "status": "verified" }
]
}
}
}Troubleshooting
| Error | Cause | Fix |
|---|---|---|
| 401 | The x-api-key header is missing, or the key is wrong or revoked. | Copy a fresh key from the app and reload $LEADOCEAN_API_KEY. |
| 402 | Your records are spent. On Free the 1,000 are a one-off allowance and do not reset. | Check GET /v1/account, or move to Solo ($97 a month) or Agency ($497 a month). |
| 429 | Over your plan's rate on one key (5 a second on Free, 50 on Solo, 200 on Agency), or a paid plan's fair-use window is reached. | Wait the Retry-After seconds and retry. GET /v1/account shows which limit applies. |
| Empty results | A filter value is not an exact enum string, such as Chief for C-Team. | Call leadocean_list_enum_values for job_level and pass the exact string. |
| 404 on enrich | We hold nothing for that person. | Nothing was charged. Try another person_id or fall back to Step 5. |
| Export refused | One export is capped at 50,000 rows a request and 500,000 rows a day. | Split the job across requests and days, or narrow the filters. |
FAQ
Is it legal to email a CTO you do not know?
Rules differ by country, and this guide is not legal advice. Check the law where your recipient sits and keep your message relevant to their role. Ask your own counsel about anything in doubt.
Why does the search not show the email?
Search returns a thin row with flags such as has_email and email_status. The address comes from enrich with reveal_email set, and that call costs 1 record.
How many CTOs can I get on the free plan?
You have 1,000 records, one-off. Each CTO costs about 2: 1 from search and 1 from the reveal. That is roughly 500 CTOs, minus any 404s that cost nothing.
Can I pull a whole region at once?
Yes, on a paid plan. Bulk CSV export is included on Solo and Agency, not on Free. The Exports page in the app takes the same filters, shows the record price before you start, and gives you a CSV. POST /v1/exports does the same by API, up to 50,000 rows a request.
Should I search for CTO or VP of Engineering?
It depends on company size. Under about 200 people the CTO usually owns engineering and buying decisions. Above that, a VP of Engineering often runs the team, so try both. Our VP of Engineering guide has the same recipe.
For the same recipe on other titles, see VP of Sales, VP of Marketing and VP of Engineering. More recipes live in the blog hub. Plans are on pricing.
Size your CTO list free, then pull verified addresses
Free to start. No credit card. 1,000 records to spend whenever you like.
Get your free API key →