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

How-to guide

How to Build a LeadOcean, Clay and Salesforce Outbound Stack (List, Verify, Send)

Export a filtered list from LeadOcean, gate risky emails in Clay, and upsert Leads into Salesforce. Sizing the list is free; the CSV export runs on Solo.

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

This stack gives each tool one job. LeadOcean builds the list, Clay gates the risky emails, and Salesforce holds the Leads your reps and senders work. The free plan sizes the list and returns search pages. The CSV export in step 2 needs Solo.

The example audience is VPs of sales in the US. Swap the filters for yours.

TL;DR

  • Time: about 60 minutes for a 200-row pilot, plus whatever your Salesforce admin needs for one custom field and an integration user.
  • Records: 200 to 400 LeadOcean records for the pilot. Sizing the list first is free. The free plan has 1,000 records, one-off, but bulk CSV export is not on Free, so the pilot export needs Solo.
  • Cost per 10,000 contacts: $97 for LeadOcean Solo plus $495 for Clay Growth, about $592 in a month at monthly list prices. Salesforce licences are on top, at whatever your contract says.

Prerequisites

  • A LeadOcean key in $LEADOCEAN_API_KEY. Sign up at app.leadocean.io/register. No card needed. Free covers steps 1 and 6; the step 2 export needs Solo ($97 a month) or Agency ($497 a month).
  • A Clay workspace on Growth or higher. The pricing page lists CRM integrations on Growth and Enterprise only (Clay pricing, September 2026).
  • A Salesforce org where an admin can create a custom field on Lead and an integration user. Test in a sandbox first.
  • curl and Python 3.

Sending is outside this guide. Point your sender at the Leads this stack creates.

Step 1: Size the list before you spend anything

Count first. A search with count=true and limit=1 is free (LeadOcean API reference, September 2026).

bash
curl -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"],"emailType":["work"],"emailStatus":["verified","catch_all_valid","catch_all"],"limit":1}'

Read meta.total. It is capped at 100,000. Filters join with AND, and several values inside one filter join with OR.

Step 2: Export the pilot list

Export 200 rows, not the whole audience. Bulk CSV export is included on Solo and Agency, not on Free. The export is asynchronous and reserves records up front (LeadOcean docs, September 2026).

bash
curl -X POST https://api.leadocean.io/v1/exports \
  -H "x-api-key: $LEADOCEAN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"vp-sales-us pilot","filters":{"jobLevel":["VP"],"jobFunction":["Sales & Business Development"],"country":["US"],"emailType":["work"],"emailStatus":["verified","catch_all_valid","catch_all"]},"limit":200,"columns":["person_id","profile_first_name","profile_last_name","profile_url","current_job_title","current_company_name","current_company_domain","email_status","email_N_address"],"caps":{"emails":1}}'

The answer is a 202 with an export id. Poll GET /v1/exports/{id} until status is done, then download the CSV.

bash
curl -H "x-api-key: $LEADOCEAN_API_KEY" -o leads.csv \
  https://api.leadocean.io/v1/exports/EXPORT_ID/download

Email columns need a key with the enrich scope. One export takes up to 50,000 rows, and an account takes 500,000 rows a day.

Step 3: Verify in Clay with a run condition

Import leads.csv into a new Clay table. Tables hold up to 50,000 rows on Launch and Growth (Clay docs, September 2026). Importing your own data uses no Actions and no Data Credits (Clay docs, September 2026).

LeadOcean already tags each address. Rows marked verified or catch_all_valid go straight through. Only catch_all rows get a second check.

text
Table:        vp-sales-us pilot (import leads.csv)
Gate column:  Ready to load  (formula)
  {{email_status}} == "verified" || {{email_status}} == "catch_all_valid"
Validator:    add an email validation enrichment from Clay's list
  Run condition: {{email_status}} == "catch_all"
Load filter:  email_status is verified or catch_all_valid
              OR the validator column says the address is valid

Clay's conditional runs take a plain-language condition and a Generate button that writes the formula, and the docs show conditions in this {{column}} == "value" shape (Clay docs, September 2026). Check the generated formula against the sample output before you run the column.

Clay's own example shows select validators at 0 Data Credits (Clay docs, September 2026). A validator still costs one Action per row it runs on. Read the cost shown next to the validator in your table before you run it.

Step 4: Prepare Salesforce

Create one custom field and one connection. The field is how Clay matches a row to an existing Lead, so a re-run updates instead of duplicating.

In Salesforce Setup, add these custom fields on Lead.

text
Object:  Lead
Field 1: LeadOcean_Person_ID__c   Text(20), External ID, Unique
Field 2: LeadOcean_Email_Status__c   Text(20)

Clay's upsert action needs an external ID on the object (Clay docs, September 2026).

Then connect Clay. Clay offers User Sign In (OAuth) or Client Credentials for a server-to-server connection (Clay docs, September 2026). Use Client Credentials with an integration user.

text
Clay: Settings > Connections > Add connection > Salesforce
  Method:           Client Credentials
  My Domain URL:    https://acme.my.salesforce.com
  Consumer key:     from your Salesforce external client app
  Consumer secret:  from your Salesforce external client app
Test first:       Settings > Connections > Salesforce Test Env (sandbox)

Clay's FAQ says you cannot undo an update or create from Clay, and that a sandbox connection exists for testing (Clay docs, September 2026). Do the pilot there.

Step 5: Upsert the Leads

Add the Salesforce Upsert object action to the table, choose Lead, and map the columns. Set the run condition to the load filter from step 3.

text
Action:           Salesforce > Upsert object
Salesforce object: Lead
External ID field: LeadOcean_Person_ID__c
Run condition:    {{Ready to load}} == true

Field mapping (Clay column -> Lead field)
  person_id               -> LeadOcean_Person_ID__c
  profile_first_name      -> FirstName
  profile_last_name       -> LastName
  current_company_name    -> Company
  current_job_title       -> Title
  email_1_address         -> Email
  email_status            -> LeadOcean_Email_Status__c
  current_company_domain  -> Website

Turn auto-update off, run 20 rows, and open the Leads in Salesforce. Then run the rest. Clay batches Create, Update and Upsert through Salesforce's Composite API and handles each record on its own, so one failure does not stop the batch (Clay docs, September 2026).

Lead needs a last name and a company, which the export carries. Rows missing either will fail, so filter them out first.

Step 6: Cost a 10,000-contact run and know where each tool stops

Price the run before you scale. This script is arithmetic from list prices, not a quote.

python
rows = 10_000
leadocean_solo = 97          # flat per month, fair-use pace, no per-record price
clay_growth_monthly = 495    # monthly list price
clay_growth_yearly = 446     # billed yearly, per month

salesforce_actions = rows            # 1 Action per CRM export record
validator_actions = rows             # worst case: every row validated
actions = salesforce_actions + validator_actions
assert actions <= 40_000             # Growth entry tier

print("Actions used:", actions, "of 40,000")
print("Monthly list:", leadocean_solo + clay_growth_monthly)
print("Clay yearly view:", leadocean_solo + clay_growth_yearly)
print("Per contact:", round((leadocean_solo + clay_growth_monthly) / rows, 3))

It prints 20,000 Actions, $592 a month at monthly list prices, $543 with Clay billed yearly, and about $0.059 a contact. Salesforce licences are extra.

The inputs come from public pages. Clay's Growth entry tier is $495 a month, or $446 billed yearly, with 40,000 Actions and 6,000 Data Credits a month (Clay pricing, September 2026). CRM exports and HTTP API calls use one Action per record (Clay docs, September 2026). LeadOcean Solo is $97 a month flat with a fair-use pace instead of credits, and 10,000 contacts use 10,000 to 20,000 records. Pace limits are on the LeadOcean pricing page (October 2026). Agency is $497 a month if a team shares the keys.

Solo's pace covers a 10,000-row export, with one catch. Export rows count against the search window, 100k records every 5 hours on Solo. Rows the export enriches also count against the enrichment window, 10k every 5 hours and 50k a week, per the limits docs. With perRow at 2, a 10,000-row export uses the whole 5-hour enrichment window. Nothing stops when you reach it: calls of that kind slow to one a minute until the window resets. Run the export as two 5,000-row jobs a few hours apart, or use Agency, whose enrichment pace is 1M a day.

Salesforce limits API calls per 24 hours. Enterprise Edition gets 100,000 plus a per-license amount (Salesforce Developer Limits, September 2026). Even at one call per record, 10,000 upserts fit.

Where each tool stops:

  • LeadOcean stops at the list. It has no sequencer, dialer, CRM or Chrome extension. email_status is a signal, and there is no refund for bounced emails.
  • Clay stops at orchestration. It holds up to 50,000 rows a table on Growth, and every record it sends to a CRM costs an Action.
  • Salesforce stops at the record. It does not supply contact data, and the edition and license decide what else it can do.

What you get

Step 1 is a free count. We ran the Step 1 audience (VP, Sales & Business Development, US, emailType work) through LeadOcean's MCP leadocean_count_leads tool on 2026-10-21, split by email status.

text
verified + catch_all_valid   total 26988   totalIsExact true   credits 0
catch_all                    total 80524   totalIsExact true   credits 0

Most of this audience is catch_all, so the Clay validator step does real work here. Your mix will differ. A count with an email status filter is a count of that status, not a total.

The export create call answers with the shape below. This is the response shape from the API reference (openapi.json), with placeholder values.

json
{
  "success": true,
  "data": {
    "id": "EXPORT_ID",
    "name": "vp-sales-us pilot",
    "status": "queued",
    "requested": 200,
    "estimatedTotal": 107512,
    "perRow": 1,
    "remaining": 800,
    "pollAfter": 5
  }
}

requested is the records reserved, and estimatedTotal is the free count at creation (here the two counts above added together). perRow is 1 or 2, and remaining is what is left after the reservation. On a paid plan remaining is null, because there is a pace rather than a balance. Read perRow before you scale up.

Troubleshooting

ErrorCauseFix
401 "Missing or malformed x-api-key"The header is missing or wrong.Send x-api-key: $LEADOCEAN_API_KEY.
402 on export createFewer records are left than the export reserves.Lower limit. On Free the 1,000 records are one-off, and bulk CSV export is not on Free.
429More requests a second than your plan allows (5 on Free, 50 on Solo, 200 on Agency), or a daily or period export ceiling.Honour Retry-After. Exports allow 500,000 rows a day.
Zero rows backA filter value is outside the catalogue, such as United States for US.Read the valid values from GET /v1/enums.
400 on export createlimit is over 50,000, or a filter cannot be applied.Split into several exports with narrower filters.
OAUTH_APPROVAL_ERROR_GENERIC in ClayThe Salesforce user has an Integration User or API Only license, or SSO blocks the approval screen.Use Client Credentials, or a full license user (Clay docs, September 2026).

FAQ

Do I need Clay at all?

Not to load Salesforce. You can import the CSV with Salesforce's own tools. Clay earns its place when you want run conditions, validators and other providers on the rows LeadOcean misses.

Why Growth and not Launch?

CRM integrations are listed on Growth and Enterprise only (Clay pricing, September 2026). On Launch you can still import the CSV into Salesforce directly.

How many records does a full run cost?

One export row costs one record, or two when the create response shows perRow of 2. Solo at $97 a month and Agency at $497 a month have no per-record price, only a fair-use pace. See pricing.

Can I send from Salesforce?

This guide stops at the Lead, and what Salesforce can send depends on your edition and add-ons. Ask your Salesforce account team for that. If you already run a sender, compare the VerifyHQ and ReachHQ stack or the Smartlead and Pipedrive stack.

Can I automate this without Clay?

Yes. The n8n and Attio stack wires the same list step to a different CRM. Back to the blog hub.

Build your first LeadOcean list for Clay and Salesforce. Start free.

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

Get your free API key →