Timing beats targeting once your list is decent. A hiring signal gives you a date, and the date sets when you send.
LeadOcean holds no job postings. It holds the people those postings become: current roles with start dates, career history and company headcount, refreshed monthly. This guide builds the timing layer on top of that. For the tracking side, see how to track hiring signals.
TL;DR
- It takes about 25 minutes to size a segment, pull new leaders and assign each one a send window.
- It spends about 30 records: 25 from one search page and a few from enrichment. Counts are free.
- The windows are a rule of thumb, not a LeadOcean figure: recruiter build-up means act now, a new leader means wait a few weeks, then go.
Prerequisites
- A LeadOcean API key from app.leadocean.io. Free gives 1,000 records, one-off, no card.
- The key in your shell as
$LEADOCEAN_API_KEY. curlandjq.- One account domain to test on. The examples use
acme.com.
export LEADOCEAN_API_KEY="paste-your-key-here"Step 1: Map each signal to a send window
Write the rules down before you query anything. Each signal has a different clock.
| Signal | What it means | When to send |
|---|---|---|
| Several recruiters at a small company | Hiring at volume, headcount pain | Now, while the team is building |
| New VP or Director in your buyer's function | New owner, budget reset | After 3 to 6 weeks, once they have a view |
| New hire below manager level | Capacity added | Low priority, add to a nurture list |
| Headcount up between monthly snapshots | Growth | Within the month, to the function head |
The week counts are judgment. Test them against your own reply rates and change them.
Step 2: Size the segment for free
Count first. A people count with count=true and limit=1 as query parameters spends nothing, and meta.distinctCompanies tells you how many accounts it spans.
curl -s -G https://api.leadocean.io/v1/people/search \
-H "x-api-key: $LEADOCEAN_API_KEY" \
--data-urlencode "title=Talent Acquisition,Recruiter" \
--data-urlencode "jobFunction=Human Resources" \
--data-urlencode "industry=Computer Software" \
--data-urlencode "employeeRange=51-200" \
--data-urlencode "country=US" \
--data-urlencode "count=true" \
--data-urlencode "limit=1" | jq '.meta | {total, distinctCompanies}'Step 3: Pull new leaders at one account
Search by domain and function, then sort by start date on your side. Each row carries the current role and its start_date. The body is flat JSON and the filters are arrays.
curl -s https://api.leadocean.io/v1/people/search \
-H "x-api-key: $LEADOCEAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"domain": ["acme.com"],
"jobFunction": ["Sales & Business Development"],
"jobLevel": ["VP", "Director"],
"limit": 25
}' > page.jsonThis costs 25 records for the page. Print one row before you write a date cutoff. The person schema uses YYYY-MM-DD strings for dates.
jq '.data[0].contact_data.contact_current_experiences[0]' page.jsonStep 4: Confirm the hire is external
A recent start date can be a promotion. A promoted leader already has a view and vendors, so the "new owner" window does not apply. Enrich the person and read the career history. Enrich takes one of linkedin_url, email, phone or person_id.
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_STEP_3"}' \
| jq '.data.resume_data.experiences[0:2][] | {job_title, company_name, start_date, end_date}'If the previous role is at another company, it is an external hire. Past employers carry no company_id yet, so compare names. An enrichment that finds nobody is a 404 and costs nothing.
Step 5: Assign the send date
Do this in code so the calendar is repeatable. The script reads the page from Step 3, keeps rows with a start date, and adds a send date four weeks after it.
jq -r '.data[]
| {name: .profile_data.profile_full_name,
role: .contact_data.contact_current_experiences[0]}
| select(.role.start_date != null)
| [.name, .role.job_title, .role.start_date] | @csv' page.json > leaders.csvThen add the offset in whatever tool you use to send. Four weeks is a starting point.
Step 6: Get emails only when the date arrives
Do not pull contact data for people you will not message this month. Wait for the send date, then reveal the email on the enrich call. reveal_email defaults to false and costs no extra record.
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", "reveal_email": true}' \
| jq '.data.contact_data | {email: .contact_emails[0]}'Read email_status before you send. Bounced emails are not refunded.
What you get
Step 2 is free. We ran two counts on 2026-10-21 with leadocean_count_leads, both for US software companies with 51 to 200 staff.
| Filter | Mailable people | Distinct companies | Without an email filter |
|---|---|---|---|
| Head of Sales, VP Sales, VP of Sales, Chief Revenue Officer (title keywords) | 11 | 9 | 43 |
| Talent Acquisition or Recruiter, in Human Resources | 19 | 15 | 80 |
"Mailable" means verified, catch_all_valid or catch_all, the tool's default. The recruiter count is larger than the sales-leader count, which is the point: at this size a dedicated recruiter is a visible flag. Title matching is by keyword, so read a sample.
A row from Step 3 comes back in the shape below. This is the response shape from the API reference, with placeholder values.
{
"success": true,
"data": [
{
"profile_data": { "profile_full_name": "Jane Doe" },
"contact_data": {
"contact_current_experiences": [
{ "job_title": "VP of Sales", "company_name": "Acme", "start_date": "2026-09-01" }
]
},
"meta": { "source_ids": { "person_id": "PERSON_ID" } }
}
],
"meta": { "count": 1 }
}Troubleshooting
| Error | Cause | Fix |
|---|---|---|
| 401 | The x-api-key header is missing, or the key is wrong or revoked. | Copy a fresh key from the app and reload $LEADOCEAN_API_KEY. |
| 402 | Your records are spent. The Free 1,000 are a one-off allowance. | Check GET /v1/account, or move to a paid plan (Solo, from $97 a month). |
| 429 | More requests a second than your plan allows: 5 on Free, 200 on Agency. | Wait the Retry-After seconds and retry, or slow the loop. |
| Empty results | A filter value is not an exact enum string, such as Sales for Sales & Business Development. | Call leadocean_list_enum_values for job_function and pass the exact string. |
meta.depthCapped is true | One search pages to 10,000 rows. | Narrow the filters instead of walking the index. |
| Export refused | One export is capped at 50,000 rows a request and 500,000 rows a day. | Split the job across requests and days. |
FAQ
How soon after a hire should I reach out?
There is no universal number. Start at 3 to 6 weeks for a new external leader, and move the offset by your reply data.
Does LeadOcean see job postings?
No. We hold people, start dates, career history and headcount. A posting feed tells you a role opened. We tell you who filled it. See signal APIs for internal sales tools for combining sources.
How fresh is the date?
The dataset is refreshed monthly, and each record carries its own fetched_at. Polling more than monthly just reruns the same answer.
Can I run the counts from Claude?
Yes. Connect Claude to https://api.leadocean.io/mcp and use leadocean_count_leads for Step 2. It is free.
| Related reading | Link |
|---|---|
| Buying signals for B2B outreach | read the guide |
Find your next hiring-signal accounts free
Free to start. No credit card. 1,000 records to spend whenever you like.
Get your free API key →