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

How-to guide

How to Find an Email From a Company Domain (2026)

Start from acme.com and end with a work email that has a status. Manual first, then the API, then a bulk export.

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

TL;DR

  • A domain alone gives you a mail server, not a person. You need a name at that domain first, then an address that has been checked.
  • It takes about 10 minutes for one person. The API route spends about 4 LeadOcean records per person: 3 for a search at limit 3, plus 1 for the work-email lookup.
  • For a list, skip the loop and run one export. Each row costs one record, and you read email_status before you send.

Prerequisites

  • A LeadOcean account from app.leadocean.io. The free plan has no card and gives 1,000 records, one-off. Step 6 needs bulk CSV export, which is not on Free: it starts on Solo ($97 a month).
  • curl, jq and dig.
  • An API key in your shell as $LEADOCEAN_API_KEY. Free keys get both scopes, search and enrich.
bash
export LEADOCEAN_API_KEY="paste-your-key-here"

Step 1: Check where the domain's mail goes (the manual way)

Mail for a domain is routed by its MX records, so look them up first. This tells you whether the domain takes mail at all, and which provider hosts it (RFC 5321, September 2026).

bash
dig +short MX acme.com

No MX record usually means no working inboxes at that domain. Stop there. If it returns records, the domain can receive mail, but you still have no name and no address.

The manual route from here is slow. Find a person on the company site or a profile, guess a pattern, then verify the guess.

Step 2: Guess the pattern and know what a guess is worth

A common pattern is first name and last name joined by a dot. This script prints the usual candidates for one placeholder name.

bash
first="jane"; last="doe"; domain="acme.com"
for p in "$first.$last" "$first" "${first:0:1}$last" "$first$last" "${first:0:1}.$last"; do
  echo "$p@$domain"
done

Every line is a guess. A domain that accepts all mail returns the same answer for a real and a fake address, so a guess can look fine and still bounce. LeadOcean calls those domains catch_all, and marks a confirmed mailbox on one as catch_all_valid.

Use this route for one or two people. For anything more, use the API.

Step 3: Size the audience at the domain for free

Ask how many people we hold at the domain before you pull a record. A search with count=true as a query parameter and limit at 1 is a free sizing call. Send the domain as an array.

bash
curl -s "https://api.leadocean.io/v1/people/search?count=true" \
  -H "x-api-key: $LEADOCEAN_API_KEY" -H "content-type: application/json" \
  -d '{
    "domain": ["acme.com"],
    "jobLevel": ["C-Team", "VP"],
    "emailStatus": ["verified", "catch_all_valid", "catch_all"],
    "limit": 1
  }' | jq '.meta | {total, totalIsExact, credits}'

The count tells you if the domain is worth a search. A zero means we hold nobody at that level there, so stop and save the records.

Step 4: Search by domain and title

There is no domain-only email lookup in the API, because an address belongs to a person. Search the domain with a title keyword, and keep limit small. Each person returned costs one record.

bash
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": ["Head of Sales", "VP Sales"],
    "limit": 3
  }' | jq '{count: .meta.count, credits: .meta.credits,
         people: [.data[] | {name: .profile_data.full_name, person_id: .meta.source_ids.person_id}]}'

A search row is thin: name, headline, current role, plus flags such as has_email and email_status. It carries no address. Each row has a person_id at meta.source_ids.person_id.

To walk a whole company instead, use GET /v1/companies/acme.com/people. It pages with a cursor and stops at 10,000 rows.

Step 5: Get the work email for one person

Send the person_id to the work-email endpoint. It returns one address, or null. It returns only an address at the person's current employer that is verified, catch_all_valid or catch_all.

bash
PERSON_ID="paste-person_id-from-step-4"

curl -s "https://api.leadocean.io/v2/people/email/work?person_id=$PERSON_ID" \
  -H "x-api-key: $LEADOCEAN_API_KEY" | jq '.data'

This costs one record per call, including a call that finds nothing. A null means no address qualifies, not that the person has no email. If you want every email and phone we hold in one go, call POST /v1/people/enrich with reveal_email set to true instead. It also costs one record.

Step 6: Do a whole list of domains with an export

For more than a handful of people, export instead of looping. Bulk CSV export needs a paid plan: Solo at $97 a month, or Agency at $497 with 50,000 rows a file. An export writes one CSV with numbered columns such as email_1_address and email_1_status. A request takes up to 50,000 rows and a day takes up to 500,000.

bash
curl -s https://api.leadocean.io/v1/exports \
  -H "x-api-key: $LEADOCEAN_API_KEY" -H "content-type: application/json" \
  -d '{
    "name": "VP and C-team at three domains",
    "filters": {
      "domain": ["acme.com", "example.com", "example.org"],
      "jobLevel": ["C-Team", "VP"],
      "emailStatus": ["verified", "catch_all_valid", "catch_all"]
    },
    "limit": 100,
    "preset": "full"
  }' | jq '.data | {id, status, requested, estimatedTotal}'

The export reserves up to your limit when it starts, and each row written costs one record. The full preset reveals emails, so the key needs the enrich scope. Poll GET /v1/exports/{id}, then fetch GET /v1/exports/{id}/download when the status is done.

The app does the same on its Exports page, with a filter builder, a column picker and the record price before you start. For a longer list, read how to find emails from a list of domains.

What you get

The Step 3 count is a real run. We ran it on 2026-10-21 with leadocean_count_leads, which spends 0 records, for C-Team and VP people at acme.com, our placeholder domain.

json
{
  "total": 9,
  "totalIsExact": true,
  "totalAll": 198,
  "distinctCompanies": 2,
  "credits": 0,
  "unsupportedFilters": []
}

That is 9 people at the default mailable filter (verified, catch_all_valid or catch_all). The same filters with no email condition match 198 people (totalAll). Quote the mailable figure when you plan sends, and say which one you used.

The work-email lookup returns the shape below. This is the response shape from the API reference, with placeholder values.

json
{
  "success": true,
  "data": {
    "email": "jane.doe@acme.com",
    "status": "verified",
    "verified_batch_date": "2026-09-20"
  }
}

A verified_batch_date means we ran the check. A null there means the check came from a source we hold to the same standard, not that the address is unverified. LeadOcean gives no refund or credit-back for bounced emails, so read status before you send.

Troubleshooting

ErrorCauseFix
401Missing, wrong or revoked keyRe-copy the key from app.leadocean.io and export $LEADOCEAN_API_KEY again.
429Above your plan's rate: 5 requests a second on Free, 50 on Solo, 200 on Agency, per accountWait the Retry-After seconds, then slow the loop, or use an export for large batches.
402The 1,000 free records are spentCheck what is left with GET /v1/account, or move to Solo ($97 a month) or Agency ($497 a month) on pricing. Free records do not reset.
Search returns no rowsA filter value outside the catalogue, such as a job level of "VP Sales"Check exact strings with GET /v1/enums or leadocean_list_enum_values. Job level is VP, not a free-text title.
data: null on the work emailNo address at the current employer is verified, catch_all_valid or catch_allCosts one record. Try POST /v1/people/enrich with reveal_email, or pick another person.
400 export_limit_rowsThe export limit is above 50,000 rowsSplit the audience by domain group or title and run several exports.

FAQ

Can I find an email from a domain alone?

Not as one address. A domain has many people. Search the domain with a title or job level, then look up the work email for the person you pick.

How many records does it cost?

One per person returned by a search and one per work-email call. Counts are free. A search at limit 3 plus one lookup spends about 4 records.

How do I know the email is real?

Each address carries a status. The work-email endpoint returns only verified, catch_all_valid or catch_all. Catch-all domains accept any address, so only catch_all_valid confirms the mailbox exists.

Can I start from a company name instead?

Yes, but a domain is cleaner. The search takes companyName as keywords, which can match more than one company. If you hold a person's name and company, read how to find an email from name and company.

Where else can I find domains to start from?

A startup directory works well. See how to find emails from a YC company list. More recipes are in the blog hub, and plans are on pricing.

Turn any company domain into a checked work email, free

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

Get your free API key →