You get a short script that turns warm leads into new prospects. It reads the companies that already engaged, finds companies with the same industry, size and country in LeadOcean, pulls one buyer at each, and posts them to a Smartlead campaign. Clay's template does the same job with Common Room and RB2B as the warm-lead sources (Clay template, September 2026). This recipe needs no Clay table.
Lookalike here means matching firmographic filters, not a similarity score. LeadOcean does not rank companies by resemblance.
Related: the Smartlead integration, company lookalikes from HubSpot, Salesloft and pricing.
How it works
- Get a free key. Sign up at app.leadocean.io/register. No card, 1,000 records.
- List your warm leads. Export the company domains of people who replied, booked or visited from your CRM or Smartlead. One domain per line in
warm.txt. - Read each seed company. Call
POST /v1/companies/enrichwith the domain. Keep industry, employee range and HQ country. - Search for matches. Call
POST /v1/companies/searchwith those three filters and drop the seed domain. Save the matched domains tolookalike_domains.txt. - Size it first. Add
count=trueandlimit=1as query parameters. That call is free.GET /v1/companies/countis also free and breaks the total down by industry and country. - Pull one buyer per company. Call
POST /v1/people/searchwith the domain and ajobLevel,limit1. Enrich thatperson_idwithreveal_emailfor the address. - Post to Smartlead. Send the rows to the campaign in batches of 400, the documented maximum (Smartlead Add Leads to Campaign, September 2026).
Run the script on a schedule, for example weekly, against the latest warm domains.
Configuration
Seed, then search. Replace acme.com with a warm lead's domain.
curl -X POST https://api.leadocean.io/v1/companies/enrich \
-H "x-api-key: $LEADOCEAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"domain":"acme.com"}'
curl -X POST "https://api.leadocean.io/v1/companies/search?count=true&limit=1" \
-H "x-api-key: $LEADOCEAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"industry":["Software Development"],"employeeRange":["51-200"],"hqCountry":["US"],"excludeName":["Acme"]}'employeeRange takes bands such as 11-50, 51-200 and 201-500, hqCountry takes two-letter codes, and industry takes one of 534 exact strings (API reference, October 2026). Look them up free with leadocean_list_enum_values. From the seed, read company_industries[0].name2, company_employees.number_of_employees_code and company_primary_address.country_code.
The load step. The script is ours; the Smartlead endpoint is theirs.
import os, requests
LO = "https://api.leadocean.io"
H = {"x-api-key": os.environ["LEADOCEAN_API_KEY"]}
SMARTLEAD = f"https://server.smartlead.ai/api/v1/campaigns/{os.environ['CAMPAIGN_ID']}/leads"
KEY = {"api_key": os.environ["SMARTLEAD_API_KEY"]}
def buyer(domain):
# 1 record if someone matches, 0 if the page is empty
rows = requests.post(f"{LO}/v1/people/search", headers=H,
json={"domain": [domain], "jobLevel": ["VP"], "limit": 1}).json().get("data", [])
if not rows:
return None
pid = rows[0]["meta"]["source_ids"]["person_id"]
# 1 record; a 404 costs nothing
r = requests.post(f"{LO}/v1/people/enrich", headers=H,
json={"person_id": pid, "reveal_email": True})
return r.json()["data"] if r.ok else None
leads = []
for domain in open("lookalike_domains.txt").read().split():
p = buyer(domain)
emails = (p or {}).get("contact_data", {}).get("contact_emails") or []
if not emails or emails[0]["status"] not in ("verified", "catch_all_valid"):
continue
role = p["contact_data"]["contact_current_experiences"][0]
leads.append({"email": emails[0]["email"],
"first_name": p["profile_data"]["profile_first_name"],
"last_name": p["profile_data"]["profile_last_name"],
"company_name": role["company_name"],
"custom_fields": {"job_title": role["job_title"], "seed": "warm"}})
for i in range(0, len(leads), 400):
print(requests.post(SMARTLEAD, params=KEY, json={"lead_list": leads[i:i+400]}).json())Each company costs up to 2 records: the search row and the enrichment. GET /v1/companies/{domain}/people has no seniority filter, so people search with domain and jobLevel is the cheaper way to one buyer. Run it on five domains before the full list. Smartlead reads the key from an api_key query parameter, so keep the script out of shared logs.
Field mapping
Smartlead requires email. Every other field is optional (Smartlead docs, September 2026).
| LeadOcean field | Target field (Smartlead) | Note |
|---|---|---|
contact_emails[0].email | email | The script skips anything not verified or catch_all_valid. |
profile_data.profile_first_name | first_name | Merge field for step one. |
profile_data.profile_last_name | last_name | |
contact_current_experiences[0].company_name | company_name | |
contact_current_experiences[0].job_title | custom_fields.job_title | Free text for personalization. |
contact_emails[0].status | custom_fields.email_status | Add it if you want to split catch-all later. |
| Seed company domain | custom_fields.seed | Names the warm lead this match came from. |
Cost at 10,000 rows
For 10,000 contacts, LeadOcean costs Solo at $97 a month, or Agency at $497 a month for teams, with a fair-use pace and no per-record price. Smartlead's Base plan lists 2,000 contacts, so 10,000 leads need Pro, listed at $78 a month with 30,000 contacts and 90,000 sends. The page has a Monthly and Yearly toggle with 17% off yearly, so check which view you are reading (Smartlead pricing, October 2026).
| Item | LeadOcean Free | Solo or Agency |
|---|---|---|
| Records | 1,000 for life, one-off | Fair-use pace |
| Seed, search and buyer | 1 record per company or person returned | Inside the pace |
| Counts | Free | Free |
| Smartlead contacts | 1,000 fits Base | 10,000 needs Pro |
On Free, the 1,000 records are shared by seeds, matched companies and people. Size the list with the free count before you start. Mailboxes and sending domains cost extra on the Smartlead side.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| Search returns the seed itself | The seed's name was not excluded. | Set excludeName and drop the seed domain in code. |
| Fewer than 10 matches | Filters too tight. | Drop hqCountry, then widen the size band, one change at a time. |
| Enum value rejected | Industry or range string is not exact. | Look it up with leadocean_list_enum_values. |
| Rows have no email | No address held, or not verified or catch_all_valid. | Expected. The script skips them. |
skipped_count above zero | Duplicate or unsubscribed leads. | Read skipped_leads for the reason. |
| 402 from LeadOcean | Free records are spent. | Upgrade, or lower the batch. |
| 429 from LeadOcean | Above 5 requests a second on Free. | Sleep between calls. |
FAQ
Is this a real lookalike model?
No. It matches industry, employee range and HQ country from the seed. That is predictable and cheap to rerun. If you need a score, add one in your own code after the search.
Can I run it from Claude instead?
Yes. The MCP server at https://api.leadocean.io/mcp has get_company, search_companies and the free count_companies, with OAuth sign-in and no key pasted. Build the list there, then run the load script.
Why one buyer per company?
One contact per account keeps a campaign from emailing five people at the same firm. Raise the cap once you see reply rates.
Do bounced emails get a credit back?
No. LeadOcean gives no refund for bounces. Read email_status and keep catch-all addresses in a small campaign of their own.
Find lookalikes of your warm leads, free
Free to start. No credit card. 1,000 records to spend whenever you like.
Get your free API key →