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

How-to guide

How to Combine Enrichment Data With Buyer Intent Timing (LeadOcean API)

Fit tells you who could buy. Timing tells you who might buy now. Layer both with four free counts, one search and a handful of enrichments.

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

A true intent feed tells you which accounts are researching a category right now. You can get most of the way there without one. Pair fit data with public timing signals, then enrich only the people who pass.

This guide builds that shortlist in five steps. Each one is a real request against https://api.leadocean.io.

TL;DR

  • Fit plus timing beats fit alone. The timing signals here are funding year, detected technology and a recent role start. None is a live research signal, so treat them as proxies.
  • It takes about 30 minutes. Sizing is free. The run spends about 110 records: 100 for one search page and 10 for enrichments.
  • LeadOcean has no intent feed or alerts. You re-run the query on your own schedule and diff the results.

Prerequisites

  • A LeadOcean account on the Free plan (1,000 records for life, no card): app.leadocean.io/register.
  • An API key from the app, exported as $LEADOCEAN_API_KEY.
  • curl and jq. Free allows 5 requests a second, so keep loops sequential.
  • Optional: the hosted MCP server at https://api.leadocean.io/mcp if you would rather run Step 1 from Claude.

Step 1: Size the fit audience for free

Count before you spend. A count costs no records, so run as many as you like.

Start with fit only: VPs of sales at US companies with 51-200 employees. Enum values are exact strings, so check them with GET /v1/enums if a filter returns nothing.

bash
curl -s -G "https://api.leadocean.io/v1/people/search" \
  -H "x-api-key: $LEADOCEAN_API_KEY" \
  -d count=true -d limit=1 \
  --data-urlencode 'jobLevel=VP' \
  --data-urlencode 'jobFunction=Sales & Business Development' \
  --data-urlencode 'hqCountry=US' \
  --data-urlencode 'employeeRange=51-200' | jq .meta

count=true goes in the query string with limit=1. meta.total is capped at 100,000 and meta.countOnly comes back true. The MCP tool leadocean_count_leads does the same job and returns the mailable total by default.

Step 2: Add a technology signal

A detected tool is a cheap proxy for a live problem. A company running a CRM is a fit for CRM-adjacent products. You see what is detected on the site, not what was removed.

Add technologies. It is detected on company websites and covers about 7.8M crawled sites.

bash
curl -s -G "https://api.leadocean.io/v1/people/search" \
  -H "x-api-key: $LEADOCEAN_API_KEY" \
  -d count=true -d limit=1 \
  --data-urlencode 'jobLevel=VP' \
  --data-urlencode 'jobFunction=Sales & Business Development' \
  --data-urlencode 'hqCountry=US' \
  --data-urlencode 'employeeRange=51-200' \
  --data-urlencode 'technologies=HubSpot' | jq .meta

If a company-side filter cannot be applied, the response names it in unsupportedFilters and withholds the total. Narrow with industry or a country and count again. Do not quote a total that came back withheld.

Step 3: Add a funding-recency signal

Fresh funding is the oldest timing signal in outbound. New money means new budget and new hires.

The funding filters are fundingType and minLastFundingYear. On a people search they apply only when the matching companies number 50,000 or fewer after the size, industry and country filters. Otherwise they land in unsupportedFilters. So keep the fit filters tight first.

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"],
    "hqCountry": ["US"],
    "employeeRange": ["51-200"],
    "technologies": ["HubSpot"],
    "fundingType": ["Series A"],
    "minLastFundingYear": 2025,
    "emailStatus": ["verified", "catch_all_valid"],
    "limit": 100
  }' > page1.json
jq '.meta | {count, nextCursor, notice}' page1.json

The body is flat JSON. List filters are arrays of strings, and year filters are integers. limit runs 1 to 100, and you page with meta.nextCursor. This call spends one record per person returned, so up to 100.

Funding data is matched through the employer. A company with no funding figure on file is never returned. A missing match does not mean the company has no funding.

Step 4: Check for a recent role start

A new VP of sales hires tools in the first 90 days. Search does not filter on role start date, so check it on enrichment.

Pull meta.source_ids.person_id from the search page, then enrich a sample of ten. Enrichment takes exactly one of linkedin_url, email, phone or person_id.

bash
for id in $(jq -r '.data[:10][].meta.source_ids.person_id' page1.json); do
  curl -s -X POST "https://api.leadocean.io/v1/people/enrich" \
    -H "x-api-key: $LEADOCEAN_API_KEY" \
    -H "Content-Type: application/json" \
    -d "{\"person_id\": \"$id\"}" \
  | jq '{role: .data.contact_data.contact_current_experiences[0], past: .data.resume_data.experiences[0:2]}'
  sleep 0.3
done

The current role is in contact_data.contact_current_experiences[]. Past roles are in resume_data.experiences[], with title, company name, start and end dates and job level. Past employers carry no company_id yet, so match them by name.

A person we hold nothing for returns a 404 and costs nothing. Add reveal_email only for people you will contact.

Step 5: Score and export

Every row on the page passed fit, technology and funding. Add the role-start check from Step 4 and score each person in a spreadsheet or a script. A plain count of signals works fine.

bash
jq -r '.data[] | [
  .profile_data.profile_full_name,
  .meta.source_ids.person_id
] | @csv' page1.json > shortlist.csv

For the full list, use POST /v1/exports or the Exports page in the app. It has a filter builder, a column picker and a price preview before you start. Exports take up to 50,000 rows a request and 500,000 a day. Bulk CSV export is not on Free.

Re-run the same body weekly and diff person_id values. New ids are people who just entered your window.

What you get

Free count run on 2026-10-21 through leadocean_count_leads, using the Step 1 and Step 2 filters. Totals are mailable people (verified, catch_all_valid or catch_all).

text
Fit only
  jobLevel VP, jobFunction Sales & Business Development,
  hqCountry US, employeeRange 51-200
  total: 23,999 (exact)   no email condition: 86,697
  distinct employers: 13,729

Fit + technologies HubSpot
  total: 5,855 (exact)    no email condition: 18,203
  distinct employers: 3,284

Adding one timing-style filter cut the audience from 23,999 to 5,855. Funding is not counted here, because free counts with funding filters are not reliable. The funding filter narrows the list further at search time.

Response shape from the API reference (placeholder values, one search row):

json
{
  "data": [
    {
      "profile_data": { "profile_full_name": "Jane Doe" },
      "contact_data": {
        "has_email": true,
        "has_phone": false,
        "email_status": "verified"
      },
      "meta": { "source_ids": { "person_id": "1234567890" } }
    }
  ],
  "meta": { "count": 1, "nextCursor": "<opaque>", "source": "leadocean" }
}

Troubleshooting

ErrorCauseFix
401Missing or wrong x-api-key headerRe-copy the key from the app and check $LEADOCEAN_API_KEY is exported
429Requests above the plan's rate limit (5 a second on Free, 200 on Agency)Honor Retry-After, add sleep between calls
402The 1,000 free records are spentCount only, or move to Solo ($97 a month) or Agency ($497 a month)
Empty resultsA bad enum value, such as "United States" instead of USCheck exact values with GET /v1/enums/{name}
Total missing, unsupportedFilters setA company-side filter matched too many employers to applyNarrow with industry or hqCountry, then count again
Export refusedOver 50,000 rows a request or 500,000 a daySplit the filters into slices under 50,000 rows

FAQ

Is this real intent data?

No. Intent data usually means observed research behavior, such as content views or search activity. LeadOcean holds none. It holds firmographics, technology, funding and career history, which work as proxies for timing.

How fresh are the timing signals?

The dataset is refreshed monthly, and each record carries its own fetched_at date. A role change from last week may not be there yet.

How many records does the whole run cost?

About 110: one search page of 100 and ten enrichments. Counts, GET /v1/filters, GET /v1/enums and enrichments that find nothing are free.

Can I run this from Claude instead of curl?

Yes. The hosted MCP server has 13 tools, including leadocean_count_leads and leadocean_count_companies, both free. Sign in with OAuth, then ask for the counts in plain language. For related background, see buyer's journey intent data.

What should I do with the shortlist?

Clean it first. Our guides on fixing a messy CRM and enrichment tools for RevOps cover that step. Plans and pace limits are on the pricing page. More guides are on the blog.

Build your timing shortlist with real data. Start free.

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

Get your free API key →