Integrations

LeadOcean + ReachHQ: Import Verified Leads Into a Cold Email Campaign

Export a filtered list from LeadOcean, keep only verified and catch_all_valid emails, and load it into a ReachHQ campaign in three API calls.

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

You get a short pipeline: a LeadOcean export of people with verified work emails, converted to rows, imported into a ReachHQ lead list and attached to a campaign. Both sides are plain REST with an API key, so you can script it in an afternoon. The free LeadOcean plan has 1,000 records to spend on a first run.

Related: the LeadOcean integrations hub, the VerifyHQ and ReachHQ stack guide, and the pricing page.

How it works

Five steps, two APIs. ReachHQ's API lets you import rows with a dry run first and then commit them to a static lead list (source: ReachHQ API spec, September 2026).

  1. Get both keys. Sign up at app.leadocean.io and copy a key from the API keys page. In ReachHQ, create a key under Settings, API keys, with the scopes leads:write, lead_lists:write and campaigns:write. ReachHQ shows the secret once.
  2. Export from LeadOcean. POST your filters to /v1/exports with emailStatus set to verified and catch_all_valid. The API answers 202 with a job id.
  3. Download the CSV. Poll GET /v1/exports/{id} until status is done, then download the file.
  4. Convert and dry-run. Turn CSV rows into the JSON ReachHQ expects and send them with action: dryRun. Read the preview, then send the same rows with action: commit and a listId.
  5. Attach the list to a campaign. Call the campaign recipients endpoint with action: addFromList. A large list returns RUNNING, so poll with audienceJobStatus.

If you would rather click, ReachHQ's public pages do not document an in-app CSV import or its column mapping screen. Check the app at app.reachhq.io for the current flow.

Do not launch from this recipe. Launching is a separate campaigns:launch scope in ReachHQ, so keep it off the key you use for imports.

Configuration

Step 2, the export. Several values inside one filter join with OR, so this keeps both good statuses.

bash
curl -X POST https://api.leadocean.io/v1/exports \
  -H "x-api-key: $LEADOCEAN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"reachhq VP sales CA","filters":{"jobLevel":["VP"],"jobFunction":["Sales & Business Development"],"country":["CA"],"emailStatus":["verified","catch_all_valid"],"emailType":["work"]},"limit":1000,"columns":["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

Size the audience first. A search with count=true and limit=1 is free. Contact columns need a key with the enrich scope. Keys on the free plan get both scopes, search and enrich, inside the 1,000 one-off records.

Step 4, convert the CSV to rows. This script is ours, and the 500-row chunk is a conservative choice, not a ReachHQ limit.

python
import csv, json, os, requests

API = "https://api.reachhq.io/api/v1/leads/import"
HEAD = {"Authorization": f"Bearer {os.environ['REACHHQ_API_KEY']}"}
LIST_ID = os.environ["REACHHQ_LIST_ID"]

rows = []
for r in csv.DictReader(open("people.csv")):
    if r["email_1_status"] not in ("verified", "catch_all_valid"):
        continue
    rows.append({
        "email": r["email_1_address"],
        "firstName": r["profile_first_name"],
        "lastName": r["profile_last_name"],
        "company": r["current_company_name"],
        "jobTitle": r["current_job_title"],
        "domain": r["current_company_domain"],
    })

for i in range(0, len(rows), 500):
    body = {"action": "commit", "source": "CSV", "fileName": "people.csv",
            "rows": rows[i:i+500], "listId": LIST_ID}
    print(requests.post(API, headers=HEAD, json=body).json())

Run it once with "action": "dryRun" and no listId before you commit. ReachHQ's API reference types rows as an array of objects and fieldMapping as a free-form object, and it states no row limit per request. The script above sends rows already named for ReachHQ, so it leaves fieldMapping out (ReachHQ API reference, October 2026).

Step 5 attaches the list to a campaign.

bash
curl -X POST https://api.reachhq.io/api/v1/campaigns/CAMPAIGN_ID/recipients \
  -H "Authorization: Bearer $REACHHQ_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"action":"addFromList","leadListId":"LIST_ID"}'

ReachHQ allows 600 requests per 60 seconds per key, so 500-row chunks stay far under it (source: ReachHQ REST API guide, September 2026).

Field mapping

ReachHQ's create-lead schema requires email and accepts firstName, lastName, company, tags and customFields. The other columns go in as extra fields. Those names come from the create-lead schema. The reference does not list the row fields that POST /leads/import reads, so confirm them with a dryRun (ReachHQ API reference, October 2026).

LeadOcean fieldTarget field (ReachHQ)Note
email_1_addressemailRequired. Best address first.
email_1_statusfilter onlyDrop anything but verified and catch_all_valid before import.
profile_first_namefirstNameEmpty if the profile has no split name.
profile_last_namelastName
current_company_namecompany
current_job_titlejobTitleExtra field. Use it as a merge field in the sequence.
current_company_domaindomainExtra field. Good dedupe key across lists.
profile_urllinkedinExtra field. Not needed for sending.
person_idexternalIdExtra field. Add it to columns to re-match later.

Column ids come from GET /v1/exports/columns, which needs no key. Email status has 13 values, and catch_all is left out here on purpose.

Cost at 10,000 rows

Ten thousand rows need LeadOcean Pro at $499 a month flat, plus a ReachHQ plan sized by sending volume, not by lead count. ReachHQ says it has no per-lead credit system, and its plans are limited by emails per month (source: ReachHQ pricing, September 2026).

ItemFree LeadOceanPro LeadOcean
LeadOcean1,000 records, one-off, no card$499 a month, flat
Rows you can exportUp to 1,000Up to 50,000 per export
ReachHQ Solo$99 a month, 75,000 emails, 2 seats, 1 workspace, no API accessSame
ReachHQ Scale$299 a month, 750,000 emails, 10 seats, 3 workspaces, API accessSame
ReachHQ free trial14 days, 3,000 emails, no cardSame

A 3-step sequence to 10,000 people is 30,000 emails, which fits inside Solo's 75,000. That sum is ours, and it ignores follow-ups you add. But ReachHQ's pricing page marks API access "No" for Solo and the free trial and "Yes" for Scale (ReachHQ pricing, October 2026). This API recipe needs Scale at $299 a month.

On LeadOcean, an export reserves one record per row up to limit, and Pro covers it flat. There is no per-record price, no credits and no overage invoice. The free plan runs a 1,000-row test of the whole recipe.

Troubleshooting

SymptomCauseFix
402 on export createFewer records left than limit reserves.Lower limit, or move to Pro.
403 on export createContact columns asked without the enrich scope.Use a key with that scope, or drop the email columns.
409 on downloadThe export is still running.Poll until data.status is done.
410 on downloadThe file expired after 7 days.Create the export again.
401 from ReachHQMissing or wrong bearer token.Send Authorization: Bearer rhq_live_..., not x-api-key.
403 from ReachHQThe key lacks a scope.Add leads:write, lead_lists:write or campaigns:write in Settings, API keys.
400 on commit with listIdThe list is dynamic, or the row shape is wrong.Use a STATIC list. Run dryRun and read the preview.
429 RATE_LIMITEDMore than 600 requests in 60 seconds.Wait a minute and resend the chunk.
Campaign audience shows RUNNINGA large list builds asynchronously.Poll audienceJobStatus with the jobId.

FAQ

Do I need LeadOcean if ReachHQ has its own lead finder?

Only if you want LeadOcean's filters and its email status values on the list. ReachHQ has its own finder and says enrichment is unlimited on paid plans. Use this recipe when your list comes from LeadOcean filters or an earlier export.

Which emails should I import?

Import verified and catch_all_valid. Treat every other status as unsent until you review it.

Can I import without code?

ReachHQ's public pages do not document an in-app CSV import, so check the app at app.reachhq.io. Download the LeadOcean CSV from the Exports page at app.leadocean.io, which has a filter builder, a column picker, the record price before you start and a Download button.

Can an AI agent run this?

Yes. LeadOcean's MCP server is at https://api.leadocean.io/mcp and has an export_leads tool that costs one record per row. ReachHQ lists its own MCP server at https://api.reachhq.io/mcp.

Export your first ReachHQ list with a free LeadOcean key

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

Get your free API key →