Pre-Black Friday: 20% off, for as long as you stayEARLYBFon Solo and AgencyEnds Nov 28Start free

How-to guide

How to Find a CHRO's Email Address (2026)

Size the audience for free, pull the CHROs with a mailable address, reveal the email and check its status before you send.

Get your free API key →Free to start. No credit card. 1,000 records to spend whenever you like.

TL;DR

  • Search by title, job level and job function, then reveal the email only for the people you will actually write to. Do not guess patterns.
  • 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_status before you send. Send to verified. Treat catch_all as 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 dashboard lists your keys).
  • curl and jq. Every request goes to https://api.leadocean.io with the x-api-key header.

Step 1: Count the mailable CHROs

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.

bash
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": ["Chief Human Resources Officer", "CHRO"],
    "jobLevel": ["C-Team"],
    "jobFunction": ["Human Resources"],
    "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.

RegionMailable CHROs
NORAM8,099
EMEA1,489
APAC1,149
LATAM219
All regions12,174

The four regions add to 10,956. The other 1,218 match the title filters but fall outside all four regions. The titles above do not include "Chief People Officer". Add it to title if you want it. Many companies use that title for the same seat, so count both before you decide.

Step 2: Pull a first page of CHROs

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.

bash
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": ["Chief Human Resources Officer", "CHRO"],
    "jobLevel": ["C-Team"],
    "jobFunction": ["Human Resources"],
    "country": ["US"],
    "emailStatus": ["verified"],
    "limit": 10
  }' > chros.json

jq '.meta | {count, credits, nextCursor}' chros.json

Search rows are thin. They carry has_email and email_status but no address. Use meta.nextCursor to fetch the next page, and pass it back unchanged.

Step 3: Reveal the email for the CHROs 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. The reveal itself adds nothing.

bash
PERSON_ID=$(jq -r '.data[0].meta.source_ids.person_id' chros.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.json

There 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. Pattern guessing (first.last@, flast@) gives you an address that may not exist. A found address with a status is better than a guess.

bash
jq -r '.data.contact_data.contact_emails[]
  | select(.type == "work")
  | [.email, .status, .verified_batch_date] | @tsv' reveal.json

Use this rule:

  • verified: send.
  • catch_all: the domain accepts everything, so a bounce is possible. Send in a small batch and watch it.
  • risky, unknown, untested, role, invalid: do not send.

verified_batch_date is the date of the verification run. A null means LeadOcean did not run the check itself. There is no refund for bounced emails, so this step is where you save your sender reputation.

Step 5: Fall back to a pattern only when the lookup is empty

Some CHROs are not in the data, or have no checked address. Then build candidates from the name and the company domain, and verify each one before you send.

bash
first="jane"; last="doe"; domain="acme.com"
printf '%s\n' "$first.$last@$domain" "${first:0:1}$last@$domain" "$first@$domain" "$first${last:0:1}@$domain"

Pick the shape you can confirm from another address you already hold at the same domain. A guess at a catch-all domain always looks valid, so it proves nothing. Skip shared inboxes such as hr@ or people@: they carry the role status and reach a team, not the CHRO.

HR leaders get heavy vendor mail. Keep the first email short, name one people-team problem, and ask one question.

Step 6: Export the whole list

When the count looks right, export instead of paging. Bulk CSV export is not on Free: it is included on Solo ($97 a month) and Agency ($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.

bash
curl -s -X POST "https://api.leadocean.io/v1/exports" \
  -H "x-api-key: $LEADOCEAN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "chro-noram-verified",
    "filters": {
      "title": ["Chief Human Resources Officer", "CHRO"],
      "jobLevel": ["C-Team"],
      "jobFunction": ["Human Resources"],
      "salesRegion": ["NORAM"],
      "emailStatus": ["verified"]
    },
    "limit": 500,
    "preset": "full",
    "caps": {"emails": 1}
  }'

Poll GET /v1/exports/{id} until status is done, then download the CSV. The CSV headers are email_1_address and email_1_status. You can also 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:

json
{
  "total": 8099,
  "totalIsExact": true,
  "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:

json
{
  "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

ErrorCauseFix
401Missing or wrong x-api-key headerSend the key in x-api-key, not as a bearer token. Check $LEADOCEAN_API_KEY is set.
429Above your plan's rate limit: 5 requests a second on Free, 50 on Solo, 200 on AgencyHonour the Retry-After header and slow the loop.
402Out of records. The 1,000 free records are one-off and do not resetCount first, lower limit, or move to Solo at $97 a month or Agency at $497 a month.
Empty dataA filter value that is not an exact enum string, such as C-Level instead of C-TeamCheck the exact values with GET /v1/enums/job_level or the free list_enum_values tool.
export_limit_rows (400)limit above 50,000 rows in one exportSplit into several exports with narrower filters.
export_limit_daily (429)More than 500,000 rows exported in a dayWait for resetsAt in the error, or ask support.

FAQ

What title should I search for to find a CHRO?

Search Chief Human Resources Officer and CHRO, with jobLevel C-Team and jobFunction Human Resources. Add Chief People Officer if you sell to people teams. The level and function filters keep out deputies and HR managers.

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 CHRO 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 see where a CHRO worked before?

Yes. Since October 2026, person enrich returns career history in resume_data: past roles with title, company name and dates, plus education and certifications when we hold them. The current role sits in contact_current_experiences. Use it to see which companies a CHRO worked at before, and mention a past employer only when it bears on your offer. The history comes back in the same 1 record as the reveal.

Can I find a CHRO'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.

Where do I go for other roles?

See the guides for a VP of Sales, a VP of Marketing and a VP of Engineering. The broader method is in how to find business email addresses. Pricing is on the pricing page.

Count the CHROs you can mail today, free

Free to start. No credit card. 1,000 records to spend whenever you like.

Get your free API key →