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

How-to guide

How to Enrich B2B Data From Code (API, Terminal and MCP)

Three ways in, one key. Size an audience for free, pull a few rows, enrich one person, loop it from the shell, then hand the same data to an AI client.

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

TL;DR

  • LeadOcean gives you the same data three ways: REST with an x-api-key header, a shell script around curl and jq, and a hosted MCP server for AI clients.
  • It takes about 20 minutes and about 8 of your 1,000 free records. Sizing, filter lookups and enum lookups cost nothing.
  • LeadOcean has no CLI of its own. The terminal route is curl and jq. If you want a packaged command, see how lm-tui works.

Prerequisites

  • A LeadOcean account and API key from app.leadocean.io. The free plan has no card and 1,000 records, one-off. Free keys get both scopes, search and enrich.
  • The key in your shell as $LEADOCEAN_API_KEY.
  • curl and jq. Claude Code only for Step 6.
bash
export LEADOCEAN_API_KEY="paste-your-key-here"

New to the terms? Read what data enrichment is and what a B2B data API is first. This guide is for people who already know and want the calls.

Developer tools in this space tend to ship three interfaces on one account balance. LeadMagic's developer page lists REST, the LeadMagic CLI (the lm command, known as lm-tui) and a hosted MCP server, all on one credit balance (LeadMagic, October 2026). LeadOcean covers two of the three natively and the third with a ten-line script. This page shows all three side by side, with the calls and the records each one spends.

Step 1: Read your account before you spend anything

GET /v1/account is free. It returns your plan, records used and remaining (or your fair-use pace on a paid plan), your rate and your export ceilings. Every call after this one is measured against it.

bash
curl -s https://api.leadocean.io/v1/account \
  -H "x-api-key: $LEADOCEAN_API_KEY" | jq

Run it first in any new environment. A 401 here means the key is wrong, and you have spent nothing finding out.

The rate limit is per account and set by plan: 5 requests a second on Free, 50 on Solo, 200 on Agency. A 429 or a paced answer carries X-RateLimit-Limit, and X-Quota-State appears while a paid plan is paced.

Step 2: Size the audience for free

Write the audience as filters. Filters are arrays of strings, and the body is flat JSON. This example is VP Engineering and Head of Engineering at US software companies with 51 to 200 staff.

Send count=true as a query parameter with limit at 1. You get meta.total and no rows.

bash
curl -s "https://api.leadocean.io/v1/people/search?count=true" \
  -H "x-api-key: $LEADOCEAN_API_KEY" -H "content-type: application/json" \
  -d '{
    "title": ["VP Engineering", "Head of Engineering"],
    "country": ["US"],
    "industry": ["Software Development"],
    "employeeRange": ["51-200"],
    "emailStatus": ["verified", "catch_all_valid", "catch_all"],
    "limit": 1
  }' | jq '.meta | {total, totalIsExact, countOnly}'

Enum values are exact strings. "Software" is not an industry. Resolve it first with the free enum endpoint.

bash
curl -s "https://api.leadocean.io/v1/enums/industry?q=software" \
  -H "x-api-key: $LEADOCEAN_API_KEY" | jq

meta.total is capped at 100,000. totalIsExact tells you when the number is a floor.

Filters join with AND. Several values inside one filter join with OR. Add excludeTitle (for example ["assistant","intern"]) when the title match is too loose. If the count is too big or too small, change one filter and count again. Counting is free, however many times you do it.

Step 3: Pull a three-row sample

Look at real rows before you pull hundreds. A search returns thin records, and each person returned counts one record. Three rows cost three records.

bash
curl -s https://api.leadocean.io/v1/people/search \
  -H "x-api-key: $LEADOCEAN_API_KEY" -H "content-type: application/json" \
  -d '{
    "title": ["VP Engineering", "Head of Engineering"],
    "country": ["US"],
    "industry": ["Software Development"],
    "employeeRange": ["51-200"],
    "emailStatus": ["verified", "catch_all_valid", "catch_all"],
    "limit": 3
  }' | tee sample.json | jq '.meta | {count, nextCursor}'

Each row carries an id at meta.source_ids.person_id. Keep it. Search publishes email flags, never addresses, and about a fifth of findable people have no LinkedIn URL. The id is how you enrich them anyway. Rows come back as profile_data, contact_data, resume_data and meta.

bash
jq -r '.data[].meta.source_ids.person_id' sample.json

To page on, pass meta.nextCursor back as cursor, unchanged. Limit is 1 to 100 a page. One search walks at most 10,000 rows, so narrow the filters rather than paging harder.

Step 4: Enrich one person by person_id

Enrich takes exactly one of linkedin_url, email, phone or person_id. There is no name plus domain lookup. Search by domain first, then enrich by id.

reveal_email defaults to false. Set it to true to get the emails and phones we hold. It costs no extra record. A person we hold nothing for is a 404 and costs nothing.

bash
PID=$(jq -r '.data[0].meta.source_ids.person_id' sample.json)
curl -s https://api.leadocean.io/v1/people/enrich \
  -H "x-api-key: $LEADOCEAN_API_KEY" -H "content-type: application/json" \
  -d "{\"person_id\": \"$PID\", \"reveal_email\": true}" \
  | jq '.data.contact_data | {has_email, email_status, email_type, contact_emails, contact_phones}'

The one record covers the person, the revealed emails and the phones. If you only need a number, GET /v2/people/phone is the narrow endpoint. The v2 contact endpoints cost one record per call, including a call that finds nothing.

For a company, POST /v1/companies/enrich takes the same style of request. To list the people at a domain, use GET /v1/companies/{domain}/people, which stops at 10,000 rows.

Step 5: Loop it from the shell

This is the job a terminal tool does: take a CSV, enrich every row, write a new file. With LeadOcean it is a short script. LeadMagic's developer page describes its own lm command for bulk CSV enrichment from the shell (LeadMagic, October 2026). LeadOcean authenticates with a key.

Put one email per line in emails.txt. Use your own list.

bash
#!/usr/bin/env bash
: "enrich.sh: one record per hit, a 404 costs nothing"
echo "email,email_status,email_type" > out.csv
while read -r EMAIL; do
  RES=$(curl -s -w "\n%{http_code}" https://api.leadocean.io/v1/people/enrich \
    -H "x-api-key: $LEADOCEAN_API_KEY" -H "content-type: application/json" \
    -d "{\"email\": \"$EMAIL\", \"reveal_email\": true}")
  CODE=$(tail -n1 <<<"$RES")
  [ "$CODE" = "404" ] && { echo "$EMAIL,not_found," >> out.csv; continue; }
  sed '$d' <<<"$RES" | jq -r --arg e "$EMAIL" \
    '[$e, .data.contact_data.email_status, .data.contact_data.email_type] | @csv' >> out.csv
done < emails.txt

Run it with bash enrich.sh. Four addresses spend at most four records. The script is sequential. On Free, add sleep 0.2 inside the loop for a long file to stay under 5 requests a second.

For more than a few thousand rows, stop looping. Use POST /v1/exports, which takes the same filters and writes a file. Bulk CSV export is included on Solo and above, not on Free. One export holds up to 50,000 rows a request and 500,000 a day. The app has the same export on its Exports page, with a column picker and the record price before you start.

Step 6: Hand the same data to an AI client

The hosted MCP server is https://api.leadocean.io/mcp. It signs in with OAuth 2.1, so no key gets pasted into a config file. It exposes 13 tools, seven of which never spend records.

bash
claude mcp add --transport http leadocean https://api.leadocean.io/mcp
claude mcp list

Inside a Claude Code session, run /mcp, pick leadocean and approve the sign-in. Claude Code's docs describe this flow for remote servers (Claude Code MCP docs, September 2026). Then ask in plain language and keep the spend off.

text
Use leadocean_list_enum_values to resolve the industry value for "software". Then use
leadocean_count_leads with title ["VP Engineering","Head of Engineering"], country ["US"],
industry ["Software Development"], employeeRange ["51-200"]. Say which email statuses it
counted. Do not call any tool that spends records.

Clients that support dynamic registration can register themselves at https://api.leadocean.io/oauth/register (scope mcp).

What you get

On 1 October 2026 the free MCP count for the Step 2 audience returned this. It ran with no records spent.

json
{
  "total": 213,
  "totalIsExact": true,
  "credits": 0,
  "defaults": "Counted only addresses worth sending to (emailStatus: verified or catch_all_valid or catch_all)",
  "unsupportedFilters": []
}

That is 213 mailable people, counted by what we filtered: email status verified, catch_all_valid or catch_all. The number moves as the data is refreshed monthly.

This is the enrich response shape from the API reference, with placeholder values. Contact points appear only because reveal_email was true.

json
{
  "success": true,
  "data": {
    "profile_data": { "profile_full_name": "Jane Doe" },
    "contact_data": {
      "has_email": true,
      "has_phone": true,
      "email_status": "verified",
      "email_type": "work",
      "contact_emails": [
        { "email": "jane.doe@acme.com", "type": "work", "status": "verified" }
      ],
      "contact_phones": [
        { "phone": "+14155550133", "type": "mobile", "priority": 1 }
      ]
    },
    "resume_data": { "experiences": [ { "...": "past roles: title, company name, start and end dates" } ] },
    "meta": { "schema": "leadocean.person.v1", "fetched_at": "<date>" }
  }
}

Read email_status before you send. There is no refund for bounced emails. Filter on email_status at search time, and re-check it after enrichment, because the search flag describes the best address and the revealed list can hold several.

The record cost of this whole walkthrough is small by design. Steps 1 and 2 and the MCP count spend nothing. Step 3 spends 3, Step 4 spends 1, and the four-row loop in Step 5 spends at most 4.

Troubleshooting

ErrorCauseFix
401Missing, wrong or revoked keyRe-copy the key from app.leadocean.io and export $LEADOCEAN_API_KEY again.
403The key lacks the scope the endpoint needsUse a key with search and enrich. A permissions mistake returns 403, not an empty result.
429Faster than your plan's rate per account (5 a second on Free, 200 on Agency)Wait the Retry-After seconds, then retry.
402Free plan: the 1,000 records are spentCheck GET /v1/account, or move to Solo ($97) or Agency ($497). Free records do not reset.
Empty results or 400A filter value outside the catalogue, such as industry: "Software"Resolve it with GET /v1/enums/industry?q=... and pass the exact string.
400 on an exportAn export limit above the 50,000-row ceilingSplit the audience by country or headcount band and run several exports.

FAQ

Does LeadOcean have a CLI like lm-tui?

No. LeadOcean has the REST API, a hosted MCP server and an app with an Exports page. From a terminal you use curl and jq, as in Step 5. The lm-tui write-up covers the packaged-command route.

What does this cost?

The free plan is 1,000 records, one-off, with no card. Solo is $97 a month and Agency $497 a month, with a fair-use pace and no per-record price. Each person returned counts one record on every plan. See pricing.

Which route should I pick?

Use REST inside an application. Use the shell loop for one-off files under a few thousand rows. Use exports above that. Use MCP when a person drives the work from an AI client.

Do I need a key for MCP?

No. The MCP server signs in with OAuth. The REST API and the shell script use the x-api-key header.

Where do I go next?

The blog hub lists the related guides. Start with what a B2B data API is if you are still choosing a provider.

Enrich your first records from code. Free key, one curl.

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

Get your free API key →