TL;DR
- Send the profile URL to
GET /v2/people/email/workand you get one checked work email, ornull. No guessing, no pattern lists. - One profile takes about 10 minutes the first time, mostly getting a key. Each lookup spends 1 record of your 1,000 free ones, whether or not it finds an address.
- For a list, loop the same call over a CSV of profile URLs. The rate limit is per plan, per account: 5 requests a second on Free, 50 on Solo, 200 on Agency.
Prerequisites
- 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. The key needs the enrich scope, which Free keys get. curlandjq. For the bulk step, a CSV with onelinkedin.com/in/...URL per line.
export LEADOCEAN_API_KEY="paste-your-key-here"Step 1: Check the profile by hand
Open the profile and look at Contact info first. It is free and takes a minute.
Often there is no email there. That is where the manual way stops scaling: guessing jane.doe@acme.com from the company domain gives you an address nobody has tested.
Before you hand the URL to anything else, clean it. Share links carry tracking parameters that 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/Step 2: Look up one profile
Send the cleaned URL to the work email endpoint. It returns one address at the person's current employer, or data: null.
curl -s -G "https://api.leadocean.io/v2/people/email/work" \
-H "x-api-key: $LEADOCEAN_API_KEY" \
--data-urlencode "linkedin_url=https://www.linkedin.com/in/jane-doe-123abc/" | jqThis endpoint only returns addresses whose deliverability was checked (verified), whose mailbox was confirmed on a catch-all domain (catch_all_valid), or whose domain accepts everything (catch_all). So null means "none that qualify", not "this person has no email".
The call costs 1 record, found or not. See the pricing page for what the free plan covers.
Step 3: Pull the full record when you need more
Use enrich when you want the profile fields, every email and the phone numbers in one response. It takes exactly one of linkedin_url, email, phone or person_id. Set reveal_email to true to get the contact points.
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'A person we hold nothing for is a 404 and costs nothing. A found person costs 1 record, and reveal_email adds no extra charge. The same record carries career history under resume_data: past roles with dates, education and certifications, when we hold them.
Pick the endpoint by job. Use /v2/people/email/work when you only want the address to send to. Use enrich when you want the record to store.
Step 4: Run it over a list
Put one profile URL per line in profiles.csv, with no header row. Loop the work email call and write one JSON line per profile.
while IFS= read -r url; do
clean=$(echo "$url" | sed -E 's/\?.*$//')
curl -s -G "https://api.leadocean.io/v2/people/email/work" \
-H "x-api-key: $LEADOCEAN_API_KEY" \
--data-urlencode "linkedin_url=$clean" \
| jq -c --arg url "$clean" '{url: $url, email: .data.email, status: .data.status}'
done < profiles.csv > results.jsonlRecords add up fast on a loop: 2,000 profiles spend 2,000 records, hits and misses alike. Run 20 profiles first and read the hit rate before you run the rest. On the free plan the loop runs at 5 requests a second at most, so add sleep 0.25 inside it.
On a paid plan, drop the sleep or shorten it. Solo at $97 a month allows 10k enrichments every 5 hours and 50k a week, and each lookup here counts as one. Agency at $497 a month allows 1M a day.
Bulk export is a different tool. POST /v1/exports takes search filters, not a list of profile URLs, and writes up to 50,000 rows a request and 500,000 a day. Use it when you have an audience definition but no URLs yet. Bulk CSV export is not on Free: Solo includes it.
Step 5: Filter on status before you send
An address is not a send list until you have read its status. Keep verified and catch_all_valid. Treat catch_all as a risk you size, not a green light.
jq -c 'select(.email != null and (.status == "verified" or .status == "catch_all_valid"))' \
results.jsonl > sendable.jsonl
wc -l sendable.jsonlLeadOcean gives no refund or credit-back for bounced emails, so this filter is your protection. Each address also carries verified_batch_date, the verification run it was checked in. A null date means the check was not LeadOcean's own, not that the address is unverified.
What to do with the result
Store the profile URL next to the address so you can re-check later. The dataset is refreshed monthly, and each record carries its own fetched_at date. If a send is more than a month old, look the profile up again.
Keep misses in the file too. A null is a decision for a human: try the personal email endpoint, find a different contact at the same company, or drop the lead. Do not guess a pattern address to fill the gap.
If your list has names and companies but no profile URLs, search by domain first with GET /v1/companies/{domain}/people, then enrich by person_id. That route needs no LinkedIn URL at all.
What you get
Response shape from the API reference, with placeholder values. The work email call returns success and data, with no meta object.
{
"success": true,
"data": {
"email": "jane.doe@acme.com",
"status": "verified",
"verified_batch_date": "2026-09-20"
}
}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",
"verified_batch_date": "2026-09-20"
}
],
"contact_phones": [
{ "phone": "+14155550133", "type": "mobile" }
]
}
}
}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 name is x-api-key. |
| 429 | More than your plan's rate limit on one key (5 a second on Free, 200 on Agency) | Honor the Retry-After header, add a short sleep between calls, 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 profile | Nothing was charged. Try the person's email or another identifier, or accept the miss. |
data: null on the work email call | No checked address at the current employer qualifies | The call still cost 1 record. Try enrich, or GET /v2/people/email/personal if a personal address is acceptable for your use. |
| 400 on a search or export | A filter value outside the catalogue, or limit above the ceiling | Resolve enum values with leadocean_list_enum_values or GET /v1/enums. Exports stop at 50,000 rows a request and 500,000 a day, so split a large one. |
FAQ
Can I find an email from a LinkedIn profile for free?
Yes, in two ways. You can look at the profile's Contact info by hand, which costs nothing. Or you can spend records from LeadOcean's free plan: 1,000 records, one-off, no card.
Does a lookup that finds nothing cost a record?
On the v2 contact endpoints, yes: 1 record per call, including a call that finds nothing. On POST /v1/people/enrich, a person we hold nothing for is a 404 and costs nothing.
Do I need the person's name or company domain?
No. The profile URL alone is enough. The API has no lookup by name plus domain: search by domain first, then enrich by person_id.
How do I know the email will not bounce?
Read the status field. verified means the address passed a deliverability check, and catch_all_valid means the mailbox was confirmed on a catch-all domain. Nothing is a guarantee, and bounced emails are not refunded.
Where do I go for other sources?
See the blog hub and the guides for finding an email from Twitter, GitHub and Crunchbase.
Turn your first LinkedIn profile into a checked work email
Free to start. No credit card. 1,000 records to spend whenever you like.
Get your free API key →