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

How-to guide

How to Enrich and Route Inbound Leads (With One API Call per Signup)

A form gives you an email. This gives you the title, level and company behind it, in under a minute of setup.

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

Inbound lead generation is getting people to come to you. Most of the work after that is deciding who deserves a human reply, and a form with three fields cannot tell you.

This guide adds the missing fields. A signup posts to your server, you enrich the email, score the result and route it. Then you use the same filters to size more people like your best signups, for free.

TL;DR

  • Enrich each form fill by email (POST /v1/people/enrich), then the company by domain, then route on job level and headcount.
  • Setup takes about 45 minutes. It spends 2 records per signup and about 10 while you test. Free includes 1,000 records for life.
  • A lookalike count with leadocean_count_leads spends nothing. Use it to see if your ICP is big enough before you buy ads for it.

Prerequisites

  • A LeadOcean key from app.leadocean.io, stored as $LEADOCEAN_API_KEY. Free works: both scopes, 1,000 records, 5 requests a second.
  • A form that can call a webhook (any form tool, or your own endpoint).
  • Python 3.10+ and requests, or curl.
  • A place to send the result: a Slack webhook, your CRM API or a queue. This guide prints the decision and stops there.

Step 1: Enrich the email from the form

One call turns an email into a person. Send exactly one identifier. Here it is the email.

bash
curl -s -X POST https://api.leadocean.io/v1/people/enrich \
  -H "x-api-key: $LEADOCEAN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"email": "jane.doe@acme.com"}'

A 404 means we hold nothing for that person. It costs nothing, so treat it as "unknown" and send the lead to your default queue.

Leave reveal_email off here. You already have the address, so you only need the profile.

Step 2: Enrich the company

The email domain is the company key. Enrich it to get headcount and industry.

bash
curl -s -X POST https://api.leadocean.io/v1/companies/enrich \
  -H "x-api-key: $LEADOCEAN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"domain": "acme.com"}'

Skip this call for free-mail domains (gmail.com, outlook.com). There is no company behind them, and you save a record.

Step 3: Score and route in one function

Put both calls behind your webhook. This handler enriches, scores on level and headcount, and picks a queue.

python
import os, requests

BASE = "https://api.leadocean.io"
H = {"x-api-key": os.environ["LEADOCEAN_API_KEY"]}
FREE_MAIL = {"gmail.com", "outlook.com", "yahoo.com", "icloud.com"}

def enrich(path, body):
    r = requests.post(f"{BASE}{path}", headers=H, json=body, timeout=15)
    if r.status_code == 404:
        return None            # nobody matched, no record spent
    r.raise_for_status()       # 401 bad key, 402 out of records, 429 too fast
    return r.json()["data"]

def route(email):
    domain = email.split("@")[1].lower()
    person = enrich("/v1/people/enrich", {"email": email})
    company = None if domain in FREE_MAIL else enrich(
        "/v1/companies/enrich", {"domain": domain})
    if not person:
        return "default-queue"
    role = (person.get("contact_data", {})
                  .get("contact_current_experiences") or [{}])[0]
    level = (role.get("job_seniority") or {}).get("job_level")
    senior = level in ("C-Team", "VP", "Director")
    emp = ((company or {}).get("company_data") or {}).get("company_employees") or {}
    size = emp.get("number_of_employees") or 0
    if senior and size >= 50:
        return "sales-now"
    if senior or size >= 200:
        return "sales-24h"
    return "nurture"

print(route("jane.doe@acme.com"))

The lookups follow the documented schemas: the role's job_seniority.job_level (MCP docs) and the company's company_data.company_employees.number_of_employees (company lookup). Print one real response before you ship and confirm the level strings match your rules.

Keep the rules this crude at first. After a month, compare the queue each lead landed in against what closed, then adjust the thresholds.

Step 4: Size the lookalikes before you spend on traffic

Your best inbound signups share a level, a function and a company size. Count how many people match that profile. The count is free.

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 '{
    "jobLevel": ["VP", "C-Team"],
    "jobFunction": ["Sales & Business Development"],
    "employeeRange": ["51-200", "201-500"],
    "country": ["US"],
    "limit": 1
  }'

The same query works as an MCP call from Claude or ChatGPT: ask for a count with those filters and the model calls leadocean_count_leads. For companies, GET /v1/companies/count and leadocean_count_companies do the same job. Enum values are exact strings, so look them up with leadocean_list_enum_values instead of guessing.

Step 5: Pull lookalikes to retarget or contact

If the count is large enough, search the same filters and page with cursor. Each person returned is one record, so set limit to what you need.

bash
curl -s -X POST 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"],
       "employeeRange": ["51-200"], "country": ["US"], "limit": 25}'

For a list above a few thousand rows, use POST /v1/exports instead (50,000 rows a request, 500,000 a day; not on Free). Upload the result as a custom audience to the ad platform you already run. Send mail only to rows whose email_status is verified.

Step 6: Log the decision so you can tune it

Write the queue, the level and the headcount next to the lead in your CRM. Without that, you cannot tell later whether the rules helped.

json
{
  "email": "jane.doe@acme.com",
  "queue": "sales-now",
  "job_level": "VP",
  "employee_count": 120,
  "enriched_at": "2026-10-21"
}

Three mistakes to avoid:

  • Enriching bots. Filter obvious spam and disposable addresses before the call. Every call that finds a person costs a record.
  • Routing on title text. Titles vary ("Head of Growth", "Growth Lead"). Route on job level and function, and keep title as a tiebreaker.
  • Skipping the status. Check email_status before you reply by email. Bounces are not refunded.

After 30 days, group closed deals by queue. If sales-24h closes as often as sales-now, lower the headcount bar. If nurture produces deals, your rules are too strict.

Records also carry their own fetched_at date, and the dataset refreshes monthly. Re-enrich a lead only when it comes back after a long gap, such as a new form fill six months later. A person who changed jobs will show it.

What you get

A free count run on 2026-10-21 for VP and C-Team people in Sales & Business Development, at companies of 51-200 and 201-500 employees, in the US:

json
{
  "total": 61084,
  "totalIsExact": true,
  "totalAll": 203746,
  "distinctCompanies": 28019,
  "credits": 0
}

total counts only addresses worth sending to (verified, catch_all_valid or catch_all). totalAll is the same filters with no email condition. That is a mailable count, not a headcount of the market.

The enrich call returns this shape (response shape from the API reference, placeholder values):

json
{
  "success": true,
  "data": {
    "profile_data": { "profile_full_name": "Jane Doe" },
    "contact_data": {
      "has_email": true,
      "has_phone": false,
      "email_status": "verified",
      "email_type": "work",
      "contact_current_experiences": [
        {
          "job_title": "VP Sales",
          "job_seniority": { "job_level": "VP" },
          "company_name": "Acme",
          "company_domain": "acme.com",
          "company_employees": { "number_of_employees": 120 }
        }
      ]
    },
    "resume_data": {}
  },
  "meta": { "credits": 1 }
}

Since 17 October 2026, resume_data also carries past roles, education and certifications when we hold them. Past employers have no company_id yet.

Troubleshooting

ErrorCauseFix
401Missing or wrong x-api-key headerSend the key as x-api-key, read from $LEADOCEAN_API_KEY
402The 1,000 Free records are spentAdd a paid plan, or enrich only leads that pass a cheap filter first
429Above the plan rate limit (5 a second on Free, 200 on Agency)Honour Retry-After; queue webhook calls instead of firing in parallel
404 on enrichWe hold nothing for that emailNot an error. No record is spent; route to the default queue
Empty search or total 0An enum value that is not an exact string, such as "VP of Sales"Look it up with leadocean_list_enum_values; levels are C-Team, VP, Director, Manager, Staff, Other
Export refusedOver 50,000 rows a request or 500,000 a day, or you are on FreeSplit the request, or move to a paid plan

FAQ

What is inbound lead generation?

It is attracting people who contact you first: signups, demo requests, downloads. The channel is content, search, ads or referrals. Enrichment is what turns those names into a ranked queue.

Does enriching inbound leads break form conversion?

No. The call runs on your server after the form is submitted. The visitor sees nothing, and you keep the form short.

How many records will this use?

Two per signup with a work email: one person, one company. Free-mail signups use one. A 404 and a count cost nothing.

Can I do this without code?

Yes, in Clay or n8n. See our Clay and n8n pages for the setup. The calls are the same ones shown above.

Where does outbound fit?

Inbound brings the people who find you. Outbound reaches the ones who do not. Our B2B lead generation guide covers building that list, and Sales Navigator is one place to start. For scoring on intent signals, read predictive lead generation. Plans are on the pricing page.

Enrich every inbound lead before a human opens it

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

Get your free API key →