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

How-to guide

How to Choose Cold Email Marketing Tools (and Build the Data Layer With an API)

Four layers, one decision per layer. Then a working recipe for the one most guides skip: a send-safe list, with real queries and output.

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

Cold email marketing tools fall into four layers: data, verification, sending and infrastructure. Pick one tool per layer and keep the layers separate, so you can swap one without rebuilding the rest.

Most guides list sender apps and stop. The layer that decides your reply rate is the list, so this page builds that part for real.

TL;DR

  • Four layers: data, verification, sending, infrastructure. Buy each on its own merits.
  • The recipe takes about 20 minutes and spends 100 LeadOcean records. Sizing and polling are free.
  • Gate every list on the email_1_status column before it touches a sender. Bounces are not refunded.

Prerequisites

  • A free LeadOcean account at app.leadocean.io. The free plan has 1,000 records, one-off, no card.
  • An API key from the app, exported as $LEADOCEAN_API_KEY.
  • curl and Python 3.
  • A sender account to import into. Any sender that takes a CSV works.
bash
export LEADOCEAN_API_KEY="paste-your-key-here"

Step 1: Split the stack into four layers

Write down one tool for each layer before you compare features. This is the whole decision.

LayerJobWhat to check
DataWho to email, with a work address and a statusFilters, status per address, price per record
VerificationDrop addresses that will bounceDoes the data layer already return a status?
SendingSequences, replies, schedulingPer-mailbox limits, reply handling, API
InfrastructureDomains, mailboxes, warm-up, DNSSeparate sending domain, SPF, DKIM, DMARC

How to decide inside each layer:

  • Data: buy on filters and status, not on a headline database size. A list you cannot size for free is a list you cannot test.
  • Verification: if the data layer returns a status per address, a second verifier adds a step, not much safety. Add one only for lists you import from elsewhere.
  • Sending: pick for reply handling and mailbox limits. Sequence builders matter less than whether you can pause one mailbox when its bounce rate climbs.
  • Infrastructure: buy separate sending domains and warm them up before the first campaign. Set SPF, DKIM and DMARC on each.

Two rules. Never send from your primary domain. And pick a sender that imports a CSV or has an API, because you will re-run the list every month.

LeadOcean is the data layer, and it returns an email status on every address, which covers most of the verification layer. It is not a sender: no sequences, dialer or CRM. For a worked sender import, see the ReachHQ recipe.

Step 2: Write the audience as filters

Describe the audience as one flat JSON object. Filters are arrays of strings, and enum values are exact strings.

This example is VP-level sales people at 51 to 200 person companies in the US, with a verified work email.

json
{
  "jobFunction": ["Sales & Business Development"],
  "jobLevel": ["VP"],
  "country": ["US"],
  "employeeRange": ["51-200"],
  "emailType": ["work"],
  "emailStatus": ["verified"]
}

Save it as audience.json. The same keys work in people search, in the export filters object and in the MCP tools.

Do not guess enum values. VP works, vp does not. Check strings with GET /v1/enums or the free leadocean_list_enum_values MCP tool.

Step 3: Size the list before you spend anything

Send the filters to people search with count=true as a query parameter and limit 1. It returns the total and no rows, so it costs no records.

bash
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 '{
    "jobFunction": ["Sales & Business Development"],
    "jobLevel": ["VP"],
    "country": ["US"],
    "employeeRange": ["51-200"],
    "emailType": ["work"],
    "emailStatus": ["verified"],
    "limit": 1
  }'

Read meta.total, which is capped at 100,000. A people count also carries meta.distinctCompanies, the number of employers the list spans. That number matters for cold email: 4,000 people at 3,000 companies is a better list than 4,000 people at 400.

Size three or four variants. Each call is free.

Step 4: Export a small test batch

POST /v1/exports takes filters, limit, columns and caps. Numbered columns are requested as email_N_address, and caps sets how many come back. Start with 100 rows to test your sender import.

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

One export holds up to 50,000 rows, and the account ceiling is 500,000 rows a day. Bulk CSV export is not on the Free plan, so the 100-row test needs a paid plan. Solo at $97 a month includes bulk export (see pricing). The Exports page in the app does the same job with a filter builder and the record price before you start.

Poll GET /v1/exports/{id} until status is done, then download.

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

Step 5: Gate the file, then import it

Keep only rows you would send to, and drop duplicate addresses. Do this on every export.

python
import csv

SEND_OK = {"verified", "catch_all_valid"}
seen, rows = set(), []

with open("test.csv", newline="") as f:
    for r in csv.DictReader(f):
        addr = (r["email_1_address"] or "").strip().lower()
        if addr and r["email_1_status"] in SEND_OK and addr not in seen:
            seen.add(addr)
            rows.append(r)

with open("send-list.csv", "w", newline="") as f:
    w = csv.DictWriter(f, fieldnames=rows[0].keys())
    w.writeheader()
    w.writerows(rows)

print(len(rows), "rows ready")

Import send-list.csv into your sender. Map email, first name, company and title. Tag the segment name so you can compare replies by audience. For what to measure after, read cold email response rate and the cold email statistics.

Keep the filter JSON in version control. It is the real asset: it defines your audience, and you can re-run it when the monthly refresh lands. A list file is a snapshot. A filter set is repeatable.

Send the 100-row batch first. If bounces stay low, run the full list. A bigger guide to the list itself is in email marketing databases.

Step 6: Re-run the list every month

A cold email list decays. People change jobs, and domains stop accepting mail. The dataset is refreshed monthly, so re-run the same filters before each large send instead of reusing last quarter's file.

Use this routine:

  • Size the filters again. A count that drops sharply means a filter value changed or the segment shrank.
  • Export only the segment you will send this month, and name it with the date.
  • Suppress anyone who replied, unsubscribed or bounced last time. Your sender holds that list, not the data layer.
  • Gate on email_1_status again with the script from Step 5.

Treat deliverability as a loop, not a setup task. Watch the bounce rate on the first 100 sends of every new batch. If it climbs, stop and check the status mix in the file before you blame the copy.

What you get

Sizing is free, so here are two real counts from the LeadOcean MCP server on 2026-10-21. Both are US VP-level sales people at 51 to 200 person companies.

FiltersCountEmployersDate
emailType work, emailStatus verified4,0273,0522026-10-21
Default mailable (verified, catch_all_valid or catch_all), any email type18,56712,1922026-10-21

The same filters with no email condition match 60,456 people. The two rows use different email filters, so do not subtract one from the other.

The count call returns this shape. This is the response shape from the API reference, with placeholder values.

json
{
  "success": true,
  "data": [],
  "meta": {
    "total": 4027,
    "totalIsExact": true,
    "countOnly": true,
    "credits": 0
  }
}

The downloaded CSV has the columns you asked for, with numbered columns expanded to email_1_address and so on. One placeholder row:

csv
profile_full_name,current_job_title,current_company_name,current_company_domain,email_1_address,email_1_status
Jane Doe,VP Sales,Acme,acme.com,jane.doe@acme.com,verified

Troubleshooting

ErrorCauseFix
401Missing or wrong x-api-key headerCopy the key from the app again. Check $LEADOCEAN_API_KEY is set in this shell.
429Above the plan's rate limit (5 a second on Free, 200 on Agency)Slow the loop. Poll an export at the pollAfter interval.
402Out of records, or fewer left than the export needsLower limit. Records are reserved for the whole export before it starts.
Empty resultsA filter value that is not an exact enum stringPull strings from GET /v1/enums or leadocean_list_enum_values. VP, not vp.
400 export_limit_rowslimit is above 50,000 rows for one exportSplit the audience by country or level and run several exports.
429 export_limit_dailyYou passed the 500,000 rows a day ceilingWait for resetsAt in the response, then run the next export.

FAQ

What are cold email marketing tools?

They are the software behind outbound email: a data source for contacts, a verifier, a sender with sequences and reply handling, and sending infrastructure such as domains and mailboxes. Some products bundle two layers. Buy the weakest layer first.

Do I need a separate email verifier?

Not if your data source returns a status per address. Read email_1_status and send only verified and catch_all_valid. A separate verifier still helps if you import lists from elsewhere.

What happens when an address bounces?

Nothing is refunded. There is no credit-back for bounced emails, so gate on status before you send. LeadOcean is the data, not the sender.

How fresh is the data?

The dataset is refreshed monthly, and each record carries its own fetched_at date. Re-export a segment before a large send.

How much does the data layer cost?

Sizing is free on every plan, and the free plan gives you 1,000 records once, no card. The export step needs a paid plan, because bulk CSV export is not on Free. Solo is $97 a month and Agency is $497 a month, with a fair-use pace instead of credits. See pricing. More guides are in the blog hub.

Build the data layer of your cold email stack. Size it free, export when it looks right.

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

Get your free API key →