TL;DR
- A deal-sourcing feed is one saved query: stage, funding year, headcount, industry and HQ. You size it free, pull the list, then find the founders.
- It takes about 30 minutes. The example spends about 53 of your 1,000 free records. Counts cost 0.
- LeadOcean refreshes its dataset monthly. It is a snapshot you diff every month, not a live feed. Each record carries its own
fetched_atdate.
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 the search and enrich scopes.
- The key in your shell as
$LEADOCEAN_API_KEY, pluscurl,jqand Python 3 withrequests. - Optional: the MCP server at
https://api.leadocean.io/mcpin Claude or Cursor. It signs in with OAuth, so no key is pasted.
export LEADOCEAN_API_KEY="paste-your-key-here"Step 1: Write the thesis as filters
Start with a query, not a story. A thesis that lives in a memo cannot be re-run, and an AI agent cannot read it. The example thesis is seed and Series A software companies in the US, last funded in 2025 or later, with 11 to 200 staff.
Filter values are exact strings. Look them up first. Both calls are free.
curl -s "https://api.leadocean.io/v1/enums/funding_type" -H "x-api-key: $LEADOCEAN_API_KEY"
curl -s "https://api.leadocean.io/v1/enums/industry" -H "x-api-key: $LEADOCEAN_API_KEY"The funding enum has 23 values, including Pre seed, Seed, Series A and Series B. The industry enum has 534 values and no plain "Software". Use Computer Software.
| Part of the thesis | Filter |
|---|---|
| Stage | fundingType: ["Seed", "Series A"] |
| Recency | minLastFundingYear: 2025 |
| Sector | industry: ["Computer Software"] |
| Size | employeeRange: ["11-50", "51-200"] |
| Geography | hqCountry: ["US"] |
Funding filters only match companies whose value we hold. A company with no funding data on file never appears, so the list is a floor.
You can add more signals to the same body. Use minFunding and maxFunding for total raised in USD, and investors for lead investor keywords. Use minFounded for company age, and technologyCategories for what a company runs on its site. The investors and funding-size filters work at search time, not in free counts.
There is no headcount history in the API. You build the growth signal yourself by saving a snapshot each month, which is what Step 6 does.
Step 2: Size the thesis for free
Count before you pull. Counting is free, but two rules matter. Do not put funding filters in the count, because a count with them can come back withheld or wrong. And a people count measures reachable founders, not companies.
Start with the founder and executive count over MCP.
Use leadocean_count_leads with seniority ["founder","c_suite"], industry ["Computer Software"],
hqCountry ["US"], employeeRange ["11-50","51-200"]. Report the total and say which email
statuses it counted. Do not call any tool that spends records.The company count over REST is GET /v1/companies/count. It takes the company-search filters, returns the exact total with breakdowns by industry, headcount band, HQ country, HQ state and NAICS, and costs nothing.
curl -s "https://api.leadocean.io/v1/companies/count?industry=Computer%20Software&hqCountry=US&employeeRange=11-50,51-200" \
-H "x-api-key: $LEADOCEAN_API_KEY" | jqOver MCP the same call is leadocean_count_companies. The funding filters in Step 3 narrow this universe further at search time, so do not quote a count that uses them.
Step 3: Pull the funded companies
Now add the funding filters and pull one page. Each company returned counts one record, so limit is your spend. This page is 25 companies and 25 records.
curl -s -X POST "https://api.leadocean.io/v1/companies/search" \
-H "x-api-key: $LEADOCEAN_API_KEY" -H "content-type: application/json" \
-d '{
"fundingType": ["Seed", "Series A"],
"minLastFundingYear": 2025,
"industry": ["Computer Software"],
"employeeRange": ["11-50", "51-200"],
"hqCountry": ["US"],
"limit": 25
}' > deals-page-1.json
jq '.meta | {count, credits, nextCursor}' deals-page-1.json
jq -r '.data[].company_data.company_domain // empty' deals-page-1.json | sort > deal-domains.txtThe body is flat JSON and every filter is an array. The API reference lists no sort parameter, so a page is not ordered by funding date. To walk the whole list, pass meta.nextCursor back unchanged as cursor.
Order your downstream code around the four top-level groups, not around guessed field names. Check one document before you build on it. Run jq '.data[0] | keys' deals-page-1.json and read the four top-level groups.
Step 4: Find the founders at five of them
A deal list is useful when it names a person. Search people at the first five domains, filtered to founders and executives. domain is a filter on people search, so one call covers all five.
DOMAINS=$(head -5 deal-domains.txt | jq -R . | jq -s .)
curl -s -X POST "https://api.leadocean.io/v1/people/search" \
-H "x-api-key: $LEADOCEAN_API_KEY" -H "content-type: application/json" \
-d "{
\"domain\": $DOMAINS,
\"seniority\": [\"founder\", \"c_suite\"],
\"limit\": 25
}" > founders.json
jq -r '.data[] | .meta.source_ids.person_id' founders.json | head -3 > person-ids.txtEach person returned counts one record, so this call spends at most 25. Search returns thin records with email flags, never addresses. Each row carries a person_id at meta.source_ids.person_id.
The same step works from a chat. Ask the MCP server to run leadocean_search_leads with these filters and a limit of 25, then show name, title and company.
Step 5: Reveal a contact for the ones you want
Pay for an address only when you will use it. Enrich takes one identifier. Send back the person_id and set reveal_email to true.
for ID in $(cat person-ids.txt); 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\", \"reveal_email\": true}" \
| jq '.data.contact_data | {email_status, contact_emails}'
doneThree reveals cost 3 records. A person we hold nothing for is a 404 and costs nothing. Read email_status before you send anything. A bounced address is not refunded.
Step 6: Diff the list every month
The dataset is refreshed monthly, so a monthly pass catches every change the data holds. Polling hourly returns the same list until the next refresh. Walk the full list, save the domains, and compare with last month.
import datetime, os, pathlib, requests
URL = "https://api.leadocean.io/v1/companies/search"
HEAD = {"x-api-key": os.environ["LEADOCEAN_API_KEY"]}
BODY = {
"fundingType": ["Seed", "Series A"],
"minLastFundingYear": 2025,
"industry": ["Computer Software"],
"employeeRange": ["11-50", "51-200"],
"hqCountry": ["US"],
"limit": 100,
}
domains, cursor = set(), None
while True:
body = dict(BODY, **({"cursor": cursor} if cursor else {}))
page = requests.post(URL, json=body, headers=HEAD).json()
domains |= {c["company_data"]["company_domain"] for c in page["data"] if c["company_data"].get("company_domain")}
cursor = page["meta"].get("nextCursor")
if not cursor:
break
today = datetime.date.today().isoformat()
pathlib.Path(f"deals-{today}.txt").write_text("\n".join(sorted(domains)))
snaps = sorted(pathlib.Path(".").glob("deals-*.txt"))
if len(snaps) > 1:
old = set(snaps[-2].read_text().split())
print("new:", sorted(domains - old))
print("gone:", sorted(old - domains))Schedule it on the second day of the month: 0 7 2 * * python3 deal_diff.py. The new list is your inbound pipeline for the month.
Treat new as two cases. A company that raised since last month is a real signal. A company that only now has a funding year on file is a data-coverage change, so check the record's fetched_at date before you act. Feed both lists to your agent with the thesis text, and ask it to rank them against the memo.
This walk costs one record per company, every month. Keep the thesis narrow so the list stays small. For a list of thousands, use POST /v1/exports, which takes up to 50,000 rows a request.
What you get
These counts ran on 2026-10-21 through the free MCP tools. Each call returned "credits": 0. Neither uses a funding filter.
| Query | Tool | Result | Cost |
|---|---|---|---|
| industry Computer Software, HQ US, 11-50 or 51-200 staff | leadocean_count_companies | 2,585 companies | 0 records |
| Same, plus seniority founder or c_suite | leadocean_count_leads | 2,425 mailable people | 0 records |
The people count uses the default email filter: verified, catch_all_valid or catch_all. Without it the same filters match 10,490 people. The 2,425 span 667 companies and split into 1,682 c_suite and 743 owner by seniority band. The company count splits into 2,125 at 11-50 and 460 at 51-200 staff, with 801 in California.
A company search page returns the envelope below. This is the response shape from the API reference, with placeholder values.
{
"success": true,
"data": [
{
"company_data": { "company_name": "Acme", "company_domain": "acme.com" },
"company_detected_technologies": {},
"company_metrics": {},
"meta": {}
}
],
"meta": { "source": "leadocean", "count": 25, "limit": 25, "credits": 25, "nextCursor": "<opaque cursor>" }
}A people page has a different shape. Each person has profile_data, contact_data, resume_data and meta, and the page-level meta carries nextCursor.
Troubleshooting
| Error | Cause | Fix |
|---|---|---|
| 401 | Missing, wrong or revoked key | Re-copy the key from app.leadocean.io and export $LEADOCEAN_API_KEY again. |
| 429 | Above your plan's rate per account: 5 a second on Free, 50 on Solo, 200 on Agency | Wait the Retry-After seconds. The monthly script sends one call per 100 companies, so only a large fan-out hits this. |
| 402 | Free plan: the 1,000 records are spent | Counts stay free. Lower limit, or move to Solo ($97 a month) or Agency ($497 a month) on pricing. Free records do not reset. |
| Zero results | A filter value outside the catalogue, such as industry: "Software" | Call GET /v1/enums/industry and pass the exact string. |
meta.total is null with totalWithheld | A filter in the count could not be applied | Drop that filter from the count and say it narrows the list at search time. |
meta.depthCapped is true | One walk reached 10,000 rows | Narrow the thesis and run several smaller searches, or use POST /v1/exports. |
FAQ
Is LeadOcean real-time company data?
No. The dataset is refreshed monthly and each record carries a fetched_at date. A monthly diff catches every change the data holds. If you need a funding alert inside a day, you need a different tool.
How many records does this cost?
About 53 on the first run: 25 companies, up to 25 founders and 3 reveals. Counts, company counts and enum lookups cost 0. The monthly diff costs one record per company on the list.
Can LeadOcean push an alert when a company raises?
Not today. The API reference, September 2026, lists no webhook or watcher endpoint, so you poll. Crustdata's pricing docs, September 2026, list watchers priced in credits per changed record, for example 5 credits per changed company at a 30-day SLA. Pick a watcher product if alerts matter more to you than a flat price.
Can an AI agent run this?
Yes. The counts, the company search and the founder search all work through the MCP server. See a real-time B2B data provider for AI agents for the setup. The same pattern sizes a market in find and monitor your TAM and finds candidates in AI recruiting with people data. Plans are on pricing.
More recipes live in the blog hub.
Source your next deal from a query you can re-run
Free to start. No credit card. 1,000 records to spend whenever you like.
Get your free API key →