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

How-to guide

How to Build 40 Cold Email Templates That Get Replies (From Real Data Fields)

Eight angles, five triggers, one search call. The merge fields come from the API, so every email has something true in it.

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

You do not need 40 templates. You need 8 angles and 5 data triggers, and the data to fill the gaps. That gives 40 versions that each say one true thing about the reader.

We publish no reply rates here. We have no tested results to show, and a "with results" number we cannot source would be invented. What we can show is the part you control: the query, the fields and the output.

TL;DR

  • Pick 1 of 8 angles and 1 of 5 triggers (table below). Pull the merge fields from one search call.
  • Time: about 25 minutes. Records: 25 for a test page, then one record per person you keep.
  • Counting your list first is free. Sending is up to your sequencer.

Prerequisites

  • A free LeadOcean API key from the app, stored as $LEADOCEAN_API_KEY. The Free plan gives 1,000 records for life, no card, 5 requests a second.
  • curl and jq.
  • A sequencer or mail tool you already use. LeadOcean has no sequencer, dialer or CRM. It returns the data.
  • Optional: the LeadOcean MCP server at https://api.leadocean.io/mcp (13 tools) if you want an assistant to run the counts.

The 40 templates: 8 angles by 5 triggers

Each cell is a template. Write the angle once, swap the trigger line.

AngleOpening shape
1. Problem"Teams like {{company}} usually hit {{pain}} around {{trigger}}."
2. Peer"We help {{similar_company_type}} with {{result}}."
3. Question"Who owns {{topic}} at {{company}}?"
4. Referral"{{mutual_context}} suggested I write."
5. Observation"Noticed {{company}} {{fact}}."
6. Offer"Here is a {{asset}} for {{role}}s. No call needed."
7. Breakup"Closing the loop on {{topic}}."
8. Bump"Short follow-up: {{one_line_proof}}."
TriggerWhere the field comes from
A. New rolestart_date in contact_data.contact_current_experiences[0]
B. Company size bandemployeeRange filter
C. Tech in usetechnologies filter
D. Past employerresume_data.experiences[] from person enrich
E. Locationcountry filter, a person field

8 angles times 5 triggers is 40. The trigger decides the first line. The angle decides the ask.

The 8 angles, written out

Each sample below uses one trigger so you can see the merge. Swap the trigger line to get the other four versions. Acme, Globex and Jane are placeholders.

1. Problem (trigger B, size band)

Jane, teams at Acme's size (51 to 200 people) often have one ops person who owns the list and the CRM. How is that split at Acme?

2. Peer (trigger C, tech in use)

Jane, we work with sales teams that run HubSpot and want verified contacts loaded before the first sequence. Is list quality on your plate this quarter?

3. Question (trigger A, new role)

Jane, you started at Acme in March. Who owns the account list there now: you or ops?

4. Referral (trigger D, past employer)

Jane, you were at Globex before Acme. Former Globex sales leaders tend to rebuild the target list in the first quarter. Worth comparing notes?

Only use a referral line when the context is true. A past employer is context, not a referral, so do not write "X suggested I write" unless X did.

5. Observation (trigger C, tech in use)

Jane, noticed acme.com runs HubSpot. Do your reps enrich contacts before they land in it, or after?

6. Offer (trigger E, location)

Jane, here is a count of VPs of sales at 51 to 200 person companies in the US, split by seniority. No call needed. Want the file?

7. Breakup (any trigger)

Jane, closing the loop on list building at Acme. If it is not a priority, I will stop here.

8. Bump (any trigger)

Jane, short follow-up: the count I mentioned is free to run yourself. One line of curl.

Keep every version under 80 words, one fact per email and one question per ask.

Step 1: Size the list for free

Counting spends no records. Start with the audience, not the copy.

bash
curl -s "https://api.leadocean.io/v1/people/search?count=true&limit=1" \
  -H "x-api-key: $LEADOCEAN_API_KEY" \
  -H "content-type: application/json" \
  -d '{
    "jobLevel": ["VP"],
    "jobFunction": ["Sales & Business Development"],
    "country": ["US"],
    "employeeRange": ["51-200"]
  }' | jq '.meta'

count=true goes in the query string, not the JSON body. meta.total is capped at 100,000. Enum values are exact strings, so check them with GET /v1/enums first.

Step 2: Pull 25 people with the trigger fields

Use the same filters and add a technology for trigger C. technologies is matched through the employer, so a very broad company filter can come back in unsupportedFilters.

bash
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"],
    "employeeRange": ["51-200"],
    "technologies": ["HubSpot"],
    "limit": 25
  }' > page1.json

That costs 25 records. Page with cursor from meta. One search pages to 10,000 rows, so narrow the filters instead of walking the index.

Step 3: Enrich for the career trigger

For trigger D, enrich one person by person_id. It takes exactly one of linkedin_url, email, phone or person_id. An enrichment that finds nothing costs nothing.

bash
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_STEP_2", "reveal_email": true}' \
  | jq '.data.resume_data.experiences'

Each past role carries job_title, company_name, start_date and end_date. Past employers have no company_id yet, so match on the name string.

Step 4: Merge into a template

A minimal merge, angle 5 (Observation) with trigger C (tech in use). Field names follow the response shape below.

bash
jq -r '.data[]
  | (.profile_data.profile_first_name) as $first
  | (.contact_data.contact_current_experiences[0].company_domain) as $domain
  | select($first and $domain)
  | "Hi \($first),\n\nNoticed \($domain) runs HubSpot. Do your reps enrich contacts before they land in it, or after?\n"' page1.json

Read each output before you send. A merge line that is wrong once costs more than the template saves.

Step 5: Check the email status before sending

Read contact_data.email_status on every row. Send to verified and catch_all_valid. Treat catch_all as a smaller test batch. LeadOcean gives no refund for bounces, so filter first.

Step 6: Test one cell at a time

Run one angle and one trigger per batch. Keep the list, the subject line and the send window the same, and change only the cell. That is the only way to learn which of the 40 works for your buyer.

  • Split the list with the same search filters, so each batch has the same audience shape.
  • Send each batch from the same mailboxes on the same days.
  • Record replies per cell in your sequencer or a sheet. LeadOcean does not track sends or replies.
  • Drop a cell after one clean test. Do not rewrite it five times.

If a trigger field is empty for a person, move them to a cell whose trigger they have. Trigger B and E fields are filled for most rows, because they come from the filters you searched on.

What you get

A free count on 2026-10-21, with every filter named:

code
leadocean_count_leads
  jobLevel: VP
  jobFunction: Sales & Business Development
  country: US
  employeeRange: 51-200

total:             18,567   (default: verified, catch_all_valid, catch_all)
totalAll:          60,456   (same filters, no email condition)
distinctCompanies: 12,192
records spent:     0

So 18,567 people are mailable on those filters. They sit at 12,192 companies, which is about 1.5 people per company. Cap your sends per company.

Adding technologies: HubSpot (trigger C) on the same day returned 4,781 mailable people at 3,069 companies, out of 13,902 with no email condition.

Response shape from the API reference, placeholder values:

json
{
  "success": true,
  "data": [
    {
      "profile_data": { "profile_first_name": "Jane", "profile_full_name": "Jane Doe" },
      "contact_data": {
        "has_email": true,
        "email_status": "verified",
        "contact_current_experiences": [
          { "job_title": "VP Sales", "company_name": "Acme", "company_domain": "acme.com", "start_date": "2026-03-01" }
        ]
      },
      "meta": { "source_ids": { "person_id": "123456789" } }
    }
  ],
  "meta": { "count": 1, "nextCursor": "NEXT_CURSOR" }
}

A search row carries email flags, not addresses. Enrich with reveal_email to get the address.

Troubleshooting

ErrorCauseFix
401Missing or wrong x-api-keyCopy the key from the app and export $LEADOCEAN_API_KEY
429Above the plan's rate limit (5 a second on Free, 200 on Agency)Honour Retry-After, or move up a plan
402Records spent (Free is 1,000 for life)See pricing; Solo is $97 a month
Empty resultsEnum value not an exact string, such as vp for VPCheck GET /v1/enums
404 on enrichNothing held for that personCosts nothing. Try another person_id
Export refusedOver 50,000 rows a request or 500,000 a daySplit the export; bulk CSV is not on Free

FAQ

Are these templates tested?

No. We have no tested reply rates to publish, so we give none. Test one angle against one trigger on 200 people, then change one thing.

How many records does the test cost?

25 for the search page in Step 2, plus one per enrichment that finds someone. The Free plan's 1,000 covers about 40 test pages.

Where do I put the merge fields?

In your sequencer. LeadOcean returns data and exports CSV. It does not send email.

Can I personalize without a name?

Yes. Trigger B, C and E need no name, only a company field. See how to personalize sales emails.

Where do I go next?

Fill your templates with data that is true

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

Get your free API key →