You get a job that searches LeadOcean for people who match your filter. It pulls a verified work email for each and creates them as Leads in Salesforce. It runs outside Salesforce, so it needs no package and no Apex. LeadOcean has no Salesforce app. This is two HTTP calls to https://api.leadocean.io and one to Salesforce. More recipes are on the integrations hub.
How it works
- Trigger. Run the script on a schedule: cron, a CI job or your workflow tool. Daily is enough for most lists.
- Size the list. Send the search with
count=trueas a query parameter andlimit1 first. It is free, so you see the number before you spend a record.
- Search.
POST /v1/people/searchwith your filters andemailStatus: ["verified"]. Page withcursor, up to 100 per page. One search pages to 10,000 rows.
- Enrich. For each
person_id, callPOST /v1/people/enrichwithreveal_email: true. A person with nothing on file returns a 404 and costs nothing.
- Upsert to Salesforce. Send batches of 200 to
PATCH /services/data/vXX.X/composite/sobjects/Lead/LeadOcean_Person_Id__c. Salesforce creates the Lead if the external ID is new and updates it if not (Salesforce, September 2026).
Before step 5, create a text field on Lead named LeadOcean_Person_Id__c and mark it as an External ID. That field is your dedupe key. Run the first pass in a sandbox with limit set to 25.
Configuration
Three calls: search, enrich one result, upsert up to 200 Leads. Acme and Jane Doe are placeholders.
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": ["US"],
"emailStatus": ["verified"],
"limit": 25
}'
curl -s https://api.leadocean.io/v1/people/enrich \
-H "x-api-key: $LEADOCEAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"person_id": "PERSON_ID_FROM_SEARCH", "reveal_email": true}'
curl -s -X PATCH \
"https://MyDomainName.my.salesforce.com/services/data/v68.0/composite/sobjects/Lead/LeadOcean_Person_Id__c" \
-H "Authorization: Bearer $SF_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"allOrNone": false,
"records": [{
"attributes": {"type": "Lead"},
"LeadOcean_Person_Id__c": "PERSON_ID_FROM_SEARCH",
"FirstName": "Jane",
"LastName": "Doe",
"Company": "Acme",
"Email": "jane.doe@acme.com",
"Title": "VP Sales",
"LeadSource": "LeadOcean"
}]
}'Enum values are exact strings. Check GET /v1/enums before changing a filter. Do not set id on the records: this call takes external IDs only. Send LastName and Company on every record. If your org requires other fields, Salesforce returns a per-record error naming them.
Field mapping
| LeadOcean field | Salesforce Lead field | Note |
|---|---|---|
meta.source_ids.person_id | LeadOcean_Person_Id__c | External ID. Stops duplicates on rerun. |
profile_data.profile_first_name, profile_data.profile_last_name | FirstName, LastName | Fall back to profile_full_name if one is empty. |
contact_data.contact_emails[].email | Email | Pick the entry whose type is work. |
contact_data.email_status | Description or a custom field | Keep verified visible. No refund for bounces. |
contact_data.contact_current_experiences[].job_title | Title | Use the first entry, the current role. |
contact_data.contact_current_experiences[].company_name | Company | Send a value on every record. |
| a constant | LeadSource | Set it to LeadOcean so you can report on it. |
Cost at 10,000 rows
On LeadOcean, 10,000 Leads cost 20,000 records: one for each person returned by search and one for each enrich. Free holds 1,000 records for life, so it covers about 500 Leads. Use it to test the loop.
Solo is $97 a month and allows 10k enrichments every 5 hours, so 10,000 rows fit in one window. Agency is $497 a month if a team shares the pace. There are no credits and no overage invoice. Plans are on /pricing.
On Salesforce, 10,000 Leads is 50 collection calls of 200 records. API calls count toward the org's daily total, which for Enterprise Edition is 100,000 plus per-license calls plus add-ons (Salesforce, September 2026). Fifty calls is a rounding error against that.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| 404 on enrich | Nothing held for that person | Skip the row. It costs nothing. |
| 402 from LeadOcean | Free records spent | Move to Solo. Free does not renew. |
| 429 from LeadOcean | Over the plan rate: 5 a second on Free, 50 on Solo | Wait the Retry-After seconds. |
| Empty search | Enum value is not an exact string | Check GET /v1/enums. |
| 300 from Salesforce | The external ID matched more than one Lead | Remove the duplicates, then rerun (Salesforce, September 2026). |
| Some records fail, others pass | allOrNone is false, so errors are per record | Read the errors array in the response. |
FAQ
Does LeadOcean have a Salesforce app?
No. It is a REST API and an MCP server. This recipe calls the REST API from a script.
Why upsert instead of insert?
An upsert on an external ID creates a Lead once and updates it on later runs. A plain insert makes a duplicate each time the job sees the same person.
Can I enrich Leads already in Salesforce?
Yes. Use the Salesforce enrichment recipe, which updates existing records by Id.
Can I send the emails too?
LeadOcean does not send mail. Pair this with a sender, such as Smartlead. The email version of this recipe skips the CRM, and enrich leads shows the same flow as a prompt.
Send your first LeadOcean leads to Salesforce on the free plan
Free to start. No credit card. 1,000 records to spend whenever you like.
Get your free API key →