TL;DR
- Sales Navigator shows an email only when the member shared one. For everyone else, send the lead's LinkedIn URL to
POST /v1/people/enrichand read back a work email with a status. - One lead takes about 15 minutes the first time, mostly getting a key. A lookup spends 1 record of your 1,000 free ones. A lead we hold nothing for is a 404 and costs nothing.
- For a list, loop the same call over a CSV of profile URLs. A 20-lead test spends up to 20 records.
Prerequisites
- A Sales Navigator lead or saved list. You need the people, not a login for anything else.
- A LeadOcean account and key from app.leadocean.io. The free plan has no card and 1,000 records, one-off.
- The key in your shell as
$LEADOCEAN_API_KEY. Free keys get the enrich scope. curlandjq. For the bulk step, a CSV with one profile URL per line.
export LEADOCEAN_API_KEY="paste-your-key-here"Step 1: Check the lead page by hand
Open the lead page and look at the contact block. It costs nothing and takes a minute. Per LinkedIn's help page (September 2026), contact details show only when the member chose to share them.
On a lead page (September 2026), that block can also hold CRM data and notes you or a teammate typed in. For most leads it is empty. FullEnrich puts the share of profiles with a visible email at 4 to 5% (October 2026).
That is where the manual way stops. Guessing jane.doe@acme.com from the company domain gives you an address nobody has tested.
Step 2: Get the profile URL
The lookup key is the lead's LinkedIn URL. Copy the public linkedin.com/in/... address and strip the tracking parameters. Share links carry them and they add nothing.
echo "https://www.linkedin.com/in/jane-doe-123abc/?utm_source=share&utm_medium=member_desktop" \
| sed -E 's/\?.*$//'https://www.linkedin.com/in/jane-doe-123abc/Sales Navigator pages use their own /sales/lead/ addresses. Those are not profile URLs. The enrich docs ask for a LinkedIn profile URL, so open the lead's LinkedIn profile from the lead page and copy the /in/ address.
Step 3: Look up one lead
Send the URL to the enrich endpoint. It takes exactly one of linkedin_url, email, phone or person_id. Set reveal_email to true to get the address back.
curl -s -X POST "https://api.leadocean.io/v1/people/enrich" \
-H "x-api-key: $LEADOCEAN_API_KEY" \
-H "content-type: application/json" \
-d '{
"linkedin_url": "https://www.linkedin.com/in/jane-doe-123abc/",
"reveal_email": true
}' | jq '.data.contact_data | {has_email, email_status, email: .contact_emails[0].email}'A found person costs 1 record and reveal_email adds nothing. A person we hold nothing for is a 404 and costs 0.
If you only want the address to send to, GET /v2/people/email/work with the same linkedin_url returns one checked work email or null. It costs 1 record per call, including a call that finds nothing. Pick enrich when you want the record to store. Pick the v2 call when you only want the inbox.
Step 4: Read the status before you trust it
An address is not a send list until you have read its status. verified means a deliverability check accepted it. catch_all_valid means the mailbox was confirmed on a catch-all domain. catch_all means the domain accepts everything, so the mailbox cannot be confirmed.
jq -r '.data.contact_data
| select(.email_status == "verified" or .email_status == "catch_all_valid")
| .contact_emails[0].email' lead.jsonDrop risky, role, derived, untested, disposable, spam_trap and invalid. LeadOcean gives no refund or credit-back for bounced emails, so this filter is your protection.
Step 5: Run the whole list
Export your saved list's profile URLs to leads.csv, one per line, no header. Loop the enrich call and write one JSON line per lead. Test 20 first and read the hit rate before the rest.
head -20 leads.csv | while IFS= read -r url; do
clean=$(echo "$url" | sed -E 's/\?.*$//')
curl -s -X POST "https://api.leadocean.io/v1/people/enrich" \
-H "x-api-key: $LEADOCEAN_API_KEY" \
-H "content-type: application/json" \
-d "{\"linkedin_url\":\"$clean\",\"reveal_email\":true}" \
| jq -c --arg url "$clean" '{url: $url,
email: .data.contact_data.contact_emails[0].email,
status: .data.contact_data.email_status}'
sleep 0.25
done > results.jsonlThe sleep 0.25 keeps you under 5 requests a second, the Free plan limit. Agency at $497 a month allows 200 a second. Solo at $97 a month allows 50, includes bulk CSV export and covers one person.
Loops spend records fast. A hit costs 1 and a miss on enrich costs 0, so the 404s in your test tell you the real rate. For the stages before this one, see how to find a VP of Sales email and the profile-to-email guide.
Step 6: Skip the URLs when you only have a filter
If you have a Sales Navigator search but no URLs, rebuild it as API filters and size it for free. Send count=true as a query parameter with limit 1. It costs 0 records and meta.total is capped at 100,000.
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": ["US"],
"limit": 1
}' | jq '.meta | {total, countOnly}'Then use POST /v1/exports for the file. One request takes up to 50,000 rows and an account takes 500,000 a day. The full filter mapping is in Does Sales Navigator provide email addresses.
Step 7: Store the result next to the lead
Keep the profile URL, the address, the status and the lookup date in one row. Sales Navigator lists age fast, so a row older than a month deserves a second lookup before a send.
Keep the misses too. A missing address is a decision for a human: try GET /v2/people/email/personal if a personal address suits your use, find another contact at the same company, or drop the lead. Do not guess a pattern address to fill the gap.
What you get
Response shape from the API reference (September 2026), with placeholder values. The enrich call returns the contact points under data.contact_data. Phones come back as contact_phones[].phone.
{
"success": true,
"data": {
"contact_data": {
"has_email": true,
"has_phone": true,
"email_status": "verified",
"email_type": "work",
"contact_emails": [
{ "email": "jane.doe@acme.com", "type": "work", "status": "verified" }
],
"contact_phones": [
{ "phone": "+14155550133", "type": "mobile" }
]
}
}
}Without reveal_email, contact_emails and contact_phones are absent. has_email and has_phone still tell you whether a value exists.
Troubleshooting
| Error | Cause | Fix |
|---|---|---|
| 401 | Missing, wrong or revoked key | Re-copy the key from app.leadocean.io and re-export $LEADOCEAN_API_KEY. The header is x-api-key. |
| 429 | More than your plan's rate limit on one key (5 a second on Free, 200 on Agency) | Honor Retry-After, add a short sleep, or run fewer workers. |
| 402 | The free 1,000 records are spent | Check GET /v1/account, or move to Solo at $97 a month or Agency at $497 a month. |
| 404 on enrich | We hold no record for that lead | Nothing was charged. Try the person's email or another identifier, or accept the miss. |
| Empty search results | A filter value outside the catalogue, such as Vice President | Enum values are exact strings. Use VP and check GET /v1/enums. |
| Export refused | The request is over 50,000 rows or the day is over 500,000 | Split the export across requests or days. |
FAQ
Does Sales Navigator show email addresses?
Only when the member shared one. Per LinkedIn's help page (September 2026), contact details appear when the member chose to share them. For everyone else you need a data source.
Can I find a Sales Navigator email for free?
Yes, in two ways. Look at the lead page by hand, which costs nothing. Or spend records from LeadOcean's free plan: 1,000 records, one-off, no card.
Does a lookup that finds nothing cost a record?
On POST /v1/people/enrich, no. A 404 is free. On the v2 contact endpoints, yes: 1 record per call, including one that finds nothing.
Do I need the person's name or company domain?
No. The profile URL is enough. There is no name plus domain lookup: search by domain first, then enrich by person_id.
How fresh is the email?
The dataset is refreshed monthly and each record carries its own fetched_at date. Check it on anyone you plan to email, because people change jobs.
Turn your first Sales Navigator lead into a checked work email
Free to start. No credit card. 1,000 records to spend whenever you like.
Get your free API key →