A stalled deal usually means one person went quiet, not that the company said no. The fix is a better follow-up plus a second person at the same account.
This guide gives you ten templates and the queries that fill them. It is the short, technical version.
TL;DR
- Ten templates, each tied to one field you can pull by API: a second stakeholder, a past employer, a new title, a headcount band.
- Takes about 25 minutes the first time. Counting the audience is free. The example spends about 11 records.
- Templates are plain text. Send them from your own mail tool or sequencer. LeadOcean supplies the data, not the sending.
Prerequisites
- A LeadOcean account. The Free plan has 1,000 records for life, no card, 5 requests a second.
- An API key in your shell:
export LEADOCEAN_API_KEY=... curlandjq.- A list of stalled deals with the account domain and the last contact's name. A CRM export works.
- Somewhere to send mail. LeadOcean has no sequencer or CRM.
Step 1: Size the second-contact pool
Decide who else signs off on your deal, then count them before you spend anything. Counting is free.
Example: US finance VPs at companies with 201-500 employees, for a product sold to finance.
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": ["Finance & Accounting"],
"country": ["US"],
"employeeRange": ["201-500"],
"limit": 1
}' | jq '.meta'count is a query parameter, not a body field. meta.total is capped at 100,000. Enum values are exact strings, so check them with GET /v1/enums/{name} or the leadocean_list_enum_values MCP tool if a filter returns nothing.
Step 2: Find the second stakeholder at the stalled account
Search by the account's domain and the buyer level you need. Use a placeholder domain here: acme.com.
curl -s -X POST "https://api.leadocean.io/v1/people/search" \
-H "x-api-key: $LEADOCEAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"domain": ["acme.com"],
"jobLevel": ["VP", "Director"],
"jobFunction": ["Finance & Accounting", "Operations"],
"limit": 10
}' > acme_people.jsonThe body is flat JSON. Filters are arrays, limit runs 1 to 100, and you page with cursor. Each person returned counts as one record.
To list everyone at a domain instead, use GET /v1/companies/acme.com/people. It stops at 10,000 rows.
Pick the second contact by what stalled. If the deal died on budget, look for finance. If it died on rollout, look for operations or IT. If your contact lost interest, look one level up in the same function. A search by domain with two or three job functions returns all of them in one page, so you can choose after you read the titles.
Keep the page small. Ten rows at one account is ten records, and you rarely need more than three names.
Step 3: Enrich one person and read their history
Pick one result and enrich it by person_id. Ask for the email with reveal_email.
curl -s -X POST "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_STEP_2", "reveal_email": true}' \
| jq '{current: .data.contact_data.contact_current_experiences, past: .data.resume_data.experiences}'Enrich takes exactly one of linkedin_url, email, phone or person_id. Since October 2026 it returns career history: past roles, education, certifications and awards. Past employers have no company_id yet.
If nobody matches you get a 404, which costs nothing. Read email_status before sending. Bounces are not refunded.
Step 4: Pick the template and fill the fields
Match the reason the deal stalled to a template. Each one needs one or two fields from Step 3.
1. The new stakeholder. Field: second contact's title.
Subject: Adding {second_title} to the {topic} thread Hi {first_name}, I have been talking with {old_contact} about {problem}. It touches finance, so you should see it too. Two lines on what we found: {finding}. Worth 15 minutes?
2. The past employer. Field: resume_data.experiences.
Subject: From {past_company} to {company} Hi {first_name}, I saw you spent time at {past_company}. Teams there often hit {problem} around {stage}. Is it on your list this quarter?
3. The new role. Field: a changed contact_current_experiences title.
Subject: Congrats on the move to {new_title} Hi {first_name}, new roles reset priorities. Last time we spoke with {old_contact}, {problem} was open. Has that changed under you?
4. The one-line check. Field: none.
Subject: Still a priority? Hi {first_name}, should I close this out, or is {problem} still live?
5. The proof drop. Field: industry.
Subject: How {industry} teams handle {problem} Hi {first_name}, one concrete example from {industry}: {your_own_result}. Happy to share the detail.
6. The calendar reset. Field: none.
Subject: Moving this to {month}? Hi {first_name}, timing looks tight. Want me to come back in {month}, or loop in someone else?
7. The champion hand-off. Field: second contact's name.
Subject: Who else should see this? Hi {old_contact}, I do not want to push. Would it help if I sent a short summary to {second_name}, or is there someone better?
8. The headcount signal. Field: employeeRange.
Subject: At {headcount} people, this gets harder Hi {first_name}, companies your size usually feel {problem} when {trigger}. Is that happening now?
9. The credential nudge. Field: certifications or education.
Subject: A question about {credential} Hi {first_name}, I saw your {credential}. Our approach to {problem} follows the same logic. Can I show you?
10. The break-up. Field: none.
Subject: Closing the loop Hi {first_name}, I will stop here. If {problem} comes back, reply with one word and I will pick it up.
Use one template per touch, and change the angle each time. A good order for most stalls is 4, then 1 or 7, then 5, then 6, then 10. Templates 2, 3, 8 and 9 slot in whenever enrich returns the field they need.
Fill every field from the data, or drop the template. A template with a guessed past employer or a wrong title does more harm than no email. Merge fields are in {braces}; rename them to whatever your sending tool uses.
Step 5: Merge in bulk
Loop over your stalled accounts. Keep to the plan's rate limit: 5 requests a second on Free, 200 on Agency. A 429 carries a Retry-After header.
while read -r domain; do
curl -s -X POST "https://api.leadocean.io/v1/people/search" \
-H "x-api-key: $LEADOCEAN_API_KEY" \
-H "Content-Type: application/json" \
-d "{\"domain\":[\"$domain\"],\"jobLevel\":[\"VP\",\"Director\"],\"limit\":3}" \
> "out_$domain.json"
sleep 0.25
done < stalled_domains.txtFor larger lists, POST /v1/exports takes up to 50,000 rows a request and 500,000 a day. Bulk CSV export is not on Free. The app at app.leadocean.io also has an Exports page with a filter builder and a price preview.
What you get
The Step 1 count, run with the leadocean_count_leads MCP tool on 2026-10-21:
{
"total": 4679,
"totalIsExact": true,
"totalAll": 13520,
"distinctCompanies": 3120,
"credits": 0
}total counts only mailable addresses (verified, catch_all_valid or catch_all). totalAll has no email condition. The same filters cost 0 records to count.
A search page, response shape from the API reference, with placeholder values. Search rows carry no contact points; enrich by person_id for the address.
{
"success": true,
"data": [
{
"profile_data": {
"profile_full_name": "Jane Doe",
"profile_headline": "VP Finance at Acme",
"profile_url": "https://www.linkedin.com/in/jane-doe"
},
"contact_data": {
"has_email": true,
"has_phone": false,
"email_status": "verified",
"email_type": "work"
},
"meta": { "source_ids": { "person_id": "1234567890" } }
}
],
"meta": { "count": 1, "limit": 10, "credits": 1, "nextCursor": "CURSOR" }
}Troubleshooting
| Error | Cause | Fix |
|---|---|---|
| 401 | Missing or wrong x-api-key | Check $LEADOCEAN_API_KEY is exported and sent as x-api-key |
| 429 | Above the plan's rate limit (5 a second on Free, 200 on Agency) | Wait for Retry-After, add a sleep, or move up a plan |
| 402 | Out of records (1,000 on Free) | Upgrade, or narrow the search before you page |
Empty data | Bad enum value, such as vp for jobLevel | Use exact strings like VP; check GET /v1/enums/job_level |
| 404 on enrich | No match for that input | Free. Try linkedin_url or email instead |
| Export refused | Over 50,000 rows a request or 500,000 a day | Split the export into smaller requests |
FAQ
How many follow-ups should I send on a stalled deal?
Send four to six touches over a few weeks, then the break-up. Vary the angle each time. Do not resend the same email.
Should I email the old contact or the new one first?
Email the old contact first with template 7, then the new one. It keeps the champion informed and avoids surprise.
Does LeadOcean send the emails?
No. LeadOcean returns data by API, MCP server and CSV export. Send from your own mail tool or sequencer.
How much does this cost?
The example spends about 11 records. Free includes 1,000 records for life. Paid plans start at $97 a month for Solo. See pricing.
Can an AI assistant fill the templates?
Yes. The hosted MCP server has 13 tools, including free counting, so an assistant can size the pool before it spends records.
Related: personalized sales email templates, 7 personalized email strategies for sales and marketing alignment, 11 personalized sales email letters with examples and all guides on the blog.
Find the second contact on your stalled deal
Free to start. No credit card. 1,000 records to spend whenever you like.
Get your free API key →