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

Integrations

LeadOcean + lemlist: Push Verified and Catch-All Leads to Separate Campaigns

Search and export in LeadOcean, split the CSV by email status, and create each lead in the right lemlist campaign with one API call per row.

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

You get a small pipeline. A LeadOcean export is split by email_1_status, then created as leads in two lemlist campaigns. Verified emails go to your main campaign. Catch-all emails go to a second campaign with a lower daily limit. LeadOcean's free plan has 1,000 records for a first run.

Related: the integrations hub, Smartlead, Salesloft, Reply.io and pricing.

How it works

Five steps. lemlist's API creates one lead per request, so the loop lives in a script (source: lemlist Create Lead in Campaign, September 2026).

  1. Get both keys. In the LeadOcean app, copy a key from API keys. In lemlist, open Settings, then Integrations, then Generate a new API key. lemlist shows it once.
  2. Size the list. Call search with count=true and limit=1. It is free.
  3. Export. POST your filters to /v1/exports with emailStatus set to verified, catch_all_valid and catch_all. You can build the same export on the Exports page of the app, which shows the record price before you start.
  4. Create two campaigns in lemlist. One for verified, one for catch-all. Copy each campaign id from the URL.
  5. Run the script. It reads the CSV and posts each row to the campaign that matches its email status.

Why split: a catch-all domain accepts every address at the SMTP level, so a bounce can hide until later. Send those at lower volume and watch bounces. LeadOcean returns 13 email statuses, and risky, unknown, invalid, role, disposable and spam_trap stay out of this recipe.

Configuration

Step 3, the export. Several values in one filter join with OR.

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

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

Step 5, the push. lemlist uses HTTP Basic auth with an empty username and your key as the password. Its limit is 20 requests per 2 seconds per key (source: lemlist authentication and rate limits, September 2026). The script is ours. It sleeps 0.15 seconds between calls and honors Retry-After.

python
import csv, os, time, requests

AUTH = ("", os.environ["LEMLIST_API_KEY"])
CAMPAIGNS = {
    "verified": os.environ["LEMLIST_CAMPAIGN_VERIFIED"],
    "catch_all_valid": os.environ["LEMLIST_CAMPAIGN_CATCHALL"],
    "catch_all": os.environ["LEMLIST_CAMPAIGN_CATCHALL"],
}

for r in csv.DictReader(open("people.csv")):
    camp = CAMPAIGNS.get(r["email_1_status"])
    if not camp:
        continue
    body = {
        "email": r["email_1_address"],
        "firstName": r["profile_first_name"],
        "lastName": r["profile_last_name"],
        "jobTitle": r["current_job_title"],
        "companyName": r["current_company_name"],
        "companyDomain": r["current_company_domain"],
        "linkedinUrl": r["profile_url"],
        "emailStatus": r["email_1_status"],
    }
    url = f"https://api.lemlist.com/api/campaigns/{camp}/leads/?deduplicate=true"
    resp = requests.post(url, auth=AUTH, json=body)
    if resp.status_code == 429:
        time.sleep(int(resp.headers.get("Retry-After", 2)))
        resp = requests.post(url, auth=AUTH, json=body)
    if resp.status_code >= 400:
        print(r["email_1_address"], resp.status_code, resp.text[:120])
    time.sleep(0.15)

deduplicate=true refuses a contact that already has a lead in another campaign (409 LEAD_ALREADY_IN_OTHER_CAMPAIGN). A repeat in the same campaign returns 400 LEAD_ALREADY_IN_CAMPAIGN. Neither writes anything. The script prints refusals so you can review them.

Field mapping

lemlist's lead body takes email, firstName, lastName, companyName, jobTitle, linkedinUrl, phone and companyDomain. Any other key is stored as a custom variable you can use as {{emailStatus}} in a template (source: lemlist Create Lead, September 2026).

LeadOcean fieldlemlist fieldNote
email_1_addressemailBest address first.
email_1_statuscustom emailStatusAlso picks the campaign. Not a built-in field.
profile_first_namefirstNameEmpty if the profile has no split name.
profile_last_namelastName
current_job_titlejobTitle
current_company_namecompanyNameReaches the shared company record in lemlist.
current_company_domaincompanyDomainGood dedupe key.
profile_urllinkedinUrl
phone_1_numberphoneAdd to columns if you want it. Phones need Solo or above for bulk export.
person_idcustom leadoceanIdAdd to columns to re-match later.

Column ids come from GET /v1/exports/columns, which needs no key. Career history is there too. Add past_1_job_title and past_1_company_name to columns, and send them as custom variables if a template mentions a past employer. lemlist stores each extra key you send as a custom variable.

Cost at 10,000 rows

LeadOcean covers 10,000 rows on Solo at $97 a month. lemlist's Email plan is $69 a month, $55 billed yearly, with 50,000 emails a month (source: lemlist pricing, October 2026). That is $166 a month in total, or $152 with lemlist billed yearly. The sum is ours.

ItemFree LeadOceanPaid LeadOcean
LeadOcean1,000 records, one-off, no cardSolo $97 a month. Agency $497 a month for teams and 50k rows a file
Rows you can exportUp to 1,000 through search and enrich. Bulk CSV export is not on Free10,000 in one export on either plan (one export holds up to 50,000 rows)
lemlist Email$69 a month, $55 billed yearlySame
lemlist Multichannel$109 a month, $87 billed yearlySame
lemlist lead-data add-onNot neededNot needed

lemlist sells its own lead data as credits. Its add-on is $20 a month ($16 billed yearly) for 2,000 credits, which it says is 400 emails or 100 phones (lemlist pricing, October 2026). You skip that here because the email comes from LeadOcean. LeadOcean has no credits or overage invoice: paid plans run on a fair-use pace. lemlist's pricing page lists "CRM integrations & API" in the Email plan, so the Email plan is enough for this recipe.

A 10,000-row run is one export and about 10,000 lemlist calls. The lemlist side is paced by its rate limit, so run it in the background and keep the printed refusals as your review list.

Troubleshooting

SymptomCauseFix
401 from lemlistBearer header or a key without the leading colonUse Basic auth with an empty username. In curl: -u ":$LEMLIST_API_KEY".
429 from lemlistMore than 20 requests in 2 seconds on one keySleep between calls and read Retry-After. At 0.15 seconds a row, 10,000 rows take about 25 minutes.
400 LEAD_ALREADY_IN_CAMPAIGNThe email is already a lead in that campaignSkip it. It is a repeat, not an error.
409 LEAD_ALREADY_IN_OTHER_CAMPAIGNdeduplicate=true and the contact sits in another campaignDrop the parameter if you want the overlap, or remove the lead first.
403 "permission to create contacts"The key's user cannot create contacts or companiesGive that user the permission in lemlist, or use another key.
Empty email_1_address columnThe key lacks the enrich scope, or the row has no emailUse a key with both scopes. Free keys get both inside the 1,000 records.
Bounces from the catch-all campaignCatch-all domains accept mail they later rejectLower the daily limit on that campaign. LeadOcean does not refund bounces, so read email_status first.

FAQ

Can I send both statuses to one campaign?

Yes, but you lose the volume control. Catch-all domains bounce later and more often, and one campaign has one daily limit. Two campaigns keep the verified list clean.

Does lemlist find emails for me?

lemlist sells email and phone lookup as credits. Its create-lead call has findEmail, verifyEmail and findPhone flags, all off by default. With LeadOcean you already hold the address and its status, so leave them off.

Can I use an MCP client instead of a script?

LeadOcean has a hosted MCP server with 13 tools, including count_leads and export_leads. lemlist documents its own MCP server for Claude, ChatGPT, Cursor and others (lemlist MCP setup, October 2026). We did not test them together, so the script stays the reliable path for 10,000 rows.

How do I size the list before I spend records?

Use the MCP count_leads tool or a search with count=true and limit=1. Both are free. A count with an emailStatus filter is a count by that status, not a promise of mailable leads.

Fill lemlist with leads you can send to

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

Get your free API key →