TL;DR
- A people search API turns your sourcing search into four calls: count the pool, search it, enrich the shortlist, push rows to your ATS or CRM.
- It takes about 45 minutes and spends about 11 LeadOcean records: 10 for a sample pull and 1 for one enrichment. Counting is free.
- Use search to build the list and enrich only the people you will contact. That keeps record spend on candidates, not on browsing.
Prerequisites
- A LeadOcean account from app.leadocean.io. The free plan has no card and 1,000 records, one-off.
- An API key exported as
$LEADOCEAN_API_KEY, pluscurlandjq. - A place to receive rows: your ATS import, a spreadsheet or a small script of your own.
export LEADOCEAN_API_KEY="paste-your-key-here"Step 1: Define the role as filters and count the pool
Write the role as filters, then count before you pull anything. The example is senior software engineers in the US at software companies with 51 to 200 staff.
Over REST, send count=true as a query parameter with limit at 1. In an MCP client, leadocean_count_leads does the same job. Both cost 0 records.
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": ["Senior Software Engineer"],
"country": ["US"],
"industry": ["Software Development"],
"employeeRange": ["51-200"],
"limit": 1
}' | jq '.meta'If the pool is too large for one search round, add a filter such as keywords or city. If it is empty, drop one. Filters are flat arrays of strings in a flat JSON body.
Step 2: Pull a small sample and read it
Ten rows is enough to see whether the filters match the people you mean. Review them with the hiring manager before you spend more.
curl -s https://api.leadocean.io/v1/people/search \
-H "x-api-key: $LEADOCEAN_API_KEY" -H "content-type: application/json" \
-d '{
"title": ["Senior Software Engineer"],
"country": ["US"],
"industry": ["Software Development"],
"employeeRange": ["51-200"],
"excludeTitle": ["intern", "manager"],
"limit": 10
}' > sample.json
jq -r '.data[] | [.meta.source_ids.person_id, .contact_data.email_status] | @csv' sample.json
jq '.data[0].profile_data' sample.jsonThis spends 10 records. The first jq line prints each person id with its email status. The second prints one profile so you can check the title and company. Fix the filters now, not after 5,000 rows.
Step 3: Page through the full list with a cursor
Search returns 1 to 100 rows a page (25 by default) and pages by cursor. Pass the cursor from the last response into the next request.
curl -s https://api.leadocean.io/v1/people/search \
-H "x-api-key: $LEADOCEAN_API_KEY" -H "content-type: application/json" \
-d '{
"title": ["Senior Software Engineer"],
"country": ["US"],
"industry": ["Software Development"],
"employeeRange": ["51-200"],
"limit": 100,
"cursor": "NEXT_CURSOR"
}' | jq '.meta'One search pages to 10,000 rows. A capped page carries meta.depthCapped as true and no nextCursor. When you hit it, narrow the filters (add a city or a keyword) instead of walking further. Each person returned counts one record, so stop when the shortlist is long enough.
Keep the loop simple. Read meta.nextCursor, send it back as cursor, and stop when it is missing. Write each page to disk before you request the next, so a dropped connection never costs you a page you already paid for.
Count first, then pull in slices. A pool of 3,140 people does not need pulling in one go. Pull a slice per week and spend records only on the people you will contact.
Step 4: Enrich the shortlist with career history
Enrich only the people you plan to contact. Enrichment takes exactly one of linkedin_url, email, phone or person_id. Use the person_id from the search. Set reveal_email to true only for those rows.
curl -s https://api.leadocean.io/v1/people/enrich \
-H "x-api-key: $LEADOCEAN_API_KEY" -H "content-type: application/json" \
-d '{"person_id": "<person_id from sample.json>", "reveal_email": true}' \
| jq '{current: .data.contact_data.contact_current_experiences, resume: .data.resume_data}'Since October 2026 the response carries resume_data: past roles, education, certifications and awards, each when held. For a recruiter that is the screen: did they ship at a company your client respects, and what did they study? Past employers have no company_id yet, so match them by name. A person we hold nothing for is a 404 and costs nothing.
For phone numbers, call GET /v2/people/phone. Contact endpoints under /v2/ cost one record per call, including a call that finds nothing.
Step 5: Send rows to your ATS or CRM
LeadOcean has no recruiter workspace, sequencer, dialer or CRM, so the rows leave as JSON or CSV. The simplest bridge is a script that maps fields and posts to your ATS import endpoint. This one writes a CSV that most ATS tools can import.
jq -r '
["person_id","email_status"],
(.data[] | [.meta.source_ids.person_id, .contact_data.email_status])
| @csv' sample.json > candidates.csvFor large pulls, bulk CSV export is on Solo ($97 a month) and above, not on Free. An export takes up to 50,000 rows a request and 500,000 a day. The app at app.leadocean.io has the same export on its Exports page, with a filter builder, a column picker, the record price before you start and a Download button.
curl -s https://api.leadocean.io/v1/exports \
-H "x-api-key: $LEADOCEAN_API_KEY" -H "content-type: application/json" \
-d '{
"name": "US senior engineers, software, 51-200",
"filters": {
"title": ["Senior Software Engineer"],
"country": ["US"],
"industry": ["Software Development"],
"employeeRange": ["51-200"],
"emailStatus": ["verified", "catch_all_valid", "catch_all"]
},
"limit": 500
}' | jq '.data | {id, status, requested}'Before you email anyone, read email_status. Bounced emails are not refunded.
What you get
The Step 1 count is a real result. We ran the same filters through the free leadocean_count_leads tool on 2026-10-21 (trimmed).
{
"total": 3140,
"totalIsExact": true,
"totalAll": 7366,
"distinctCompanies": 1317,
"credits": 0,
"defaults": "Counted only addresses worth sending to (emailStatus: verified or catch_all_valid or catch_all)",
"facets": {
"country": [{ "value": "US", "count": 3140 }],
"industry": [{ "value": "Software Development", "count": 3140 }],
"seniority": [
{ "value": "senior", "count": 3059 },
{ "value": "manager", "count": 45 },
{ "value": "entry", "count": 8 }
],
"company_size": [{ "value": "51-200", "count": 3140 }]
}
}That is 3,140 mailable people across 1,317 companies. Without the email condition the same filters match 7,366 people. Your number will move as the data changes.
A search page looks like this. This is the response shape from the API reference, with placeholder values.
{
"data": [
{
"meta": { "source_ids": { "person_id": "<person id>" } },
"profile_data": { "profile_full_name": "Jane Doe" },
"contact_data": {
"has_email": true,
"email_status": "verified",
"contact_current_experiences": [
{ "job_title": "Senior Software Engineer", "company_name": "Acme", "company_domain": "acme.com" }
]
}
}
],
"meta": { "nextCursor": "<cursor>" }
}Troubleshooting
| Error | Cause | Fix |
|---|---|---|
| 401 | Missing, wrong or revoked API key | Re-copy the key from app.leadocean.io and export $LEADOCEAN_API_KEY again. |
| 429 | Above your plan's rate (5 requests a second on Free, 200 on Agency) | Wait the Retry-After seconds, then retry. Use pages of 100 instead of more calls. |
| 402 | Free plan: the 1,000 records are spent | Check GET /v1/account, lower limit, or move to a paid plan. |
| Count of 0 or a 400 on a filter | A value outside the catalogue, such as industry: "Software" | Call leadocean_list_enum_values or GET /v1/enums and pass the exact string. |
Pagination stops with no nextCursor | The search hit the 10,000-row depth cap (meta.depthCapped is true) | Narrow the filters and run a second search. |
| Export refused | Bulk CSV export is not on Free, or the request is above 50,000 rows or the 500,000 daily ceiling | Page the search on Free, or split the export across requests and days. |
FAQ
How many records does a recruiting search cost?
One record per person returned. A page of 25 is 25 records. Counts, empty pages and an enrichment that finds nothing cost 0.
Can I get a candidate's past employers and education?
Yes, from person enrich. Since October 2026 resume_data returns past roles, education, certifications and awards when held. Search results do not carry that depth, so enrich the shortlist.
Does LeadOcean replace my ATS?
No. It is a people data API and MCP server, plus an app with an Exports page. It has no recruiter workspace, sequencer, dialer, CRM or Chrome extension, so rows go into the tools you already use.
Which people search API should I pick?
Compare coverage, filters and price per record first. See the best people search APIs and the detailed analysis of people data search APIs. More guides are on the LeadOcean blog.
How fresh is the data?
The dataset is refreshed monthly, and each record carries its own fetched_at date. Check it before outreach on a role-sensitive search.
Source your next shortlist from one API
Free to start. No credit card. 1,000 records to spend whenever you like.
Get your free API key →