TL;DR
- Six API calls cover the job: size a list, search it, enrich a LinkedIn URL, pull a phone, export in bulk, check status.
- It takes about 20 minutes. Sizing costs 0 records. Each enrichment or returned row costs 1 record, and the free plan has 1,000.
- Send the
linkedin_urltoPOST /v1/people/enrichwithreveal_emailset to true. The email and phones come back in one record.
This is the short version. For a wider tool comparison, read 8 contact enrichment tools that find emails and phone numbers fast.
Prerequisites
- A LeadOcean account. The free plan needs no card: 1,000 records, 5 requests a second.
- An API key from app.leadocean.io, exported as
$LEADOCEAN_API_KEY. curlandjq, or any HTTP client.- A list of LinkedIn profile URLs, or a target audience you can describe with filters.
Step 1: Size the audience for free
Count before you spend. A count call returns the total and costs 0 records.
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":["CA"],"limit":1}'count=true goes in the query string, not the JSON body. limit 1 goes in the body. meta.total is capped at 100,000, and the data array comes back empty with meta.countOnly true. Enum values are exact strings. Check them with GET /v1/enums if a filter returns nothing.
Step 2: Search for the people
Search returns profiles and email flags, not addresses. The body is flat JSON, filters are arrays, and limit runs from 1 to 100.
curl -s https://api.leadocean.io/v1/people/search \
-H "x-api-key: $LEADOCEAN_API_KEY" \
-H "content-type: application/json" \
-d '{"jobLevel":["VP"],"jobFunction":["Sales & Business Development"],"country":["CA"],"limit":25}' \
| jq '.meta.nextCursor'Pass the returned nextCursor back as cursor to get the next page. One search pages to 10,000 rows. Past that, meta.depthCapped is true, so narrow the filters. Each returned person costs 1 record.
Step 3: Enrich one LinkedIn URL
This is the call most LinkedIn prospecting needs. Send exactly one identifier: linkedin_url, email, phone or person_id.
curl -s 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-example","reveal_email":true}'reveal_email returns every email and phone held for the person at no extra record. The enrichment costs 1 record. A person we hold nothing for returns 404 and costs nothing. There is no name plus domain lookup: search by domain first, then enrich by person_id.
Step 4: Pull a phone number on its own
If you have a profile URL or a person_id and only want the phone, call the phone endpoint. It takes exactly one of linkedin_url or person_id.
curl -s -G "https://api.leadocean.io/v2/people/phone" \
-H "x-api-key: $LEADOCEAN_API_KEY" \
--data-urlencode "linkedin_url=https://www.linkedin.com/in/jane-doe-example"The answer is data.phones, best first, each with phone and type, and found: false when we hold none.
The v2 contact endpoints cost one record per call, including a call that finds nothing. Use them when you know the person is in the data. Use enrich when you are not sure. For the reverse direction, see find email from phone number.
Step 5: Export the whole list as CSV
For hundreds or thousands of rows, export instead of looping. An export takes up to 50,000 rows a request and 500,000 a day. Bulk CSV export is not on the free plan.
curl -s https://api.leadocean.io/v1/exports \
-H "x-api-key: $LEADOCEAN_API_KEY" \
-H "content-type: application/json" \
-d '{"filters":{"jobLevel":["VP"],"jobFunction":["Sales & Business Development"],"country":["CA"]},"limit":5000,"preset":"full"}'The body takes the people search filters under filters, a row limit and a column preset (full includes emails and phones). Poll GET /v1/exports/{id}, then download from GET /v1/exports/{id}/download.
You can also do this in the app at app.leadocean.io: the Exports page has a filter builder, a column picker and a price preview before you start. Numbered columns come back as email_1_address, email_1_status and phone_1_number.
Step 6: Check email status before you send
Read email_status on every row. The mailable values are verified, catch_all_valid and catch_all. LeadOcean does not refund bounced emails, so filter before the sequencer sees the list.
curl -s https://api.leadocean.io/v1/people/search \
-H "x-api-key: $LEADOCEAN_API_KEY" \
-H "content-type: application/json" \
-d '{"jobLevel":["VP"],"country":["CA"],"emailStatus":["verified"],"hasPhone":true,"limit":25}'Your outreach tool (a sequencer, a CRM, a dialer) is separate. LeadOcean supplies the data and does not run the sequence.
What you get
A free sizing run through the MCP tool leadocean_count_leads on 2026-10-21, filters jobLevel VP, jobFunction Sales & Business Development, country CA:
{
"total": 5574,
"totalIsExact": true,
"totalAll": 18800,
"distinctCompanies": 3873,
"credits": 0
}total counts mailable people (verified, catch_all_valid or catch_all). totalAll is the same filters with no email condition. The call spent 0 records.
An enrichment response has this shape (response shape from the API reference, placeholder values):
{
"success": true,
"data": {
"profile_data": { "...": "name, headline, location" },
"contact_data": { "...": "emails, contact_phones[].phone" },
"resume_data": { "experiences": [], "educations": [] }
},
"meta": { "credits": 1, "key_hash": "..." }
}Since October 2026, resume_data carries past roles, education and certifications when held. Past employers have no company_id yet.
Troubleshooting
| Error | Cause | Fix |
|---|---|---|
| 401 | Missing or wrong x-api-key | Check $LEADOCEAN_API_KEY is exported and the header name is exact |
| 429 | Above the plan's rate limit (5 a second on Free, 200 on Agency) | Honor Retry-After, or move to a higher plan |
| 402 | The 1,000 Free records are spent | Upgrade at pricing. Paid plans slow down at their pace instead of stopping. |
| Empty results | Enum value has the wrong case or spelling | Look it up in GET /v1/enums, for example VP, not vp |
| 404 on enrich | Nothing held for that identifier | Free. Try email or search by domain and use person_id |
| Export refused | Over 50,000 rows a request or 500,000 a day, or on Free | Split the request, or use a paid plan |
FAQ
Can I get a phone number from a LinkedIn URL?
Yes. Enrich the linkedin_url with reveal_email true, and phones come back in contact_phones[].phone when held. Use GET /v2/people/phone for the phone alone.
How many records does this spend?
Sizing is free. Enrichment and each returned search row cost 1 record. A 404 costs nothing. Enriching 50 profiles spends at most 50 of your 1,000 free records.
Does LeadOcean scrape LinkedIn live?
No. It answers from its own dataset, which is refreshed monthly. Each record carries its own fetched_at date. For removal of your own number from LinkedIn, see the privacy guide.
Can an AI agent run these calls?
Yes. The hosted MCP server at https://api.leadocean.io/mcp exposes 13 tools, including free leadocean_count_leads and leadocean_list_enum_values.
What does it cost at volume?
Solo is $97 a month, Agency is $497 a month. Paid plans have a fair-use pace, not credits. See pricing.
Turn your LinkedIn list into emails and phones
Free to start. No credit card. 1,000 records to spend whenever you like.
Get your free API key →